Core/DolphinQt: Implement Emulated Balance Board.
Co-authored-by: Jordan Woyak <jordan.woyak@gmail.com>
This commit is contained in:
parent
c770e7c276
commit
cffda24a55
|
@ -304,6 +304,8 @@ add_library(core
|
|||
HW/WiimoteEmu/EmuSubroutines.cpp
|
||||
HW/WiimoteEmu/Encryption.cpp
|
||||
HW/WiimoteEmu/Encryption.h
|
||||
HW/WiimoteEmu/Extension/BalanceBoard.cpp
|
||||
HW/WiimoteEmu/Extension/BalanceBoard.h
|
||||
HW/WiimoteEmu/Extension/Classic.cpp
|
||||
HW/WiimoteEmu/Extension/Classic.h
|
||||
HW/WiimoteEmu/Extension/DesiredExtensionState.h
|
||||
|
|
|
@ -713,7 +713,7 @@ void SetState(Core::System& system, State state, bool report_state_change,
|
|||
// NOTE: GetState() will return State::Paused immediately, even before anything has
|
||||
// stopped (including the CPU).
|
||||
system.GetCPU().SetStepping(true); // Break
|
||||
Wiimote::Pause();
|
||||
WiimoteReal::Pause();
|
||||
ResetRumble();
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
AchievementManager::GetInstance().DoIdle();
|
||||
|
@ -722,7 +722,7 @@ void SetState(Core::System& system, State state, bool report_state_change,
|
|||
case State::Running:
|
||||
{
|
||||
system.GetCPU().SetStepping(false);
|
||||
Wiimote::Resume();
|
||||
WiimoteReal::Resume();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
|
|
@ -18,16 +18,12 @@
|
|||
#include "Core/IOS/USB/Bluetooth/BTEmu.h"
|
||||
#include "Core/IOS/USB/Bluetooth/WiimoteDevice.h"
|
||||
#include "Core/Movie.h"
|
||||
#include "Core/NetPlayClient.h"
|
||||
#include "Core/System.h"
|
||||
#include "Core/WiiUtils.h"
|
||||
|
||||
#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h"
|
||||
#include "InputCommon/InputConfig.h"
|
||||
|
||||
// Limit the amount of wiimote connect requests, when a button is pressed in disconnected state
|
||||
static std::array<u8, MAX_BBMOTES> s_last_connect_request_counter;
|
||||
|
||||
namespace
|
||||
{
|
||||
static std::array<std::atomic<WiimoteSource>, MAX_BBMOTES> s_wiimote_sources;
|
||||
|
@ -60,6 +56,27 @@ void RefreshConfig()
|
|||
OnSourceChanged(i, Config::Get(Config::GetInfoForWiimoteSource(i)));
|
||||
}
|
||||
|
||||
void DoWiimoteSlotState(PointerWrap& p, int slot, ControllerEmu::EmulatedController* controller)
|
||||
{
|
||||
const WiimoteSource source = GetSource(slot);
|
||||
auto state_wiimote_source = u8(source);
|
||||
p.Do(state_wiimote_source);
|
||||
|
||||
if (WiimoteSource(state_wiimote_source) == WiimoteSource::Emulated)
|
||||
{
|
||||
// Sync complete state of emulated wiimotes.
|
||||
static_cast<WiimoteEmu::WiimoteBase*>(controller)->DoState(p);
|
||||
}
|
||||
|
||||
if (p.IsReadMode())
|
||||
{
|
||||
// If using a real wiimote or the save-state source does not match the current source,
|
||||
// then force a reconnection on load.
|
||||
if (source == WiimoteSource::Real || source != WiimoteSource(state_wiimote_source))
|
||||
WiimoteCommon::UpdateSource(slot);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace WiimoteCommon
|
||||
|
@ -80,7 +97,9 @@ HIDWiimote* GetHIDWiimoteSource(unsigned int index)
|
|||
switch (GetSource(index))
|
||||
{
|
||||
case WiimoteSource::Emulated:
|
||||
hid_source = static_cast<WiimoteEmu::Wiimote*>(::Wiimote::GetConfig()->GetController(index));
|
||||
hid_source = static_cast<WiimoteEmu::WiimoteBase*>(
|
||||
(index == WIIMOTE_BALANCE_BOARD) ? ::BalanceBoard::GetConfig()->GetController(0) :
|
||||
::Wiimote::GetConfig()->GetController(index));
|
||||
break;
|
||||
|
||||
case WiimoteSource::Real:
|
||||
|
@ -96,10 +115,15 @@ HIDWiimote* GetHIDWiimoteSource(unsigned int index)
|
|||
|
||||
} // namespace WiimoteCommon
|
||||
|
||||
namespace
|
||||
{
|
||||
static InputConfig s_config(WIIMOTE_INI_NAME, _trans("Balance Board"), "BalanceBoard",
|
||||
"BalanceBoard");
|
||||
static InputConfig s_bb_config(WIIMOTE_INI_NAME, _trans("Wii Remote"), "Wiimote", "Wiimote");
|
||||
} // namespace
|
||||
|
||||
namespace Wiimote
|
||||
{
|
||||
static InputConfig s_config(WIIMOTE_INI_NAME, _trans("Wii Remote"), "Wiimote", "Wiimote");
|
||||
|
||||
InputConfig* GetConfig()
|
||||
{
|
||||
return &s_config;
|
||||
|
@ -164,6 +188,7 @@ void Shutdown()
|
|||
{
|
||||
s_config.UnregisterHotplugCallback();
|
||||
|
||||
s_bb_config.ClearControllers();
|
||||
s_config.ClearControllers();
|
||||
|
||||
WiimoteReal::Stop();
|
||||
|
@ -179,11 +204,14 @@ void Initialize(InitializeMode init_mode)
|
|||
{
|
||||
if (s_config.ControllersNeedToBeCreated())
|
||||
{
|
||||
for (unsigned int i = WIIMOTE_CHAN_0; i < MAX_BBMOTES; ++i)
|
||||
for (unsigned int i = WIIMOTE_CHAN_0; i < MAX_WIIMOTES; ++i)
|
||||
s_config.CreateController<WiimoteEmu::Wiimote>(i);
|
||||
|
||||
s_bb_config.CreateController<WiimoteEmu::BalanceBoard>(WIIMOTE_BALANCE_BOARD);
|
||||
}
|
||||
|
||||
s_config.RegisterHotplugCallback();
|
||||
s_bb_config.RegisterHotplugCallback();
|
||||
|
||||
LoadConfig();
|
||||
|
||||
|
@ -201,47 +229,41 @@ void Initialize(InitializeMode init_mode)
|
|||
|
||||
void ResetAllWiimotes()
|
||||
{
|
||||
for (int i = WIIMOTE_CHAN_0; i < MAX_BBMOTES; ++i)
|
||||
for (int i = WIIMOTE_CHAN_0; i < MAX_WIIMOTES; ++i)
|
||||
static_cast<WiimoteEmu::Wiimote*>(s_config.GetController(i))->Reset();
|
||||
|
||||
static_cast<WiimoteEmu::BalanceBoard*>(s_bb_config.GetController(0))->Reset();
|
||||
}
|
||||
|
||||
void LoadConfig()
|
||||
{
|
||||
s_config.LoadConfig();
|
||||
s_last_connect_request_counter.fill(0);
|
||||
}
|
||||
|
||||
void Resume()
|
||||
{
|
||||
WiimoteReal::Resume();
|
||||
}
|
||||
|
||||
void Pause()
|
||||
{
|
||||
WiimoteReal::Pause();
|
||||
}
|
||||
|
||||
void DoState(PointerWrap& p)
|
||||
{
|
||||
for (int i = 0; i < MAX_BBMOTES; ++i)
|
||||
{
|
||||
const WiimoteSource source = GetSource(i);
|
||||
auto state_wiimote_source = u8(source);
|
||||
p.Do(state_wiimote_source);
|
||||
for (int slot = 0; slot < MAX_WIIMOTES; ++slot)
|
||||
DoWiimoteSlotState(p, slot, s_config.GetController(slot));
|
||||
|
||||
if (WiimoteSource(state_wiimote_source) == WiimoteSource::Emulated)
|
||||
{
|
||||
// Sync complete state of emulated wiimotes.
|
||||
static_cast<WiimoteEmu::Wiimote*>(s_config.GetController(i))->DoState(p);
|
||||
}
|
||||
|
||||
if (p.IsReadMode())
|
||||
{
|
||||
// If using a real wiimote or the save-state source does not match the current source,
|
||||
// then force a reconnection on load.
|
||||
if (source == WiimoteSource::Real || source != WiimoteSource(state_wiimote_source))
|
||||
WiimoteCommon::UpdateSource(i);
|
||||
}
|
||||
}
|
||||
DoWiimoteSlotState(p, WIIMOTE_BALANCE_BOARD, s_bb_config.GetController(0));
|
||||
}
|
||||
} // namespace Wiimote
|
||||
|
||||
namespace BalanceBoard
|
||||
{
|
||||
InputConfig* GetConfig()
|
||||
{
|
||||
return &s_bb_config;
|
||||
}
|
||||
|
||||
void LoadConfig()
|
||||
{
|
||||
s_bb_config.LoadConfig();
|
||||
}
|
||||
|
||||
ControllerEmu::ControlGroup* GetBalanceBoardGroup(int number, WiimoteEmu::BalanceBoardGroup group)
|
||||
{
|
||||
return static_cast<WiimoteEmu::BalanceBoard*>(s_bb_config.GetController(number))
|
||||
->GetBalanceBoardGroup(group);
|
||||
}
|
||||
} // namespace BalanceBoard
|
||||
|
|
|
@ -3,10 +3,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
|
||||
#include "Common/Common.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
class InputConfig;
|
||||
|
@ -29,9 +25,10 @@ enum class UDrawTabletGroup;
|
|||
enum class DrawsomeTabletGroup;
|
||||
enum class TaTaConGroup;
|
||||
enum class ShinkansenGroup;
|
||||
enum class BalanceBoardGroup;
|
||||
} // namespace WiimoteEmu
|
||||
|
||||
enum
|
||||
enum : u8
|
||||
{
|
||||
WIIMOTE_CHAN_0 = 0,
|
||||
WIIMOTE_CHAN_1,
|
||||
|
@ -74,14 +71,13 @@ enum class InitializeMode
|
|||
// The Real Wii Remote sends report every ~5ms (200 Hz).
|
||||
constexpr int UPDATE_FREQ = 200;
|
||||
|
||||
void Shutdown();
|
||||
// Note that these also handle the 5th-slot BalanceBoard, though it has a separate InputConfig.
|
||||
void Initialize(InitializeMode init_mode);
|
||||
void Shutdown();
|
||||
void ResetAllWiimotes();
|
||||
void LoadConfig();
|
||||
void Resume();
|
||||
void Pause();
|
||||
|
||||
void DoState(PointerWrap& p);
|
||||
|
||||
void LoadConfig();
|
||||
InputConfig* GetConfig();
|
||||
ControllerEmu::ControlGroup* GetWiimoteGroup(int number, WiimoteEmu::WiimoteGroup group);
|
||||
ControllerEmu::ControlGroup* GetNunchukGroup(int number, WiimoteEmu::NunchukGroup group);
|
||||
|
@ -96,6 +92,13 @@ ControllerEmu::ControlGroup* GetTaTaConGroup(int number, WiimoteEmu::TaTaConGrou
|
|||
ControllerEmu::ControlGroup* GetShinkansenGroup(int number, WiimoteEmu::ShinkansenGroup group);
|
||||
} // namespace Wiimote
|
||||
|
||||
namespace BalanceBoard
|
||||
{
|
||||
InputConfig* GetConfig();
|
||||
void LoadConfig();
|
||||
ControllerEmu::ControlGroup* GetBalanceBoardGroup(int number, WiimoteEmu::BalanceBoardGroup group);
|
||||
} // namespace BalanceBoard
|
||||
|
||||
namespace WiimoteReal
|
||||
{
|
||||
void Initialize(::Wiimote::InitializeMode init_mode);
|
||||
|
@ -104,5 +107,4 @@ void Shutdown();
|
|||
void Resume();
|
||||
void Pause();
|
||||
void Refresh();
|
||||
|
||||
} // namespace WiimoteReal
|
||||
|
|
|
@ -214,7 +214,8 @@ struct InputReportStatus
|
|||
private:
|
||||
static constexpr auto BATTERY_MAX = std::numeric_limits<decltype(battery)>::max();
|
||||
|
||||
// Linear fit of battery level mid-point for charge bars in home menu.
|
||||
// Linear fit of battery level mid-point for Wii Remote charge bars in home menu.
|
||||
// Note that Balance Board battery values differ (probably from 2xAA vs 4xAA).
|
||||
static constexpr float BATTERY_LEVEL_M = 2.46f;
|
||||
static constexpr float BATTERY_LEVEL_B = -0.013f;
|
||||
};
|
||||
|
|
|
@ -11,15 +11,15 @@
|
|||
#include "Common/MathUtil.h"
|
||||
#include "Common/Matrix.h"
|
||||
|
||||
#include "Core/HW/WiimoteCommon/WiimoteReport.h"
|
||||
|
||||
namespace WiimoteEmu
|
||||
{
|
||||
CameraLogic::CameraLogic(const WiimoteCommon::InputReportStatus* status) : m_wiimote_status{*status}
|
||||
{
|
||||
}
|
||||
|
||||
void CameraLogic::Reset()
|
||||
{
|
||||
m_reg_data = {};
|
||||
|
||||
m_is_enabled = false;
|
||||
}
|
||||
|
||||
void CameraLogic::DoState(PointerWrap& p)
|
||||
|
@ -34,7 +34,7 @@ int CameraLogic::BusRead(u8 slave_addr, u8 addr, int count, u8* data_out)
|
|||
if (I2C_ADDR != slave_addr)
|
||||
return 0;
|
||||
|
||||
if (!m_is_enabled)
|
||||
if (!IsEnabled())
|
||||
return 0;
|
||||
|
||||
return RawRead(&m_reg_data, addr, count, data_out);
|
||||
|
@ -45,7 +45,7 @@ int CameraLogic::BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in)
|
|||
if (I2C_ADDR != slave_addr)
|
||||
return 0;
|
||||
|
||||
if (!m_is_enabled)
|
||||
if (!IsEnabled())
|
||||
return 0;
|
||||
|
||||
return RawWrite(&m_reg_data, addr, count, data_in);
|
||||
|
@ -179,9 +179,9 @@ void CameraLogic::Update(const std::array<CameraPoint, NUM_POINTS>& camera_point
|
|||
}
|
||||
}
|
||||
|
||||
void CameraLogic::SetEnabled(bool is_enabled)
|
||||
bool CameraLogic::IsEnabled() const
|
||||
{
|
||||
m_is_enabled = is_enabled;
|
||||
return m_wiimote_status.ir;
|
||||
}
|
||||
|
||||
} // namespace WiimoteEmu
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
|
||||
#include "Common/ChunkFile.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Core/HW/WiimoteEmu/Dynamics.h"
|
||||
#include "Common/Matrix.h"
|
||||
|
||||
#include "Core/HW/WiimoteCommon/WiimoteReport.h"
|
||||
#include "Core/HW/WiimoteEmu/I2CBus.h"
|
||||
#include "InputCommon/ControllerEmu/ControlGroup/Cursor.h"
|
||||
|
||||
namespace Common
|
||||
{
|
||||
|
@ -104,6 +105,8 @@ static_assert(sizeof(IRFull) == 9, "Wrong size");
|
|||
class CameraLogic : public I2CSlave
|
||||
{
|
||||
public:
|
||||
CameraLogic(const WiimoteCommon::InputReportStatus* status);
|
||||
|
||||
// OEM sensor bar distance between LED clusters in meters.
|
||||
static constexpr float SENSOR_BAR_LED_SEPARATION = 0.2f;
|
||||
|
||||
|
@ -136,7 +139,6 @@ public:
|
|||
static std::array<CameraPoint, NUM_POINTS> GetCameraPoints(const Common::Matrix44& transform,
|
||||
Common::Vec2 field_of_view);
|
||||
void Update(const std::array<CameraPoint, NUM_POINTS>& camera_points);
|
||||
void SetEnabled(bool is_enabled);
|
||||
|
||||
static constexpr u8 I2C_ADDR = 0x58;
|
||||
static constexpr int CAMERA_DATA_BYTES = 36;
|
||||
|
@ -176,13 +178,15 @@ public:
|
|||
static const u8 REPORT_DATA_OFFSET = offsetof(Register, camera_data);
|
||||
|
||||
private:
|
||||
// When disabled the camera does not respond on the bus.
|
||||
// Change is triggered by wiimote report 0x13.
|
||||
bool IsEnabled() const;
|
||||
|
||||
int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) override;
|
||||
int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) override;
|
||||
|
||||
Register m_reg_data{};
|
||||
|
||||
// When disabled the camera does not respond on the bus.
|
||||
// Change is triggered by wiimote report 0x13.
|
||||
bool m_is_enabled = false;
|
||||
const WiimoteCommon::InputReportStatus& m_wiimote_status;
|
||||
};
|
||||
} // namespace WiimoteEmu
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "Core/HW/WiimoteEmu/DesiredWiimoteState.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
|
@ -117,8 +118,9 @@ SerializedWiimoteState SerializeDesiredState(const DesiredWiimoteState& state)
|
|||
using T = std::decay_t<decltype(arg)>;
|
||||
if constexpr (!std::is_same_v<std::monostate, T>)
|
||||
{
|
||||
static_assert(sizeof(arg) <= 6);
|
||||
static_assert(sizeof(arg) <= s.MAX_EXT_DATA_SIZE);
|
||||
static_assert(std::is_trivially_copyable_v<T>);
|
||||
assert(s.length + sizeof(arg) <= s.data.size());
|
||||
std::memcpy(&s.data[s.length], &arg, sizeof(arg));
|
||||
s.length += sizeof(arg);
|
||||
}
|
||||
|
@ -213,6 +215,9 @@ bool DeserializeDesiredState(DesiredWiimoteState* state, const SerializedWiimote
|
|||
case ExtensionNumber::SHINKANSEN:
|
||||
s += sizeof(Shinkansen::DesiredState);
|
||||
break;
|
||||
case ExtensionNumber::BALANCE_BOARD:
|
||||
s += sizeof(BalanceBoardExt::DataFormat);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -225,6 +230,10 @@ bool DeserializeDesiredState(DesiredWiimoteState* state, const SerializedWiimote
|
|||
return false;
|
||||
}
|
||||
|
||||
// Contriving data with MPlus + BBoard could create an oversided length.
|
||||
if (expected_size > serialized.data.size())
|
||||
return false;
|
||||
|
||||
size_t pos = 1;
|
||||
|
||||
if (has_buttons)
|
||||
|
@ -319,6 +328,8 @@ bool DeserializeDesiredState(DesiredWiimoteState* state, const SerializedWiimote
|
|||
return DeserializeExtensionState<TaTaCon::DataFormat>(state, serialized, pos);
|
||||
case ExtensionNumber::SHINKANSEN:
|
||||
return DeserializeExtensionState<Shinkansen::DesiredState>(state, serialized, pos);
|
||||
case ExtensionNumber::BALANCE_BOARD:
|
||||
return DeserializeExtensionState<BalanceBoardExt::DataFormat>(state, serialized, pos);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -35,7 +35,10 @@ struct DesiredWiimoteState
|
|||
struct SerializedWiimoteState
|
||||
{
|
||||
u8 length;
|
||||
std::array<u8, 30> data; // 18 bytes Wiimote, 6 bytes MotionPlus, 6 bytes Extension
|
||||
|
||||
// 18 bytes Wiimote + ((6 bytes MotionPlus + 6 bytes Extension) | 8 bytes BalanceBoardExt)
|
||||
static constexpr size_t MAX_EXT_DATA_SIZE = std::max(6, 8);
|
||||
std::array<u8, 18 + std::max(6 + 6, 8)> data;
|
||||
};
|
||||
|
||||
SerializedWiimoteState SerializeDesiredState(const DesiredWiimoteState& state);
|
||||
|
|
|
@ -4,18 +4,15 @@
|
|||
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
|
||||
#include "Common/BitUtils.h"
|
||||
#include "Common/ChunkFile.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/EnumUtils.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/MsgHandler.h"
|
||||
#include "Common/Swap.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/DolphinAnalytics.h"
|
||||
#include "Core/HW/Wiimote.h"
|
||||
#include "Core/HW/WiimoteCommon/WiimoteHid.h"
|
||||
|
@ -25,7 +22,10 @@ namespace WiimoteEmu
|
|||
{
|
||||
using namespace WiimoteCommon;
|
||||
|
||||
void Wiimote::HandleReportMode(const OutputReportMode& dr)
|
||||
// Used only for error generation:
|
||||
static constexpr u8 EEPROM_I2C_ADDR = 0x50;
|
||||
|
||||
void WiimoteBase::HandleReportMode(const OutputReportMode& dr)
|
||||
{
|
||||
if (!DataReportBuilder::IsValidMode(dr.mode))
|
||||
{
|
||||
|
@ -47,7 +47,8 @@ void Wiimote::HandleReportMode(const OutputReportMode& dr)
|
|||
|
||||
// Tests that we have enough bytes for the report before we run the handler.
|
||||
template <typename T, typename H>
|
||||
void Wiimote::InvokeHandler(H&& handler, const WiimoteCommon::OutputReportGeneric& rpt, u32 size)
|
||||
void WiimoteBase::InvokeHandler(H&& handler, const WiimoteCommon::OutputReportGeneric& rpt,
|
||||
u32 size)
|
||||
{
|
||||
if (size < sizeof(T))
|
||||
{
|
||||
|
@ -59,17 +60,17 @@ void Wiimote::InvokeHandler(H&& handler, const WiimoteCommon::OutputReportGeneri
|
|||
(this->*handler)(Common::BitCastPtr<T>(&rpt.data[0]));
|
||||
}
|
||||
|
||||
void Wiimote::EventLinked()
|
||||
void WiimoteBase::EventLinked()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
void Wiimote::EventUnlinked()
|
||||
void WiimoteBase::EventUnlinked()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
void Wiimote::InterruptDataOutput(const u8* data, u32 size)
|
||||
void WiimoteBase::InterruptDataOutput(const u8* data, u32 size)
|
||||
{
|
||||
if (size == 0)
|
||||
{
|
||||
|
@ -88,7 +89,7 @@ void Wiimote::InterruptDataOutput(const u8* data, u32 size)
|
|||
|
||||
// WiiBrew:
|
||||
// In every single Output Report, bit 0 (0x01) of the first byte controls the Rumble feature.
|
||||
InvokeHandler<OutputReportRumble>(&Wiimote::HandleReportRumble, rpt, rpt_size);
|
||||
InvokeHandler<OutputReportRumble>(&WiimoteBase::HandleReportRumble, rpt, rpt_size);
|
||||
|
||||
switch (rpt.rpt_id)
|
||||
{
|
||||
|
@ -96,34 +97,34 @@ void Wiimote::InterruptDataOutput(const u8* data, u32 size)
|
|||
// This is handled above.
|
||||
break;
|
||||
case OutputReportID::LED:
|
||||
InvokeHandler<OutputReportLeds>(&Wiimote::HandleReportLeds, rpt, rpt_size);
|
||||
InvokeHandler<OutputReportLeds>(&WiimoteBase::HandleReportLeds, rpt, rpt_size);
|
||||
break;
|
||||
case OutputReportID::ReportMode:
|
||||
InvokeHandler<OutputReportMode>(&Wiimote::HandleReportMode, rpt, rpt_size);
|
||||
InvokeHandler<OutputReportMode>(&WiimoteBase::HandleReportMode, rpt, rpt_size);
|
||||
break;
|
||||
case OutputReportID::IRLogicEnable:
|
||||
InvokeHandler<OutputReportEnableFeature>(&Wiimote::HandleIRLogicEnable, rpt, rpt_size);
|
||||
InvokeHandler<OutputReportEnableFeature>(&WiimoteBase::HandleIRLogicEnable, rpt, rpt_size);
|
||||
break;
|
||||
case OutputReportID::SpeakerEnable:
|
||||
InvokeHandler<OutputReportEnableFeature>(&Wiimote::HandleSpeakerEnable, rpt, rpt_size);
|
||||
InvokeHandler<OutputReportEnableFeature>(&WiimoteBase::HandleSpeakerEnable, rpt, rpt_size);
|
||||
break;
|
||||
case OutputReportID::RequestStatus:
|
||||
InvokeHandler<OutputReportRequestStatus>(&Wiimote::HandleRequestStatus, rpt, rpt_size);
|
||||
InvokeHandler<OutputReportRequestStatus>(&WiimoteBase::HandleRequestStatus, rpt, rpt_size);
|
||||
break;
|
||||
case OutputReportID::WriteData:
|
||||
InvokeHandler<OutputReportWriteData>(&Wiimote::HandleWriteData, rpt, rpt_size);
|
||||
InvokeHandler<OutputReportWriteData>(&WiimoteBase::HandleWriteData, rpt, rpt_size);
|
||||
break;
|
||||
case OutputReportID::ReadData:
|
||||
InvokeHandler<OutputReportReadData>(&Wiimote::HandleReadData, rpt, rpt_size);
|
||||
InvokeHandler<OutputReportReadData>(&WiimoteBase::HandleReadData, rpt, rpt_size);
|
||||
break;
|
||||
case OutputReportID::SpeakerData:
|
||||
InvokeHandler<OutputReportSpeakerData>(&Wiimote::HandleSpeakerData, rpt, rpt_size);
|
||||
InvokeHandler<OutputReportSpeakerData>(&WiimoteBase::HandleSpeakerData, rpt, rpt_size);
|
||||
break;
|
||||
case OutputReportID::SpeakerMute:
|
||||
InvokeHandler<OutputReportEnableFeature>(&Wiimote::HandleSpeakerMute, rpt, rpt_size);
|
||||
InvokeHandler<OutputReportEnableFeature>(&WiimoteBase::HandleSpeakerMute, rpt, rpt_size);
|
||||
break;
|
||||
case OutputReportID::IRLogicEnable2:
|
||||
InvokeHandler<OutputReportEnableFeature>(&Wiimote::HandleIRLogicEnable2, rpt, rpt_size);
|
||||
InvokeHandler<OutputReportEnableFeature>(&WiimoteBase::HandleIRLogicEnable2, rpt, rpt_size);
|
||||
break;
|
||||
default:
|
||||
PanicAlertFmt("HidOutputReport: Unknown report ID {:#04x}", static_cast<u8>(rpt.rpt_id));
|
||||
|
@ -131,7 +132,7 @@ void Wiimote::InterruptDataOutput(const u8* data, u32 size)
|
|||
}
|
||||
}
|
||||
|
||||
void Wiimote::SendAck(OutputReportID rpt_id, ErrorCode error_code)
|
||||
void WiimoteBase::SendAck(OutputReportID rpt_id, ErrorCode error_code)
|
||||
{
|
||||
TypedInputData<InputReportAck> rpt(InputReportID::Ack);
|
||||
auto& ack = rpt.payload;
|
||||
|
@ -146,13 +147,6 @@ void Wiimote::SendAck(OutputReportID rpt_id, ErrorCode error_code)
|
|||
void Wiimote::HandleExtensionSwap(ExtensionNumber desired_extension_number,
|
||||
bool desired_motion_plus)
|
||||
{
|
||||
if (WIIMOTE_BALANCE_BOARD == m_index)
|
||||
{
|
||||
// Prevent M+ or anything else silly from being attached to a balance board.
|
||||
// In the future if we support an emulated balance board we can force the BB "extension" here.
|
||||
return;
|
||||
}
|
||||
|
||||
// FYI: AttachExtension also connects devices to the i2c bus
|
||||
|
||||
if (m_is_motion_plus_attached && !desired_motion_plus)
|
||||
|
@ -231,29 +225,19 @@ void Wiimote::HandleExtensionSwap(ExtensionNumber desired_extension_number,
|
|||
}
|
||||
}
|
||||
|
||||
void Wiimote::HandleRequestStatus(const OutputReportRequestStatus&)
|
||||
void WiimoteBase::HandleRequestStatus(const OutputReportRequestStatus&)
|
||||
{
|
||||
// FYI: buttons are updated in Update() for determinism
|
||||
|
||||
// Update status struct
|
||||
m_status.extension = m_extension_port.IsDeviceConnected();
|
||||
m_status.SetEstimatedCharge(m_battery_setting.GetValue() / ciface::BATTERY_INPUT_MAX_VALUE);
|
||||
|
||||
if (Core::WantsDeterminism())
|
||||
{
|
||||
// One less thing to break determinism:
|
||||
m_status.SetEstimatedCharge(1.f);
|
||||
}
|
||||
|
||||
// Less than 0x20 triggers the low-battery flag:
|
||||
m_status.battery_low = m_status.battery < 0x20;
|
||||
|
||||
TypedInputData<InputReportStatus> rpt(InputReportID::Status);
|
||||
rpt.payload = m_status;
|
||||
InterruptDataInputCallback(rpt.GetData(), rpt.GetSize());
|
||||
}
|
||||
|
||||
void Wiimote::HandleWriteData(const OutputReportWriteData& wd)
|
||||
void WiimoteBase::HandleWriteData(const OutputReportWriteData& wd)
|
||||
{
|
||||
if (m_read_request.size)
|
||||
{
|
||||
|
@ -332,7 +316,7 @@ void Wiimote::HandleReportRumble(const WiimoteCommon::OutputReportRumble& rpt)
|
|||
// FYI: A real wiimote never seems to ACK a rumble report:
|
||||
}
|
||||
|
||||
void Wiimote::HandleReportLeds(const WiimoteCommon::OutputReportLeds& rpt)
|
||||
void WiimoteBase::HandleReportLeds(const WiimoteCommon::OutputReportLeds& rpt)
|
||||
{
|
||||
m_status.leds = rpt.leds;
|
||||
|
||||
|
@ -340,7 +324,7 @@ void Wiimote::HandleReportLeds(const WiimoteCommon::OutputReportLeds& rpt)
|
|||
SendAck(OutputReportID::LED, ErrorCode::Success);
|
||||
}
|
||||
|
||||
void Wiimote::HandleIRLogicEnable2(const WiimoteCommon::OutputReportEnableFeature& rpt)
|
||||
void WiimoteBase::HandleIRLogicEnable2(const WiimoteCommon::OutputReportEnableFeature& rpt)
|
||||
{
|
||||
// FYI: We ignore this and update camera data regardless.
|
||||
|
||||
|
@ -348,7 +332,7 @@ void Wiimote::HandleIRLogicEnable2(const WiimoteCommon::OutputReportEnableFeatur
|
|||
SendAck(OutputReportID::IRLogicEnable2, ErrorCode::Success);
|
||||
}
|
||||
|
||||
void Wiimote::HandleIRLogicEnable(const WiimoteCommon::OutputReportEnableFeature& rpt)
|
||||
void WiimoteBase::HandleIRLogicEnable(const WiimoteCommon::OutputReportEnableFeature& rpt)
|
||||
{
|
||||
// Note: Wiibrew currently refers to this report (0x13) as "Enable IR Pixel Clock"
|
||||
// however my testing shows this affects the relevant status bit and whether or not
|
||||
|
@ -356,8 +340,6 @@ void Wiimote::HandleIRLogicEnable(const WiimoteCommon::OutputReportEnableFeature
|
|||
|
||||
m_status.ir = rpt.enable;
|
||||
|
||||
m_camera_logic.SetEnabled(m_status.ir);
|
||||
|
||||
if (rpt.ack)
|
||||
SendAck(OutputReportID::IRLogicEnable, ErrorCode::Success);
|
||||
}
|
||||
|
@ -370,7 +352,7 @@ void Wiimote::HandleSpeakerMute(const WiimoteCommon::OutputReportEnableFeature&
|
|||
SendAck(OutputReportID::SpeakerMute, ErrorCode::Success);
|
||||
}
|
||||
|
||||
void Wiimote::HandleSpeakerEnable(const WiimoteCommon::OutputReportEnableFeature& rpt)
|
||||
void WiimoteBase::HandleSpeakerEnable(const WiimoteCommon::OutputReportEnableFeature& rpt)
|
||||
{
|
||||
m_status.speaker = rpt.enable;
|
||||
|
||||
|
@ -402,7 +384,7 @@ void Wiimote::HandleSpeakerData(const WiimoteCommon::OutputReportSpeakerData& rp
|
|||
// More investigation is needed.
|
||||
}
|
||||
|
||||
void Wiimote::HandleReadData(const OutputReportReadData& rd)
|
||||
void WiimoteBase::HandleReadData(const OutputReportReadData& rd)
|
||||
{
|
||||
if (m_read_request.size)
|
||||
{
|
||||
|
@ -433,7 +415,7 @@ void Wiimote::HandleReadData(const OutputReportReadData& rd)
|
|||
// FYI: No "ACK" is sent under normal situations.
|
||||
}
|
||||
|
||||
bool Wiimote::ProcessReadDataRequest()
|
||||
auto WiimoteBase::ProcessReadDataRequest() -> UpdateProgress
|
||||
{
|
||||
// Limit the amt to 16 bytes
|
||||
// AyuanX: the MTU is 640B though... what a waste!
|
||||
|
@ -442,7 +424,7 @@ bool Wiimote::ProcessReadDataRequest()
|
|||
if (0 == bytes_to_read)
|
||||
{
|
||||
// No active request:
|
||||
return false;
|
||||
return UpdateProgress::Continue;
|
||||
}
|
||||
|
||||
TypedInputData<InputReportReadDataReply> rpt(InputReportID::ReadDataReply);
|
||||
|
@ -544,32 +526,29 @@ bool Wiimote::ProcessReadDataRequest()
|
|||
|
||||
InterruptDataInputCallback(rpt.GetData(), rpt.GetSize());
|
||||
|
||||
return true;
|
||||
return UpdateProgress::DoNotContinue;
|
||||
}
|
||||
|
||||
void Wiimote::DoState(PointerWrap& p)
|
||||
void WiimoteBase::DoState(PointerWrap& p)
|
||||
{
|
||||
// No need to sync. Index will not change.
|
||||
// p.Do(m_index);
|
||||
|
||||
// No need to sync. This is not wiimote state.
|
||||
// p.Do(m_sensor_bar_on_top);
|
||||
|
||||
p.Do(m_reporting_mode);
|
||||
p.Do(m_reporting_continuous);
|
||||
|
||||
p.Do(m_speaker_mute);
|
||||
|
||||
p.Do(m_status);
|
||||
p.Do(m_eeprom);
|
||||
p.Do(m_read_request);
|
||||
|
||||
// Sub-devices:
|
||||
m_speaker_logic.DoState(p);
|
||||
m_camera_logic.DoState(p);
|
||||
p.DoMarker("WiimoteBase");
|
||||
}
|
||||
|
||||
if (p.IsReadMode())
|
||||
m_camera_logic.SetEnabled(m_status.ir);
|
||||
void Wiimote::DoState(PointerWrap& p)
|
||||
{
|
||||
WiimoteBase::DoState(p);
|
||||
|
||||
p.Do(m_speaker_mute);
|
||||
|
||||
p.Do(m_is_motion_plus_attached);
|
||||
p.Do(m_active_extension);
|
||||
|
@ -585,6 +564,10 @@ void Wiimote::DoState(PointerWrap& p)
|
|||
if (m_active_extension != ExtensionNumber::NONE)
|
||||
GetActiveExtension()->DoState(p);
|
||||
|
||||
// Sub-devices:
|
||||
m_speaker_logic.DoState(p);
|
||||
m_camera_logic.DoState(p);
|
||||
|
||||
// Dynamics
|
||||
p.Do(m_swing_state);
|
||||
p.Do(m_tilt_state);
|
||||
|
@ -597,9 +580,35 @@ void Wiimote::DoState(PointerWrap& p)
|
|||
p.DoMarker("Wiimote");
|
||||
}
|
||||
|
||||
ExtensionNumber Wiimote::GetActiveExtensionNumber() const
|
||||
void BalanceBoard::DoState(PointerWrap& p)
|
||||
{
|
||||
return m_active_extension;
|
||||
WiimoteBase::DoState(p);
|
||||
m_ext.DoState(p);
|
||||
p.DoMarker("BalanceBoard");
|
||||
}
|
||||
|
||||
void BalanceBoard::HandleReportRumble(const WiimoteCommon::OutputReportRumble&)
|
||||
{
|
||||
// Behaves like a Wii Remote (no ACK).
|
||||
}
|
||||
|
||||
void BalanceBoard::HandleSpeakerMute(const WiimoteCommon::OutputReportEnableFeature& rpt)
|
||||
{
|
||||
// ACKs like Wii Remote.
|
||||
if (rpt.ack)
|
||||
SendAck(OutputReportID::SpeakerMute, ErrorCode::Success);
|
||||
}
|
||||
|
||||
void BalanceBoard::HandleSpeakerData(const WiimoteCommon::OutputReportSpeakerData&)
|
||||
{
|
||||
// Behaves like a Wii Remote (no ACK).
|
||||
}
|
||||
|
||||
ControllerEmu::SubscribableSettingValue<int>& Wiimote::GetAttachmentSetting()
|
||||
{
|
||||
return static_cast<ControllerEmu::Attachments*>(
|
||||
GetWiimoteGroup(WiimoteEmu::WiimoteGroup::Attachments))
|
||||
->GetAttachmentSetting();
|
||||
}
|
||||
|
||||
ControllerEmu::SubscribableSettingValue<bool>& Wiimote::GetMotionPlusSetting()
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
// Copyright 2025 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "Core/HW/WiimoteEmu/Extension/BalanceBoard.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
|
||||
#include "Common/Common.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Hash.h"
|
||||
#include "Common/Swap.h"
|
||||
|
||||
#include "Core/HW/WiimoteEmu/Extension/DesiredExtensionState.h"
|
||||
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
|
||||
|
||||
#include "InputCommon/ControllerEmu/ControlGroup/AnalogStick.h"
|
||||
|
||||
namespace WiimoteEmu
|
||||
{
|
||||
constexpr std::array<u8, 6> balance_board_id{{0x00, 0x00, 0xa4, 0x20, 0x04, 0x02}};
|
||||
|
||||
BalanceBoardExt::BalanceBoardExt(BalanceBoard* owner)
|
||||
: Extension1stParty("BalanceBoard", _trans("Balance Board")), m_owner(owner)
|
||||
{
|
||||
}
|
||||
|
||||
// Use the same calibration data for all sensors.
|
||||
// Wii Fit internally converts to grams, but using grams for the actual values leads to
|
||||
// overflowing values, and also underflowing values when a sensor gets negative if balance is
|
||||
// extremely tilted. Actual balance boards tend to have a sensitivity of about 10 grams.
|
||||
|
||||
// Real board values vary greatly but these nice values are very near those of a real board.
|
||||
static constexpr u16 KG17_RANGE = 1700;
|
||||
static constexpr u16 CALIBRATED_0_KG = 10000;
|
||||
static constexpr u16 CALIBRATED_17_KG = CALIBRATED_0_KG + KG17_RANGE;
|
||||
static constexpr u16 CALIBRATED_34_KG = CALIBRATED_17_KG + KG17_RANGE;
|
||||
|
||||
// WiiBrew: "always 0x69"
|
||||
static constexpr u8 REFERENCE_BATTERY = 0x69;
|
||||
|
||||
// Chosen arbitrarily from the value for pokechu22's board. As long as the calibration and
|
||||
// actual temperatures match, the value here doesn't matter.
|
||||
static constexpr u8 REFERENCE_TEMPERATURE = 0x19;
|
||||
|
||||
void BalanceBoardExt::BuildDesiredExtensionState(DesiredExtensionState* target_state)
|
||||
{
|
||||
const ControllerEmu::AnalogStick::StateData balance_state = m_owner->m_balance->GetState();
|
||||
|
||||
const bool is_stepped_off = m_owner->m_options->controls[0]->GetState<bool>();
|
||||
|
||||
const double weight = is_stepped_off ? 0.0 : m_owner->m_weight_setting.GetValue();
|
||||
|
||||
auto [weight_tr, weight_br, weight_tl, weight_bl] =
|
||||
CenterOfBalanceToSensors(balance_state, weight).data;
|
||||
|
||||
if (auto& func = m_input_override_function)
|
||||
{
|
||||
weight_tr = func(BALANCE_GROUP, SENSOR_TR, weight_tr).value_or(weight_tr);
|
||||
weight_br = func(BALANCE_GROUP, SENSOR_BR, weight_br).value_or(weight_br);
|
||||
weight_tl = func(BALANCE_GROUP, SENSOR_TL, weight_tl).value_or(weight_tl);
|
||||
weight_bl = func(BALANCE_GROUP, SENSOR_BL, weight_bl).value_or(weight_bl);
|
||||
}
|
||||
|
||||
DataFormat bb_data = {};
|
||||
|
||||
bb_data.sensor_tr = Common::swap16(ConvertToSensorWeight(weight_tr));
|
||||
bb_data.sensor_br = Common::swap16(ConvertToSensorWeight(weight_br));
|
||||
bb_data.sensor_tl = Common::swap16(ConvertToSensorWeight(weight_tl));
|
||||
bb_data.sensor_bl = Common::swap16(ConvertToSensorWeight(weight_bl));
|
||||
|
||||
target_state->data = bb_data;
|
||||
}
|
||||
|
||||
void BalanceBoardExt::Update(const DesiredExtensionState& target_state)
|
||||
{
|
||||
DefaultExtensionUpdate<DataFormat>(&m_reg, target_state);
|
||||
|
||||
m_reg.controller_data[0x8] = REFERENCE_TEMPERATURE;
|
||||
m_reg.controller_data[0x9] = 0x00;
|
||||
|
||||
// FYI: Real EXT battery byte doesn't exactly match status report battery byte.
|
||||
// e.g. Seen: EXT:0x9e and Status:0xc6
|
||||
// I'm guessing they just have separate ADCs.
|
||||
m_reg.controller_data[0xa] = m_owner->m_status.battery;
|
||||
}
|
||||
|
||||
void BalanceBoardExt::Reset()
|
||||
{
|
||||
EncryptedExtension::Reset();
|
||||
|
||||
m_reg.identifier = balance_board_id;
|
||||
|
||||
struct CalibrationData
|
||||
{
|
||||
u8 always_0x01 = 0x01;
|
||||
u8 battery = REFERENCE_BATTERY;
|
||||
u8 always_0x00[2] = {};
|
||||
|
||||
using SensorValue = Common::BigEndianValue<u16>;
|
||||
|
||||
// Each array is ordered: TR, BR, TL, BL
|
||||
std::array<SensorValue, 4> kg0;
|
||||
std::array<SensorValue, 4> kg17;
|
||||
std::array<SensorValue, 4> kg34;
|
||||
|
||||
u8 checksum[4];
|
||||
};
|
||||
static_assert(sizeof(CalibrationData) == 0x20, "Wrong size");
|
||||
|
||||
CalibrationData cal_data{};
|
||||
cal_data.kg0.fill(CalibrationData::SensorValue{CALIBRATED_0_KG});
|
||||
cal_data.kg17.fill(CalibrationData::SensorValue{CALIBRATED_17_KG});
|
||||
cal_data.kg34.fill(CalibrationData::SensorValue{CALIBRATED_34_KG});
|
||||
|
||||
std::copy_n(reinterpret_cast<char*>(&cal_data), 0x10, m_reg.calibration.data());
|
||||
std::copy_n(reinterpret_cast<char*>(&cal_data) + 0x10, 0x10, m_reg.calibration2.data());
|
||||
m_reg.calibration3 = {REFERENCE_TEMPERATURE, 0x01};
|
||||
|
||||
ComputeCalibrationChecksum();
|
||||
}
|
||||
|
||||
u16 BalanceBoardExt::ConvertToSensorWeight(double weight_in_kilos)
|
||||
{
|
||||
return MapFloat((weight_in_kilos - 17.0) / 17.0, CALIBRATED_17_KG, CALIBRATED_0_KG,
|
||||
CALIBRATED_34_KG);
|
||||
}
|
||||
|
||||
double BalanceBoardExt::ConvertToKilograms(u16 sensor_weight)
|
||||
{
|
||||
const auto result =
|
||||
MapToFloat<double>(sensor_weight, CALIBRATED_17_KG, CALIBRATED_0_KG, CALIBRATED_34_KG);
|
||||
return result * 17.0 + 17.0;
|
||||
}
|
||||
|
||||
void BalanceBoardExt::ComputeCalibrationChecksum()
|
||||
{
|
||||
u32 crc = Common::StartCRC32();
|
||||
// Skip the first 4 bytes
|
||||
crc = Common::UpdateCRC32(crc, &m_reg.calibration[4], 0xc);
|
||||
// Skip the last 4 bytes (the CRC itself)
|
||||
crc = Common::UpdateCRC32(crc, &m_reg.calibration2[0], 0xc);
|
||||
// Hash 2 of the bytes skipped earlier
|
||||
crc = Common::UpdateCRC32(crc, &m_reg.calibration[0], 2);
|
||||
crc = Common::UpdateCRC32(crc, &m_reg.calibration3[0], 2);
|
||||
|
||||
m_reg.calibration2[0x0c] = u8(crc >> 24);
|
||||
m_reg.calibration2[0x0d] = u8(crc >> 16);
|
||||
m_reg.calibration2[0x0e] = u8(crc >> 8);
|
||||
m_reg.calibration2[0x0f] = u8(crc);
|
||||
}
|
||||
|
||||
Common::DVec2 BalanceBoardExt::SensorsToCenterOfBalance(Common::DVec4 sensors)
|
||||
{
|
||||
const double right = sensors.data[0] + sensors.data[1];
|
||||
const double left = sensors.data[2] + sensors.data[3];
|
||||
|
||||
const double total = right + left;
|
||||
if (!total)
|
||||
return Common::DVec2{};
|
||||
|
||||
const double top = sensors.data[0] + sensors.data[2];
|
||||
const double bottom = sensors.data[1] + sensors.data[3];
|
||||
return Common::DVec2(right - left, top - bottom) / total;
|
||||
}
|
||||
|
||||
Common::DVec4 BalanceBoardExt::CenterOfBalanceToSensors(Common::DVec2 balance, double total_weight)
|
||||
{
|
||||
return Common::DVec4{(1 + balance.x) * (1 + balance.y), (1 + balance.x) * (1 - balance.y),
|
||||
(1 - balance.x) * (1 + balance.y), (1 - balance.x) * (1 - balance.y)} *
|
||||
(total_weight * 0.25);
|
||||
}
|
||||
|
||||
} // namespace WiimoteEmu
|
|
@ -0,0 +1,59 @@
|
|||
// Copyright 2025 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Common/Matrix.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/Extension.h"
|
||||
|
||||
namespace WiimoteEmu
|
||||
{
|
||||
class BalanceBoard;
|
||||
|
||||
// NOTE: Although the balance board is implemented as an extension (which matches how the actual
|
||||
// balance board works), actual controls are not registered in the same way as other extensions;
|
||||
// WiimoteEmu::BalanceBoard has all of the controls and some are also used by the extension.
|
||||
class BalanceBoardExt : public Extension1stParty
|
||||
{
|
||||
public:
|
||||
struct DataFormat
|
||||
{
|
||||
// top-right, bottom-right, top-left, bottom-left
|
||||
u16 sensor_tr;
|
||||
u16 sensor_br;
|
||||
u16 sensor_tl;
|
||||
u16 sensor_bl;
|
||||
// Note: temperature, padding, and battery bytes are not included.
|
||||
// temperature change is not exposed, and arguably not important.
|
||||
// battery level is pulled from the wii remote state.
|
||||
};
|
||||
static_assert(sizeof(DataFormat) == 8, "Wrong size");
|
||||
|
||||
static constexpr const char* BALANCE_GROUP = "Balance";
|
||||
|
||||
static constexpr const char* SENSOR_TR = "TR";
|
||||
static constexpr const char* SENSOR_BR = "BR";
|
||||
static constexpr const char* SENSOR_TL = "TL";
|
||||
static constexpr const char* SENSOR_BL = "BL";
|
||||
|
||||
BalanceBoardExt(BalanceBoard* owner);
|
||||
|
||||
static constexpr float DEFAULT_WEIGHT = 60;
|
||||
|
||||
void BuildDesiredExtensionState(DesiredExtensionState* target_state) override;
|
||||
void Update(const DesiredExtensionState& target_state) override;
|
||||
void Reset() override;
|
||||
|
||||
// Vec4 ordered: top-right, bottom-right, top-left, bottom-left
|
||||
static Common::DVec2 SensorsToCenterOfBalance(Common::DVec4 sensors);
|
||||
static Common::DVec4 CenterOfBalanceToSensors(Common::DVec2 balance, double total_weight);
|
||||
|
||||
static u16 ConvertToSensorWeight(double weight_in_kilos);
|
||||
static double ConvertToKilograms(u16 sensor_weight);
|
||||
|
||||
private:
|
||||
void ComputeCalibrationChecksum();
|
||||
|
||||
const BalanceBoard* m_owner;
|
||||
};
|
||||
} // namespace WiimoteEmu
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "Common/BitUtils.h"
|
||||
|
||||
#include "Core/HW/WiimoteEmu/Extension/BalanceBoard.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/Classic.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/DrawsomeTablet.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/Drums.h"
|
||||
|
@ -27,7 +28,8 @@ struct DesiredExtensionState
|
|||
using ExtensionData =
|
||||
std::variant<std::monostate, Nunchuk::DataFormat, Classic::DataFormat, Guitar::DataFormat,
|
||||
Drums::DesiredState, Turntable::DataFormat, UDrawTablet::DataFormat,
|
||||
DrawsomeTablet::DataFormat, TaTaCon::DataFormat, Shinkansen::DesiredState>;
|
||||
DrawsomeTablet::DataFormat, TaTaCon::DataFormat, Shinkansen::DesiredState,
|
||||
BalanceBoardExt::DataFormat>;
|
||||
ExtensionData data = std::monostate();
|
||||
|
||||
static_assert(std::is_same_v<std::monostate,
|
||||
|
@ -57,6 +59,9 @@ struct DesiredExtensionState
|
|||
static_assert(
|
||||
std::is_same_v<Shinkansen::DesiredState,
|
||||
std::variant_alternative_t<ExtensionNumber::SHINKANSEN, ExtensionData>>);
|
||||
static_assert(
|
||||
std::is_same_v<BalanceBoardExt::DataFormat,
|
||||
std::variant_alternative_t<ExtensionNumber::BALANCE_BOARD, ExtensionData>>);
|
||||
static_assert(std::variant_size_v<DesiredExtensionState::ExtensionData> == ExtensionNumber::MAX);
|
||||
};
|
||||
|
||||
|
|
|
@ -64,6 +64,8 @@ private:
|
|||
class EncryptedExtension : public Extension
|
||||
{
|
||||
public:
|
||||
void DoState(PointerWrap& p) override;
|
||||
|
||||
static constexpr u8 I2C_ADDR = 0x52;
|
||||
static constexpr int CONTROLLER_DATA_BYTES = 21;
|
||||
|
||||
|
@ -85,11 +87,14 @@ public:
|
|||
|
||||
// address 0x20
|
||||
std::array<u8, 0x10> calibration;
|
||||
u8 unknown3[0x10];
|
||||
std::array<u8, 0x10> calibration2;
|
||||
|
||||
// address 0x40
|
||||
std::array<u8, 0x10> encryption_key_data;
|
||||
u8 unknown4[0xA0];
|
||||
u8 unknown3[0x10];
|
||||
// Address 0x60
|
||||
std::array<u8, 2> calibration3;
|
||||
u8 unknown4[0x8e];
|
||||
|
||||
// address 0xF0
|
||||
u8 encryption;
|
||||
|
@ -106,7 +111,6 @@ protected:
|
|||
Register m_reg = {};
|
||||
|
||||
void Reset() override;
|
||||
void DoState(PointerWrap& p) override;
|
||||
|
||||
virtual void UpdateEncryptionKey() = 0;
|
||||
|
||||
|
|
|
@ -23,6 +23,8 @@ enum ExtensionNumber : u8
|
|||
TATACON,
|
||||
SHINKANSEN,
|
||||
|
||||
BALANCE_BOARD,
|
||||
|
||||
MAX
|
||||
};
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include "Common/FileUtil.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/MathUtil.h"
|
||||
#include "Common/MsgHandler.h"
|
||||
|
||||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/Core.h"
|
||||
|
@ -28,6 +27,7 @@
|
|||
#include "Core/HW/WiimoteCommon/WiimoteConstants.h"
|
||||
#include "Core/HW/WiimoteCommon/WiimoteHid.h"
|
||||
#include "Core/HW/WiimoteEmu/DesiredWiimoteState.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/BalanceBoard.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/Classic.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/DesiredExtensionState.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/DrawsomeTablet.h"
|
||||
|
@ -39,8 +39,7 @@
|
|||
#include "Core/HW/WiimoteEmu/Extension/Turntable.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/UDrawTablet.h"
|
||||
|
||||
#include "InputCommon/ControllerEmu/Control/Input.h"
|
||||
#include "InputCommon/ControllerEmu/Control/Output.h"
|
||||
#include "InputCommon/ControllerEmu/ControlGroup/AnalogStick.h"
|
||||
#include "InputCommon/ControllerEmu/ControlGroup/Attachments.h"
|
||||
#include "InputCommon/ControllerEmu/ControlGroup/Buttons.h"
|
||||
#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h"
|
||||
|
@ -67,18 +66,21 @@ static const u16 dpad_bitmasks[] = {Wiimote::PAD_UP, Wiimote::PAD_DOWN, Wiimote:
|
|||
static const u16 dpad_sideways_bitmasks[] = {Wiimote::PAD_RIGHT, Wiimote::PAD_LEFT, Wiimote::PAD_UP,
|
||||
Wiimote::PAD_DOWN};
|
||||
|
||||
void Wiimote::Reset()
|
||||
static const u16 bboard_bitmasks[] = {BalanceBoard::BUTTON_POWER};
|
||||
|
||||
static constexpr u16 IR_LOW_X = 0x7F;
|
||||
static constexpr u16 IR_LOW_Y = 0x5D;
|
||||
static constexpr u16 IR_HIGH_X = 0x380;
|
||||
static constexpr u16 IR_HIGH_Y = 0x2A2;
|
||||
|
||||
void WiimoteBase::Reset()
|
||||
{
|
||||
const bool want_determinism = Core::WantsDeterminism();
|
||||
|
||||
SetRumble(false);
|
||||
|
||||
// Wiimote starts in non-continuous CORE mode:
|
||||
m_reporting_mode = InputReportID::ReportCore;
|
||||
m_reporting_continuous = false;
|
||||
|
||||
m_speaker_mute = false;
|
||||
|
||||
// EEPROM
|
||||
|
||||
// TODO: This feels sketchy, this needs to properly handle the case where the load and the write
|
||||
|
@ -108,6 +110,74 @@ void Wiimote::Reset()
|
|||
}
|
||||
else
|
||||
{
|
||||
LoadDefaultEeprom();
|
||||
}
|
||||
|
||||
m_read_request = {};
|
||||
|
||||
// Initialize i2c bus:
|
||||
m_i2c_bus.Reset();
|
||||
|
||||
m_status = {};
|
||||
|
||||
// A real wii remote does not normally send a status report on connection.
|
||||
// But if an extension is already attached it does send one.
|
||||
// Clearing this initially will simulate that on the first update cycle.
|
||||
m_status.extension = 0;
|
||||
}
|
||||
|
||||
u8 WiimoteBase::GetWiimoteDeviceIndex() const
|
||||
{
|
||||
return m_bt_device_index;
|
||||
}
|
||||
|
||||
void WiimoteBase::SetWiimoteDeviceIndex(u8 index)
|
||||
{
|
||||
m_bt_device_index = index;
|
||||
}
|
||||
|
||||
void Wiimote::Reset()
|
||||
{
|
||||
const bool want_determinism = Core::WantsDeterminism();
|
||||
|
||||
SetRumble(false);
|
||||
m_speaker_mute = false;
|
||||
|
||||
// Reset extension connections to NONE:
|
||||
m_is_motion_plus_attached = false;
|
||||
m_active_extension = ExtensionNumber::NONE;
|
||||
m_extension_port.AttachExtension(GetNoneExtension());
|
||||
m_motion_plus.GetExtPort().AttachExtension(GetNoneExtension());
|
||||
|
||||
if (!want_determinism)
|
||||
{
|
||||
// Switch to desired M+ status and extension (if any).
|
||||
// M+ and EXT are reset on attachment.
|
||||
HandleExtensionSwap(static_cast<ExtensionNumber>(m_attachments->GetSelectedAttachment()),
|
||||
m_motion_plus_setting.GetValue());
|
||||
}
|
||||
|
||||
WiimoteBase::Reset();
|
||||
|
||||
// Initialize i2c bus:
|
||||
m_i2c_bus.AddSlave(&m_speaker_logic);
|
||||
m_i2c_bus.AddSlave(&m_camera_logic);
|
||||
|
||||
// Reset sub-devices.
|
||||
m_speaker_logic.Reset();
|
||||
m_camera_logic.Reset();
|
||||
|
||||
// Dynamics:
|
||||
m_swing_state = {};
|
||||
m_tilt_state = {};
|
||||
m_point_state = {};
|
||||
m_shake_state = {};
|
||||
|
||||
m_imu_cursor_state = {};
|
||||
}
|
||||
|
||||
void Wiimote::LoadDefaultEeprom()
|
||||
{
|
||||
// Load some default data.
|
||||
|
||||
// IR calibration:
|
||||
|
@ -148,9 +218,9 @@ void Wiimote::Reset()
|
|||
|
||||
// TODO: Is this needed?
|
||||
// Data of unknown purpose:
|
||||
constexpr std::array<u8, 24> EEPROM_DATA_16D0 = {
|
||||
0x00, 0x00, 0x00, 0xFF, 0x11, 0xEE, 0x00, 0x00, 0x33, 0xCC, 0x44, 0xBB,
|
||||
0x00, 0x00, 0x66, 0x99, 0x77, 0x88, 0x00, 0x00, 0x2B, 0x01, 0xE8, 0x13};
|
||||
constexpr std::array<u8, 24> EEPROM_DATA_16D0 = {0x00, 0x00, 0x00, 0xFF, 0x11, 0xEE, 0x00, 0x00,
|
||||
0x33, 0xCC, 0x44, 0xBB, 0x00, 0x00, 0x66, 0x99,
|
||||
0x77, 0x88, 0x00, 0x00, 0x2B, 0x01, 0xE8, 0x13};
|
||||
m_eeprom.unk_2 = EEPROM_DATA_16D0;
|
||||
|
||||
std::string mii_file = File::GetUserPath(D_SESSION_WIIROOT_IDX) + "/mii.bin";
|
||||
|
@ -163,50 +233,24 @@ void Wiimote::Reset()
|
|||
m_eeprom.mii_data_2 = m_eeprom.mii_data_1;
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
m_read_request = {};
|
||||
|
||||
// Initialize i2c bus:
|
||||
m_i2c_bus.Reset();
|
||||
m_i2c_bus.AddSlave(&m_speaker_logic);
|
||||
m_i2c_bus.AddSlave(&m_camera_logic);
|
||||
|
||||
// Reset extension connections to NONE:
|
||||
m_is_motion_plus_attached = false;
|
||||
m_active_extension = ExtensionNumber::NONE;
|
||||
m_extension_port.AttachExtension(GetNoneExtension());
|
||||
m_motion_plus.GetExtPort().AttachExtension(GetNoneExtension());
|
||||
|
||||
if (!want_determinism)
|
||||
{
|
||||
// Switch to desired M+ status and extension (if any).
|
||||
// M+ and EXT are reset on attachment.
|
||||
HandleExtensionSwap(static_cast<ExtensionNumber>(m_attachments->GetSelectedAttachment()),
|
||||
m_motion_plus_setting.GetValue());
|
||||
}
|
||||
|
||||
// Reset sub-devices.
|
||||
m_speaker_logic.Reset();
|
||||
m_camera_logic.Reset();
|
||||
|
||||
m_status = {};
|
||||
|
||||
// A real wii remote does not normally send a status report on connection.
|
||||
// But if an extension is already attached it does send one.
|
||||
// Clearing this initially will simulate that on the first update cycle.
|
||||
m_status.extension = 0;
|
||||
|
||||
// Dynamics:
|
||||
m_swing_state = {};
|
||||
m_tilt_state = {};
|
||||
m_point_state = {};
|
||||
m_shake_state = {};
|
||||
|
||||
m_imu_cursor_state = {};
|
||||
}
|
||||
|
||||
Wiimote::Wiimote(const unsigned int index) : m_index(index), m_bt_device_index(index)
|
||||
void BalanceBoard::LoadDefaultEeprom()
|
||||
{
|
||||
// A real balance board starts with zero-filled EEPROM.
|
||||
m_eeprom = {};
|
||||
}
|
||||
|
||||
WiimoteBase::WiimoteBase(const u8 index) : m_index(index), m_bt_device_index(index)
|
||||
{
|
||||
}
|
||||
|
||||
InputConfig* WiimoteBase::GetConfig() const
|
||||
{
|
||||
return ::Wiimote::GetConfig();
|
||||
}
|
||||
|
||||
Wiimote::Wiimote(const u8 index) : WiimoteBase(index)
|
||||
{
|
||||
using Translatability = ControllerEmu::Translatability;
|
||||
|
||||
|
@ -280,7 +324,6 @@ Wiimote::Wiimote(const unsigned int index) : m_index(index), m_bt_device_index(i
|
|||
m_attachments->AddAttachment(std::make_unique<WiimoteEmu::DrawsomeTablet>());
|
||||
m_attachments->AddAttachment(std::make_unique<WiimoteEmu::TaTaCon>());
|
||||
m_attachments->AddAttachment(std::make_unique<WiimoteEmu::Shinkansen>());
|
||||
|
||||
m_attachments->AddSetting(&m_motion_plus_setting, {_trans("Attach MotionPlus")}, true);
|
||||
|
||||
// Rumble
|
||||
|
@ -323,16 +366,9 @@ Wiimote::~Wiimote()
|
|||
|
||||
std::string Wiimote::GetName() const
|
||||
{
|
||||
if (m_index == WIIMOTE_BALANCE_BOARD)
|
||||
return "BalanceBoard";
|
||||
return fmt::format("Wiimote{}", 1 + m_index);
|
||||
}
|
||||
|
||||
InputConfig* Wiimote::GetConfig() const
|
||||
{
|
||||
return ::Wiimote::GetConfig();
|
||||
}
|
||||
|
||||
ControllerEmu::ControlGroup* Wiimote::GetWiimoteGroup(WiimoteGroup group) const
|
||||
{
|
||||
switch (group)
|
||||
|
@ -429,13 +465,13 @@ ControllerEmu::ControlGroup* Wiimote::GetShinkansenGroup(ShinkansenGroup group)
|
|||
->GetGroup(group);
|
||||
}
|
||||
|
||||
bool Wiimote::ProcessExtensionPortEvent()
|
||||
auto WiimoteBase::ProcessExtensionPortEvent() -> UpdateProgress
|
||||
{
|
||||
// WiiBrew: Following a connection or disconnection event on the Extension Port,
|
||||
// data reporting is disabled and the Data Reporting Mode must be reset before new data can
|
||||
// arrive.
|
||||
if (m_extension_port.IsDeviceConnected() == m_status.extension)
|
||||
return false;
|
||||
return UpdateProgress::Continue;
|
||||
|
||||
// FYI: This happens even during a read request which continues after the status report is sent.
|
||||
m_reporting_mode = InputReportID::ReportDisabled;
|
||||
|
@ -444,7 +480,7 @@ bool Wiimote::ProcessExtensionPortEvent()
|
|||
|
||||
HandleRequestStatus(OutputReportRequestStatus{});
|
||||
|
||||
return true;
|
||||
return UpdateProgress::DoNotContinue;
|
||||
}
|
||||
|
||||
void Wiimote::UpdateButtonsStatus(const DesiredWiimoteState& target_state)
|
||||
|
@ -452,6 +488,14 @@ void Wiimote::UpdateButtonsStatus(const DesiredWiimoteState& target_state)
|
|||
m_status.buttons.hex = target_state.buttons.hex & ButtonData::BUTTON_MASK;
|
||||
}
|
||||
|
||||
void Wiimote::UpdateBatteryStatus(double charge)
|
||||
{
|
||||
m_status.SetEstimatedCharge(charge);
|
||||
|
||||
// Less than 0x20 triggers the low-battery flag:
|
||||
m_status.battery_low = m_status.battery < 0x20;
|
||||
}
|
||||
|
||||
static std::array<CameraPoint, CameraLogic::NUM_POINTS>
|
||||
GetPassthroughCameraPoints(ControllerEmu::IRPassthrough* ir_passthrough)
|
||||
{
|
||||
|
@ -533,61 +577,85 @@ void Wiimote::BuildDesiredWiimoteState(DesiredWiimoteState* target_state,
|
|||
->BuildDesiredExtensionState(&target_state->extension);
|
||||
}
|
||||
|
||||
u8 Wiimote::GetWiimoteDeviceIndex() const
|
||||
{
|
||||
return m_bt_device_index;
|
||||
}
|
||||
|
||||
void Wiimote::SetWiimoteDeviceIndex(u8 index)
|
||||
{
|
||||
m_bt_device_index = index;
|
||||
}
|
||||
|
||||
// This is called every ::Wiimote::UPDATE_FREQ (200hz)
|
||||
void Wiimote::PrepareInput(WiimoteEmu::DesiredWiimoteState* target_state,
|
||||
void WiimoteBase::PrepareInput(WiimoteEmu::DesiredWiimoteState* target_state,
|
||||
SensorBarState sensor_bar_state)
|
||||
{
|
||||
const auto lock = GetStateLock();
|
||||
BuildDesiredWiimoteState(target_state, sensor_bar_state);
|
||||
}
|
||||
|
||||
void Wiimote::Update(const WiimoteEmu::DesiredWiimoteState& target_state)
|
||||
auto WiimoteBase::ProcessEvents() -> UpdateProgress
|
||||
{
|
||||
if (ProcessExtensionPortEvent() == UpdateProgress::DoNotContinue)
|
||||
{
|
||||
// Extension port event occurred.
|
||||
// Don't send any other reports.
|
||||
return UpdateProgress::DoNotContinue;
|
||||
}
|
||||
|
||||
if (ProcessReadDataRequest() == UpdateProgress::DoNotContinue)
|
||||
{
|
||||
// Read requests suppress normal input reports
|
||||
// Don't send any other reports
|
||||
return UpdateProgress::DoNotContinue;
|
||||
}
|
||||
|
||||
return UpdateProgress::Continue;
|
||||
}
|
||||
|
||||
void WiimoteBase::Update(const WiimoteEmu::DesiredWiimoteState& target_state)
|
||||
{
|
||||
// Update buttons in the status struct which is sent in 99% of input reports.
|
||||
UpdateButtonsStatus(target_state);
|
||||
|
||||
if (Core::WantsDeterminism())
|
||||
{
|
||||
// One less thing to break determinism:
|
||||
UpdateBatteryStatus(1.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateBatteryStatus(m_battery_setting.GetValue() / ciface::BATTERY_INPUT_MAX_VALUE);
|
||||
}
|
||||
|
||||
SubDeviceUpdate(target_state);
|
||||
|
||||
if (ProcessEvents() == UpdateProgress::DoNotContinue)
|
||||
return;
|
||||
|
||||
SendDataReport(target_state);
|
||||
}
|
||||
|
||||
void Wiimote::SubDeviceUpdate(const WiimoteEmu::DesiredWiimoteState& target_state)
|
||||
{
|
||||
m_camera_logic.Update(target_state.camera_points);
|
||||
|
||||
// If a new extension is requested in the GUI the change will happen here.
|
||||
HandleExtensionSwap(static_cast<ExtensionNumber>(target_state.extension.data.index()),
|
||||
target_state.motion_plus.has_value());
|
||||
|
||||
// Prepare input data of the extension for reading.
|
||||
// Prepare extension input first as motion-plus may read from it.
|
||||
// FYI: This should only happen when trigged by EXT bus read.
|
||||
// but it's really not going to break anything being here.
|
||||
// and refactoring is needed to do it properly.
|
||||
GetActiveExtension()->Update(target_state.extension);
|
||||
|
||||
if (m_is_motion_plus_attached)
|
||||
{
|
||||
// M+ has some internal state that must processed.
|
||||
m_motion_plus.Update(target_state.extension);
|
||||
}
|
||||
|
||||
// Returns true if a report was sent.
|
||||
if (ProcessExtensionPortEvent())
|
||||
{
|
||||
// Extension port event occurred.
|
||||
// Don't send any other reports.
|
||||
return;
|
||||
// FYI: This should really only happen when trigged by EXT bus read.
|
||||
// refactoring is needed to do that properly.
|
||||
m_motion_plus.PrepareInput(target_state.motion_plus.has_value() ?
|
||||
target_state.motion_plus.value() :
|
||||
MotionPlus::GetDefaultGyroscopeData());
|
||||
}
|
||||
|
||||
if (ProcessReadDataRequest())
|
||||
{
|
||||
// Read requests suppress normal input reports
|
||||
// Don't send any other reports
|
||||
return;
|
||||
}
|
||||
|
||||
SendDataReport(target_state);
|
||||
}
|
||||
|
||||
void Wiimote::SendDataReport(const DesiredWiimoteState& target_state)
|
||||
void WiimoteBase::SendDataReport(const DesiredWiimoteState& target_state)
|
||||
{
|
||||
auto& movie = Core::System::GetInstance().GetMovie();
|
||||
movie.SetPolledDevice();
|
||||
|
@ -607,8 +675,9 @@ void Wiimote::SendDataReport(const DesiredWiimoteState& target_state)
|
|||
|
||||
DataReportBuilder rpt_builder(m_reporting_mode);
|
||||
|
||||
if (movie.IsPlayingInput() && movie.PlayWiimote(m_bt_device_index, rpt_builder,
|
||||
m_active_extension, GetExtensionEncryptionKey()))
|
||||
if (movie.IsPlayingInput() &&
|
||||
movie.PlayWiimote(m_bt_device_index, rpt_builder, GetActiveExtensionNumber(),
|
||||
GetExtensionEncryptionKey()))
|
||||
{
|
||||
// Update buttons in status struct from movie:
|
||||
rpt_builder.GetCoreData(&m_status.buttons);
|
||||
|
@ -630,10 +699,6 @@ void Wiimote::SendDataReport(const DesiredWiimoteState& target_state)
|
|||
// IR Camera:
|
||||
if (rpt_builder.HasIR())
|
||||
{
|
||||
// Note: Camera logic currently contains no changing state so we can just update it here.
|
||||
// If that changes this should be moved to Wiimote::Update();
|
||||
m_camera_logic.Update(target_state.camera_points);
|
||||
|
||||
// The real wiimote reads camera data from the i2c bus starting at offset 0x37:
|
||||
const u8 camera_data_offset =
|
||||
CameraLogic::REPORT_DATA_OFFSET + rpt_builder.GetIRDataFormatOffset();
|
||||
|
@ -652,19 +717,6 @@ void Wiimote::SendDataReport(const DesiredWiimoteState& target_state)
|
|||
// Extension port:
|
||||
if (rpt_builder.HasExt())
|
||||
{
|
||||
// Prepare extension input first as motion-plus may read from it.
|
||||
// This currently happens in Wiimote::Update();
|
||||
// TODO: Separate extension input data preparation from Update.
|
||||
// GetActiveExtension()->PrepareInput();
|
||||
|
||||
if (m_is_motion_plus_attached)
|
||||
{
|
||||
// TODO: Make input preparation triggered by bus read.
|
||||
m_motion_plus.PrepareInput(target_state.motion_plus.has_value() ?
|
||||
target_state.motion_plus.value() :
|
||||
MotionPlus::GetDefaultGyroscopeData());
|
||||
}
|
||||
|
||||
u8* ext_data = rpt_builder.GetExtDataPtr();
|
||||
const u8 ext_size = rpt_builder.GetExtDataSize();
|
||||
|
||||
|
@ -677,7 +729,7 @@ void Wiimote::SendDataReport(const DesiredWiimoteState& target_state)
|
|||
}
|
||||
}
|
||||
|
||||
movie.CheckWiimoteStatus(m_bt_device_index, rpt_builder, m_active_extension,
|
||||
movie.CheckWiimoteStatus(m_bt_device_index, rpt_builder, GetActiveExtensionNumber(),
|
||||
GetExtensionEncryptionKey());
|
||||
|
||||
// Send the report:
|
||||
|
@ -741,15 +793,16 @@ void Wiimote::LoadDefaults(const ControllerInterface& ciface)
|
|||
// B
|
||||
m_buttons->SetControlExpression(1, "`Click 1`");
|
||||
#endif
|
||||
m_buttons->SetControlExpression(2, "`1`"); // 1
|
||||
m_buttons->SetControlExpression(3, "`2`"); // 2
|
||||
m_buttons->SetControlExpression(4, "Q"); // -
|
||||
m_buttons->SetControlExpression(5, "E"); // +
|
||||
// 1, 2, -, +
|
||||
m_buttons->SetControlExpression(2, "`1`");
|
||||
m_buttons->SetControlExpression(3, "`2`");
|
||||
m_buttons->SetControlExpression(4, "Q");
|
||||
m_buttons->SetControlExpression(5, "E");
|
||||
|
||||
#ifdef _WIN32
|
||||
m_buttons->SetControlExpression(6, "RETURN"); // Home
|
||||
#else
|
||||
// Home
|
||||
#ifdef _WIN32
|
||||
m_buttons->SetControlExpression(6, "RETURN");
|
||||
#else
|
||||
m_buttons->SetControlExpression(6, "Return");
|
||||
#endif
|
||||
|
||||
|
@ -769,20 +822,20 @@ void Wiimote::LoadDefaults(const ControllerInterface& ciface)
|
|||
|
||||
// DPad
|
||||
#ifdef _WIN32
|
||||
m_dpad->SetControlExpression(0, "UP"); // Up
|
||||
m_dpad->SetControlExpression(1, "DOWN"); // Down
|
||||
m_dpad->SetControlExpression(2, "LEFT"); // Left
|
||||
m_dpad->SetControlExpression(3, "RIGHT"); // Right
|
||||
m_dpad->SetControlExpression(0, "UP");
|
||||
m_dpad->SetControlExpression(1, "DOWN");
|
||||
m_dpad->SetControlExpression(2, "LEFT");
|
||||
m_dpad->SetControlExpression(3, "RIGHT");
|
||||
#elif __APPLE__
|
||||
m_dpad->SetControlExpression(0, "`Up Arrow`"); // Up
|
||||
m_dpad->SetControlExpression(1, "`Down Arrow`"); // Down
|
||||
m_dpad->SetControlExpression(2, "`Left Arrow`"); // Left
|
||||
m_dpad->SetControlExpression(3, "`Right Arrow`"); // Right
|
||||
m_dpad->SetControlExpression(0, "`Up Arrow`");
|
||||
m_dpad->SetControlExpression(1, "`Down Arrow`");
|
||||
m_dpad->SetControlExpression(2, "`Left Arrow`");
|
||||
m_dpad->SetControlExpression(3, "`Right Arrow`");
|
||||
#else
|
||||
m_dpad->SetControlExpression(0, "Up"); // Up
|
||||
m_dpad->SetControlExpression(1, "Down"); // Down
|
||||
m_dpad->SetControlExpression(2, "Left"); // Left
|
||||
m_dpad->SetControlExpression(3, "Right"); // Right
|
||||
m_dpad->SetControlExpression(0, "Up");
|
||||
m_dpad->SetControlExpression(1, "Down");
|
||||
m_dpad->SetControlExpression(2, "Left");
|
||||
m_dpad->SetControlExpression(3, "Right");
|
||||
#endif
|
||||
|
||||
// Motion Source
|
||||
|
@ -817,12 +870,27 @@ Extension* Wiimote::GetNoneExtension() const
|
|||
return static_cast<Extension*>(m_attachments->GetAttachmentList()[ExtensionNumber::NONE].get());
|
||||
}
|
||||
|
||||
Extension* Wiimote::GetActiveExtension() const
|
||||
Extension* Wiimote::GetActiveExtension()
|
||||
{
|
||||
return static_cast<Extension*>(m_attachments->GetAttachmentList()[m_active_extension].get());
|
||||
}
|
||||
|
||||
EncryptionKey Wiimote::GetExtensionEncryptionKey() const
|
||||
Extension* BalanceBoard::GetActiveExtension()
|
||||
{
|
||||
return &m_ext;
|
||||
}
|
||||
|
||||
ExtensionNumber Wiimote::GetActiveExtensionNumber() const
|
||||
{
|
||||
return m_active_extension;
|
||||
}
|
||||
|
||||
ExtensionNumber BalanceBoard::GetActiveExtensionNumber() const
|
||||
{
|
||||
return ExtensionNumber::BALANCE_BOARD;
|
||||
}
|
||||
|
||||
EncryptionKey WiimoteBase::GetExtensionEncryptionKey()
|
||||
{
|
||||
if (ExtensionNumber::NONE == GetActiveExtensionNumber())
|
||||
return {};
|
||||
|
@ -991,4 +1059,123 @@ Common::Matrix44 Wiimote::GetTotalTransformation() const
|
|||
Common::Quaternion::RotateX(m_imu_cursor_state.recentered_pitch)));
|
||||
}
|
||||
|
||||
std::string BalanceBoard::GetName() const
|
||||
{
|
||||
return "BalanceBoard";
|
||||
}
|
||||
|
||||
void BalanceBoard::LoadDefaults(const ControllerInterface& ciface)
|
||||
{
|
||||
EmulatedController::LoadDefaults(ciface);
|
||||
|
||||
// Power
|
||||
m_buttons->SetControlExpression(0, "P");
|
||||
|
||||
// Balance: Up, Down, Left, Right
|
||||
m_balance->SetControlExpression(0, "I");
|
||||
m_balance->SetControlExpression(1, "K");
|
||||
m_balance->SetControlExpression(2, "J");
|
||||
m_balance->SetControlExpression(3, "L");
|
||||
|
||||
// Step Off
|
||||
m_options->SetControlExpression(0, "O");
|
||||
|
||||
// Because our defaults use keyboard input, set calibration shape to a square.
|
||||
m_balance->SetCalibrationFromGate(ControllerEmu::SquareStickGate(1.0));
|
||||
}
|
||||
|
||||
void BalanceBoard::SubDeviceUpdate(const WiimoteEmu::DesiredWiimoteState& target_state)
|
||||
{
|
||||
m_ext.Update(target_state.extension);
|
||||
}
|
||||
|
||||
void BalanceBoard::Reset()
|
||||
{
|
||||
WiimoteBase::Reset();
|
||||
|
||||
m_extension_port.AttachExtension(&m_ext);
|
||||
m_ext.Reset();
|
||||
}
|
||||
|
||||
BalanceBoard::BalanceBoard(const u8 index) : WiimoteBase(index)
|
||||
{
|
||||
using Translatability = ControllerEmu::Translatability;
|
||||
|
||||
// Buttons
|
||||
groups.emplace_back(m_buttons = new ControllerEmu::Buttons(BUTTONS_GROUP_NAME));
|
||||
m_buttons->AddInput(Translatability::Translate, BUTTON_POWER_NAME);
|
||||
|
||||
// Balance
|
||||
groups.emplace_back(
|
||||
m_balance = new ControllerEmu::AnalogStick(
|
||||
_trans("Balance"), std::make_unique<ControllerEmu::SquareStickGate>(1.0)));
|
||||
|
||||
// Options
|
||||
groups.emplace_back(m_options = new ControllerEmu::ControlGroup(_trans("Options")));
|
||||
m_options->AddInput(Translatability::Translate, "Step Off");
|
||||
|
||||
m_options->AddSetting(&m_battery_setting,
|
||||
{_trans("Battery"),
|
||||
// i18n: The percent symbol.
|
||||
_trans("%")},
|
||||
95, 0, 100);
|
||||
|
||||
m_options->AddSetting(&m_weight_setting,
|
||||
{_trans("Weight"),
|
||||
// i18n: The abbreviation for kilograms.
|
||||
_trans("kg")},
|
||||
BalanceBoardExt::DEFAULT_WEIGHT, 0, 250);
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
ControllerEmu::ControlGroup* BalanceBoard::GetBalanceBoardGroup(BalanceBoardGroup group) const
|
||||
{
|
||||
switch (group)
|
||||
{
|
||||
case BalanceBoardGroup::Buttons:
|
||||
return m_buttons;
|
||||
case BalanceBoardGroup::Options:
|
||||
return m_options;
|
||||
case BalanceBoardGroup::Balance:
|
||||
return m_balance;
|
||||
default:
|
||||
ASSERT(false);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ButtonData BalanceBoard::GetCurrentlyPressedButtons()
|
||||
{
|
||||
const auto lock = GetStateLock();
|
||||
|
||||
ButtonData buttons{};
|
||||
m_buttons->GetState(&buttons.hex, bboard_bitmasks, m_input_override_function);
|
||||
|
||||
return buttons;
|
||||
}
|
||||
|
||||
// Update buttons in status struct from user input.
|
||||
void BalanceBoard::UpdateButtonsStatus(const DesiredWiimoteState& target_state)
|
||||
{
|
||||
m_status.buttons.hex = target_state.buttons.hex & BalanceBoard::BUTTON_POWER;
|
||||
}
|
||||
|
||||
void BalanceBoard::UpdateBatteryStatus(double charge)
|
||||
{
|
||||
// Balance Board battery values are higher than that of a Wii Remote.
|
||||
// This is a linear fit for the charge bars in the home menu.
|
||||
m_status.battery = u8(std::lround((charge + 3.53) / 8.52 * 0xff));
|
||||
|
||||
// TODO: Verify this behavior on real hardware.
|
||||
m_status.battery_low = m_status.battery <= 0x69;
|
||||
}
|
||||
|
||||
void BalanceBoard::BuildDesiredWiimoteState(DesiredWiimoteState* target_state,
|
||||
SensorBarState sensor_bar_state)
|
||||
{
|
||||
m_buttons->GetState(&target_state->buttons.hex, bboard_bitmasks, m_input_override_function);
|
||||
m_ext.BuildDesiredExtensionState(&target_state->extension);
|
||||
}
|
||||
|
||||
} // namespace WiimoteEmu
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "Core/HW/WiimoteEmu/Camera.h"
|
||||
#include "Core/HW/WiimoteEmu/Dynamics.h"
|
||||
#include "Core/HW/WiimoteEmu/Encryption.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/BalanceBoard.h"
|
||||
#include "Core/HW/WiimoteEmu/ExtensionPort.h"
|
||||
#include "Core/HW/WiimoteEmu/I2CBus.h"
|
||||
#include "Core/HW/WiimoteEmu/MotionPlus.h"
|
||||
|
@ -25,6 +26,7 @@ class PointerWrap;
|
|||
|
||||
namespace ControllerEmu
|
||||
{
|
||||
class AnalogStick;
|
||||
class Attachments;
|
||||
class Buttons;
|
||||
class ControlGroup;
|
||||
|
@ -38,6 +40,7 @@ class IRPassthrough;
|
|||
class ModifySettingsButton;
|
||||
class Output;
|
||||
class Tilt;
|
||||
class Triggers;
|
||||
} // namespace ControllerEmu
|
||||
|
||||
namespace WiimoteEmu
|
||||
|
@ -63,6 +66,13 @@ enum class WiimoteGroup
|
|||
IRPassthrough,
|
||||
};
|
||||
|
||||
enum class BalanceBoardGroup
|
||||
{
|
||||
Buttons,
|
||||
Balance,
|
||||
Options,
|
||||
};
|
||||
|
||||
enum class NunchukGroup;
|
||||
enum class ClassicGroup;
|
||||
enum class GuitarGroup;
|
||||
|
@ -94,129 +104,43 @@ void UpdateCalibrationDataChecksum(T& data, int cksum_bytes)
|
|||
}
|
||||
}
|
||||
|
||||
class Wiimote : public ControllerEmu::EmulatedController, public WiimoteCommon::HIDWiimote
|
||||
class WiimoteBase : public ControllerEmu::EmulatedController, public WiimoteCommon::HIDWiimote
|
||||
{
|
||||
public:
|
||||
static constexpr u16 IR_LOW_X = 0x7F;
|
||||
static constexpr u16 IR_LOW_Y = 0x5D;
|
||||
static constexpr u16 IR_HIGH_X = 0x380;
|
||||
static constexpr u16 IR_HIGH_Y = 0x2A2;
|
||||
|
||||
static constexpr u8 ACCEL_ZERO_G = 0x80;
|
||||
static constexpr u8 ACCEL_ONE_G = 0x9A;
|
||||
|
||||
static constexpr u16 PAD_LEFT = 0x01;
|
||||
static constexpr u16 PAD_RIGHT = 0x02;
|
||||
static constexpr u16 PAD_DOWN = 0x04;
|
||||
static constexpr u16 PAD_UP = 0x08;
|
||||
static constexpr u16 BUTTON_PLUS = 0x10;
|
||||
|
||||
static constexpr u16 BUTTON_TWO = 0x0100;
|
||||
static constexpr u16 BUTTON_ONE = 0x0200;
|
||||
static constexpr u16 BUTTON_B = 0x0400;
|
||||
static constexpr u16 BUTTON_A = 0x0800;
|
||||
static constexpr u16 BUTTON_MINUS = 0x1000;
|
||||
static constexpr u16 BUTTON_HOME = 0x8000;
|
||||
|
||||
static constexpr const char* BUTTONS_GROUP = _trans("Buttons");
|
||||
static constexpr const char* DPAD_GROUP = _trans("D-Pad");
|
||||
static constexpr const char* ACCELEROMETER_GROUP = "IMUAccelerometer";
|
||||
static constexpr const char* GYROSCOPE_GROUP = "IMUGyroscope";
|
||||
static constexpr const char* IR_GROUP = "IR";
|
||||
static constexpr const char* IR_PASSTHROUGH_GROUP = "IRPassthrough";
|
||||
|
||||
static constexpr const char* A_BUTTON = "A";
|
||||
static constexpr const char* B_BUTTON = "B";
|
||||
static constexpr const char* ONE_BUTTON = "1";
|
||||
static constexpr const char* TWO_BUTTON = "2";
|
||||
static constexpr const char* MINUS_BUTTON = "-";
|
||||
static constexpr const char* PLUS_BUTTON = "+";
|
||||
static constexpr const char* HOME_BUTTON = "Home";
|
||||
|
||||
static constexpr const char* UPRIGHT_OPTION = "Upright Wiimote";
|
||||
static constexpr const char* SIDEWAYS_OPTION = "Sideways Wiimote";
|
||||
|
||||
explicit Wiimote(unsigned int index);
|
||||
~Wiimote();
|
||||
|
||||
std::string GetName() const override;
|
||||
explicit WiimoteBase(u8 index);
|
||||
|
||||
InputConfig* GetConfig() const override;
|
||||
|
||||
void LoadDefaults(const ControllerInterface& ciface) override;
|
||||
|
||||
ControllerEmu::ControlGroup* GetWiimoteGroup(WiimoteGroup group) const;
|
||||
ControllerEmu::ControlGroup* GetNunchukGroup(NunchukGroup group) const;
|
||||
ControllerEmu::ControlGroup* GetClassicGroup(ClassicGroup group) const;
|
||||
ControllerEmu::ControlGroup* GetGuitarGroup(GuitarGroup group) const;
|
||||
ControllerEmu::ControlGroup* GetDrumsGroup(DrumsGroup group) const;
|
||||
ControllerEmu::ControlGroup* GetTurntableGroup(TurntableGroup group) const;
|
||||
ControllerEmu::ControlGroup* GetUDrawTabletGroup(UDrawTabletGroup group) const;
|
||||
ControllerEmu::ControlGroup* GetDrawsomeTabletGroup(DrawsomeTabletGroup group) const;
|
||||
ControllerEmu::ControlGroup* GetTaTaConGroup(TaTaConGroup group) const;
|
||||
ControllerEmu::ControlGroup* GetShinkansenGroup(ShinkansenGroup group) const;
|
||||
|
||||
u8 GetWiimoteDeviceIndex() const override;
|
||||
void SetWiimoteDeviceIndex(u8 index) override;
|
||||
|
||||
void PrepareInput(WiimoteEmu::DesiredWiimoteState* target_state,
|
||||
SensorBarState sensor_bar_state) override;
|
||||
void Update(const WiimoteEmu::DesiredWiimoteState& target_state) override;
|
||||
void EventLinked() override;
|
||||
void EventUnlinked() override;
|
||||
void InterruptDataOutput(const u8* data, u32 size) override;
|
||||
WiimoteCommon::ButtonData GetCurrentlyPressedButtons() override;
|
||||
|
||||
void Reset();
|
||||
virtual void Reset();
|
||||
virtual void LoadDefaultEeprom() = 0;
|
||||
|
||||
void DoState(PointerWrap& p);
|
||||
virtual void DoState(PointerWrap& p);
|
||||
|
||||
virtual Extension* GetActiveExtension() = 0;
|
||||
// Active extension number is exposed for TAS.
|
||||
ExtensionNumber GetActiveExtensionNumber() const;
|
||||
ControllerEmu::SubscribableSettingValue<bool>& GetMotionPlusSetting();
|
||||
virtual ExtensionNumber GetActiveExtensionNumber() const = 0;
|
||||
|
||||
static Common::Vec3
|
||||
OverrideVec3(const ControllerEmu::ControlGroup* control_group, Common::Vec3 vec,
|
||||
const ControllerEmu::InputOverrideFunction& input_override_function);
|
||||
|
||||
private:
|
||||
// Used only for error generation:
|
||||
static constexpr u8 EEPROM_I2C_ADDR = 0x50;
|
||||
|
||||
// static constexpr int EEPROM_SIZE = 16 * 1024;
|
||||
// This is the region exposed over bluetooth:
|
||||
protected:
|
||||
// This is the region exposed over bluetooth, total size is 0x4000.
|
||||
static constexpr int EEPROM_FREE_SIZE = 0x1700;
|
||||
|
||||
void RefreshConfig();
|
||||
virtual void SubDeviceUpdate(const WiimoteEmu::DesiredWiimoteState& target_state) = 0;
|
||||
virtual void UpdateButtonsStatus(const DesiredWiimoteState& target_state) = 0;
|
||||
virtual void UpdateBatteryStatus(double charge) = 0;
|
||||
virtual void BuildDesiredWiimoteState(DesiredWiimoteState* target_state,
|
||||
SensorBarState sensor_bar_state) = 0;
|
||||
void PrepareInput(WiimoteEmu::DesiredWiimoteState* target_state,
|
||||
SensorBarState sensor_bar_state) override;
|
||||
|
||||
void StepDynamics();
|
||||
void UpdateButtonsStatus(const DesiredWiimoteState& target_state);
|
||||
void BuildDesiredWiimoteState(DesiredWiimoteState* target_state, SensorBarState sensor_bar_state);
|
||||
|
||||
// Returns simulated accelerometer data in m/s^2.
|
||||
Common::Vec3 GetAcceleration(Common::Vec3 extra_acceleration) const;
|
||||
|
||||
// Returns simulated gyroscope data in radians/s.
|
||||
Common::Vec3 GetAngularVelocity(Common::Vec3 extra_angular_velocity) const;
|
||||
|
||||
// Returns the transformation of the world around the wiimote.
|
||||
// Used for simulating camera data and for rotating acceleration data.
|
||||
// Does not include orientation transformations.
|
||||
Common::Matrix44
|
||||
GetTransformation(const Common::Matrix33& extra_rotation = Common::Matrix33::Identity()) const;
|
||||
|
||||
// Returns the world rotation from the effects of sideways/upright settings.
|
||||
Common::Quaternion GetOrientation() const;
|
||||
|
||||
std::optional<Common::Vec3> OverrideVec3(const ControllerEmu::ControlGroup* control_group,
|
||||
std::optional<Common::Vec3> optional_vec) const;
|
||||
Common::Vec3 OverrideVec3(const ControllerEmu::ControlGroup* control_group,
|
||||
Common::Vec3 vec) const;
|
||||
Common::Vec3 GetTotalAcceleration() const;
|
||||
Common::Vec3 GetTotalAngularVelocity() const;
|
||||
Common::Matrix44 GetTotalTransformation() const;
|
||||
|
||||
void HandleReportRumble(const WiimoteCommon::OutputReportRumble&);
|
||||
virtual void HandleReportRumble(const WiimoteCommon::OutputReportRumble&) = 0;
|
||||
void HandleReportLeds(const WiimoteCommon::OutputReportLeds&);
|
||||
void HandleReportMode(const WiimoteCommon::OutputReportMode&);
|
||||
void HandleRequestStatus(const WiimoteCommon::OutputReportRequestStatus&);
|
||||
|
@ -224,30 +148,28 @@ private:
|
|||
void HandleWriteData(const WiimoteCommon::OutputReportWriteData&);
|
||||
void HandleIRLogicEnable(const WiimoteCommon::OutputReportEnableFeature&);
|
||||
void HandleIRLogicEnable2(const WiimoteCommon::OutputReportEnableFeature&);
|
||||
void HandleSpeakerMute(const WiimoteCommon::OutputReportEnableFeature&);
|
||||
virtual void HandleSpeakerMute(const WiimoteCommon::OutputReportEnableFeature&) = 0;
|
||||
void HandleSpeakerEnable(const WiimoteCommon::OutputReportEnableFeature&);
|
||||
void HandleSpeakerData(const WiimoteCommon::OutputReportSpeakerData&);
|
||||
virtual void HandleSpeakerData(const WiimoteCommon::OutputReportSpeakerData&) = 0;
|
||||
|
||||
template <typename T, typename H>
|
||||
void InvokeHandler(H&& handler, const WiimoteCommon::OutputReportGeneric& rpt, u32 size);
|
||||
|
||||
void HandleExtensionSwap(ExtensionNumber desired_extension_number, bool desired_motion_plus);
|
||||
bool ProcessExtensionPortEvent();
|
||||
void SendDataReport(const DesiredWiimoteState& target_state);
|
||||
bool ProcessReadDataRequest();
|
||||
|
||||
void SetRumble(bool on);
|
||||
|
||||
void SendAck(WiimoteCommon::OutputReportID rpt_id, WiimoteCommon::ErrorCode err);
|
||||
|
||||
bool IsSideways() const;
|
||||
bool IsUpright() const;
|
||||
|
||||
Extension* GetActiveExtension() const;
|
||||
Extension* GetNoneExtension() const;
|
||||
private:
|
||||
enum class UpdateProgress
|
||||
{
|
||||
Continue,
|
||||
DoNotContinue,
|
||||
};
|
||||
UpdateProgress ProcessEvents();
|
||||
UpdateProgress ProcessExtensionPortEvent();
|
||||
UpdateProgress ProcessReadDataRequest();
|
||||
void SendDataReport(const DesiredWiimoteState& target_state);
|
||||
|
||||
// TODO: Kill this nonsensical function used for TAS:
|
||||
EncryptionKey GetExtensionEncryptionKey() const;
|
||||
EncryptionKey GetExtensionEncryptionKey();
|
||||
|
||||
struct ReadRequest
|
||||
{
|
||||
|
@ -289,6 +211,153 @@ private:
|
|||
|
||||
static_assert(EEPROM_FREE_SIZE == sizeof(UsableEEPROMData));
|
||||
|
||||
protected:
|
||||
I2CBus m_i2c_bus;
|
||||
|
||||
ExtensionPort m_extension_port{&m_i2c_bus};
|
||||
|
||||
// Wiimote index, 0-3.
|
||||
// Can also be 4 for Balance Board.
|
||||
// This is used to look up the user button config.
|
||||
const u8 m_index;
|
||||
|
||||
// The Bluetooth 'slot' this device is connected to.
|
||||
// This is usually the same as m_index, but can differ during Netplay.
|
||||
u8 m_bt_device_index;
|
||||
|
||||
ControllerEmu::SettingValue<double> m_battery_setting;
|
||||
|
||||
private:
|
||||
WiimoteCommon::InputReportID m_reporting_mode = WiimoteCommon::InputReportID::ReportDisabled;
|
||||
bool m_reporting_continuous = false;
|
||||
|
||||
protected:
|
||||
WiimoteCommon::InputReportStatus m_status;
|
||||
|
||||
private:
|
||||
bool m_eeprom_dirty = false;
|
||||
ReadRequest m_read_request;
|
||||
|
||||
protected:
|
||||
UsableEEPROMData m_eeprom;
|
||||
};
|
||||
|
||||
class Wiimote final : public WiimoteBase
|
||||
{
|
||||
public:
|
||||
static constexpr u8 ACCEL_ZERO_G = 0x80;
|
||||
static constexpr u8 ACCEL_ONE_G = 0x9A;
|
||||
|
||||
static constexpr u16 PAD_LEFT = 0x01;
|
||||
static constexpr u16 PAD_RIGHT = 0x02;
|
||||
static constexpr u16 PAD_DOWN = 0x04;
|
||||
static constexpr u16 PAD_UP = 0x08;
|
||||
static constexpr u16 BUTTON_PLUS = 0x10;
|
||||
|
||||
static constexpr u16 BUTTON_TWO = 0x0100;
|
||||
static constexpr u16 BUTTON_ONE = 0x0200;
|
||||
static constexpr u16 BUTTON_B = 0x0400;
|
||||
static constexpr u16 BUTTON_A = 0x0800;
|
||||
static constexpr u16 BUTTON_MINUS = 0x1000;
|
||||
static constexpr u16 BUTTON_HOME = 0x8000;
|
||||
|
||||
static constexpr const char* BUTTONS_GROUP = _trans("Buttons");
|
||||
static constexpr const char* DPAD_GROUP = _trans("D-Pad");
|
||||
static constexpr const char* ACCELEROMETER_GROUP = "IMUAccelerometer";
|
||||
static constexpr const char* GYROSCOPE_GROUP = "IMUGyroscope";
|
||||
static constexpr const char* IR_GROUP = "IR";
|
||||
static constexpr const char* IR_PASSTHROUGH_GROUP = "IRPassthrough";
|
||||
|
||||
static constexpr const char* A_BUTTON = "A";
|
||||
static constexpr const char* B_BUTTON = "B";
|
||||
static constexpr const char* ONE_BUTTON = "1";
|
||||
static constexpr const char* TWO_BUTTON = "2";
|
||||
static constexpr const char* MINUS_BUTTON = "-";
|
||||
static constexpr const char* PLUS_BUTTON = "+";
|
||||
static constexpr const char* HOME_BUTTON = "Home";
|
||||
|
||||
static constexpr const char* UPRIGHT_OPTION = "Upright Wiimote";
|
||||
static constexpr const char* SIDEWAYS_OPTION = "Sideways Wiimote";
|
||||
|
||||
explicit Wiimote(u8 index);
|
||||
~Wiimote();
|
||||
|
||||
std::string GetName() const override;
|
||||
void LoadDefaults(const ControllerInterface& ciface) override;
|
||||
|
||||
ControllerEmu::ControlGroup* GetWiimoteGroup(WiimoteGroup group) const;
|
||||
ControllerEmu::ControlGroup* GetNunchukGroup(NunchukGroup group) const;
|
||||
ControllerEmu::ControlGroup* GetClassicGroup(ClassicGroup group) const;
|
||||
ControllerEmu::ControlGroup* GetGuitarGroup(GuitarGroup group) const;
|
||||
ControllerEmu::ControlGroup* GetDrumsGroup(DrumsGroup group) const;
|
||||
ControllerEmu::ControlGroup* GetTurntableGroup(TurntableGroup group) const;
|
||||
ControllerEmu::ControlGroup* GetUDrawTabletGroup(UDrawTabletGroup group) const;
|
||||
ControllerEmu::ControlGroup* GetDrawsomeTabletGroup(DrawsomeTabletGroup group) const;
|
||||
ControllerEmu::ControlGroup* GetTaTaConGroup(TaTaConGroup group) const;
|
||||
ControllerEmu::ControlGroup* GetShinkansenGroup(ShinkansenGroup group) const;
|
||||
|
||||
void SubDeviceUpdate(const WiimoteEmu::DesiredWiimoteState& target_state) override;
|
||||
WiimoteCommon::ButtonData GetCurrentlyPressedButtons() override;
|
||||
|
||||
void Reset() override;
|
||||
void LoadDefaultEeprom() override;
|
||||
|
||||
void DoState(PointerWrap& p) override;
|
||||
|
||||
Extension* GetActiveExtension() override;
|
||||
Extension* GetNoneExtension() const;
|
||||
ExtensionNumber GetActiveExtensionNumber() const override;
|
||||
ControllerEmu::SubscribableSettingValue<int>& GetAttachmentSetting();
|
||||
ControllerEmu::SubscribableSettingValue<bool>& GetMotionPlusSetting();
|
||||
|
||||
static Common::Vec3
|
||||
OverrideVec3(const ControllerEmu::ControlGroup* control_group, Common::Vec3 vec,
|
||||
const ControllerEmu::InputOverrideFunction& input_override_function);
|
||||
|
||||
protected:
|
||||
void HandleReportRumble(const WiimoteCommon::OutputReportRumble&) override;
|
||||
void HandleSpeakerMute(const WiimoteCommon::OutputReportEnableFeature&) override;
|
||||
void HandleSpeakerData(const WiimoteCommon::OutputReportSpeakerData&) override;
|
||||
|
||||
void UpdateButtonsStatus(const DesiredWiimoteState& target_state) override;
|
||||
void UpdateBatteryStatus(double charge) override;
|
||||
void BuildDesiredWiimoteState(DesiredWiimoteState* target_state,
|
||||
SensorBarState sensor_bar_state) override;
|
||||
|
||||
std::optional<Common::Vec3> OverrideVec3(const ControllerEmu::ControlGroup* control_group,
|
||||
std::optional<Common::Vec3> optional_vec) const;
|
||||
Common::Vec3 OverrideVec3(const ControllerEmu::ControlGroup* control_group,
|
||||
Common::Vec3 vec) const;
|
||||
Common::Vec3 GetTotalAcceleration() const;
|
||||
Common::Vec3 GetTotalAngularVelocity() const;
|
||||
|
||||
private:
|
||||
void RefreshConfig();
|
||||
void StepDynamics();
|
||||
void HandleExtensionSwap(ExtensionNumber desired_extension_number, bool desired_motion_plus);
|
||||
|
||||
// Returns simulated accelerometer data in m/s^2.
|
||||
Common::Vec3 GetAcceleration(Common::Vec3 extra_acceleration) const;
|
||||
|
||||
// Returns simulated gyroscope data in radians/s.
|
||||
Common::Vec3 GetAngularVelocity(Common::Vec3 extra_angular_velocity) const;
|
||||
|
||||
// Returns the transformation of the world around the wiimote.
|
||||
// Used for simulating camera data and for rotating acceleration data.
|
||||
// Does not include orientation transformations.
|
||||
Common::Matrix44
|
||||
GetTransformation(const Common::Matrix33& extra_rotation = Common::Matrix33::Identity()) const;
|
||||
|
||||
// Returns the world rotation from the effects of sideways/upright settings.
|
||||
Common::Quaternion GetOrientation() const;
|
||||
|
||||
Common::Matrix44 GetTotalTransformation() const;
|
||||
|
||||
void SetRumble(bool on);
|
||||
|
||||
bool IsSideways() const;
|
||||
bool IsUpright() const;
|
||||
|
||||
// Control groups for user input:
|
||||
ControllerEmu::Buttons* m_buttons;
|
||||
ControllerEmu::Buttons* m_dpad;
|
||||
|
@ -307,42 +376,18 @@ private:
|
|||
|
||||
ControllerEmu::SettingValue<bool> m_sideways_setting;
|
||||
ControllerEmu::SettingValue<bool> m_upright_setting;
|
||||
ControllerEmu::SettingValue<double> m_battery_setting;
|
||||
ControllerEmu::SubscribableSettingValue<bool> m_motion_plus_setting;
|
||||
ControllerEmu::SettingValue<double> m_fov_x_setting;
|
||||
ControllerEmu::SettingValue<double> m_fov_y_setting;
|
||||
|
||||
SpeakerLogic m_speaker_logic;
|
||||
MotionPlus m_motion_plus;
|
||||
CameraLogic m_camera_logic;
|
||||
|
||||
I2CBus m_i2c_bus;
|
||||
|
||||
ExtensionPort m_extension_port{&m_i2c_bus};
|
||||
|
||||
// Wiimote index, 0-3.
|
||||
// Can also be 4 for Balance Board.
|
||||
// This is used to look up the user button config.
|
||||
const u8 m_index;
|
||||
|
||||
// The Bluetooth 'slot' this device is connected to.
|
||||
// This is usually the same as m_index, but can differ during Netplay.
|
||||
u8 m_bt_device_index;
|
||||
|
||||
WiimoteCommon::InputReportID m_reporting_mode;
|
||||
bool m_reporting_continuous;
|
||||
CameraLogic m_camera_logic{&m_status};
|
||||
|
||||
bool m_speaker_mute;
|
||||
|
||||
WiimoteCommon::InputReportStatus m_status;
|
||||
|
||||
ExtensionNumber m_active_extension;
|
||||
|
||||
bool m_is_motion_plus_attached;
|
||||
|
||||
bool m_eeprom_dirty = false;
|
||||
ReadRequest m_read_request;
|
||||
UsableEEPROMData m_eeprom;
|
||||
ExtensionNumber m_active_extension = NONE;
|
||||
MotionPlus m_motion_plus;
|
||||
bool m_is_motion_plus_attached = false;
|
||||
|
||||
// Dynamics:
|
||||
MotionState m_swing_state;
|
||||
|
@ -354,4 +399,51 @@ private:
|
|||
|
||||
Config::ConfigChangedCallbackID m_config_changed_callback_id;
|
||||
};
|
||||
|
||||
class BalanceBoard final : public WiimoteBase
|
||||
{
|
||||
friend class BalanceBoardExt;
|
||||
|
||||
public:
|
||||
static constexpr const char* BUTTONS_GROUP_NAME = _trans("Buttons");
|
||||
static constexpr u16 BUTTON_POWER = Wiimote::BUTTON_A;
|
||||
static constexpr const char* BUTTON_POWER_NAME = "Power";
|
||||
|
||||
explicit BalanceBoard(u8 index);
|
||||
|
||||
ControllerEmu::ControlGroup* GetBalanceBoardGroup(BalanceBoardGroup group) const;
|
||||
|
||||
std::string GetName() const override;
|
||||
void LoadDefaults(const ControllerInterface& ciface) override;
|
||||
|
||||
void SubDeviceUpdate(const WiimoteEmu::DesiredWiimoteState& target_state) override;
|
||||
WiimoteCommon::ButtonData GetCurrentlyPressedButtons() override;
|
||||
|
||||
void Reset() override;
|
||||
void LoadDefaultEeprom() override;
|
||||
|
||||
void DoState(PointerWrap& p) override;
|
||||
|
||||
Extension* GetActiveExtension() override;
|
||||
ExtensionNumber GetActiveExtensionNumber() const override;
|
||||
|
||||
protected:
|
||||
void UpdateButtonsStatus(const DesiredWiimoteState& target_state) override;
|
||||
void UpdateBatteryStatus(double charge) override;
|
||||
void BuildDesiredWiimoteState(DesiredWiimoteState* target_state,
|
||||
SensorBarState sensor_bar_state) override;
|
||||
|
||||
void HandleReportRumble(const WiimoteCommon::OutputReportRumble&) override;
|
||||
void HandleSpeakerMute(const WiimoteCommon::OutputReportEnableFeature&) override;
|
||||
void HandleSpeakerData(const WiimoteCommon::OutputReportSpeakerData&) override;
|
||||
|
||||
private:
|
||||
BalanceBoardExt m_ext{this};
|
||||
|
||||
ControllerEmu::Buttons* m_buttons;
|
||||
ControllerEmu::AnalogStick* m_balance;
|
||||
ControllerEmu::ControlGroup* m_options;
|
||||
|
||||
ControllerEmu::SettingValue<double> m_weight_setting;
|
||||
};
|
||||
} // namespace WiimoteEmu
|
||||
|
|
|
@ -4,9 +4,7 @@
|
|||
#include "Core/IOS/USB/Bluetooth/WiimoteDevice.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
|
@ -15,7 +13,6 @@
|
|||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/MsgHandler.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Common/Swap.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/HW/WII_IPC.h"
|
||||
|
@ -62,7 +59,7 @@ WiimoteDevice::WiimoteDevice(BluetoothEmuDevice* host, bdaddr_t bd, unsigned int
|
|||
m_name(GetNumber() == WIIMOTE_BALANCE_BOARD ? "Nintendo RVL-WBC-01" : "Nintendo RVL-CNT-01")
|
||||
|
||||
{
|
||||
INFO_LOG_FMT(IOS_WIIMOTE, "Wiimote: #{} Constructed", GetNumber());
|
||||
INFO_LOG_FMT(IOS_WIIMOTE, "{} Constructed", GetDisplayName());
|
||||
|
||||
m_link_key.fill(0xa0 + GetNumber());
|
||||
m_class = {0x00, 0x04, 0x48};
|
||||
|
@ -224,8 +221,7 @@ void WiimoteDevice::Activate(bool connect)
|
|||
{
|
||||
SetBasebandState(BasebandState::RequestConnection);
|
||||
|
||||
Core::DisplayMessage(fmt::format("Wii Remote {} connected", GetNumber() + 1),
|
||||
CONNECTION_MESSAGE_TIME);
|
||||
Core::DisplayMessage(fmt::format("{} connected", GetDisplayName()), CONNECTION_MESSAGE_TIME);
|
||||
}
|
||||
else if (!connect && IsConnected())
|
||||
{
|
||||
|
@ -235,8 +231,7 @@ void WiimoteDevice::Activate(bool connect)
|
|||
// Not doing that doesn't seem to break anything.
|
||||
m_host->RemoteDisconnect(GetBD());
|
||||
|
||||
Core::DisplayMessage(fmt::format("Wii Remote {} disconnected", GetNumber() + 1),
|
||||
CONNECTION_MESSAGE_TIME);
|
||||
Core::DisplayMessage(fmt::format("{} disconnected", GetDisplayName()), CONNECTION_MESSAGE_TIME);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -275,7 +270,7 @@ void WiimoteDevice::EventDisconnect(u8 reason)
|
|||
// FYI: It looks like reason is always 0x13 (User Ended Connection).
|
||||
|
||||
Core::DisplayMessage(
|
||||
fmt::format("Wii Remote {} disconnected by emulated software", GetNumber() + 1),
|
||||
fmt::format("{} disconnected by emulated software (0x{:02x})", GetDisplayName(), reason),
|
||||
CONNECTION_MESSAGE_TIME);
|
||||
|
||||
Reset();
|
||||
|
@ -1006,4 +1001,12 @@ void WiimoteDevice::InterruptDataInputCallback(u8 hid_type, const u8* data, u32
|
|||
|
||||
m_host->SendACLPacket(GetBD(), reinterpret_cast<const u8*>(&data_frame), data_frame_size);
|
||||
}
|
||||
|
||||
std::string WiimoteDevice::GetDisplayName()
|
||||
{
|
||||
if (GetNumber() != WIIMOTE_BALANCE_BOARD)
|
||||
return fmt::format("Wii Remote {}", GetNumber() + 1);
|
||||
else
|
||||
return "Balance Board";
|
||||
}
|
||||
} // namespace IOS::HLE
|
||||
|
|
|
@ -170,5 +170,7 @@ private:
|
|||
void SDPSendServiceAttributeResponse(u16 cid, u16 transaction_id, u32 service_handle,
|
||||
u16 start_attr_id, u16 end_attr_id,
|
||||
u16 maximum_attribute_byte_count, u8* continuation_state);
|
||||
|
||||
std::string GetDisplayName();
|
||||
};
|
||||
} // namespace IOS::HLE
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include <mbedtls/config.h>
|
||||
#include <mbedtls/md.h>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
@ -38,7 +37,6 @@
|
|||
#include "Core/Boot/Boot.h"
|
||||
#include "Core/Config/AchievementSettings.h"
|
||||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/Config/SYSCONFSettings.h"
|
||||
#include "Core/Config/WiimoteSettings.h"
|
||||
#include "Core/ConfigLoaders/MovieConfigLoader.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
|
@ -58,6 +56,7 @@
|
|||
#include "Core/HW/WiimoteCommon/WiimoteReport.h"
|
||||
|
||||
#include "Core/HW/WiimoteEmu/Encryption.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/BalanceBoard.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/Classic.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/Nunchuk.h"
|
||||
#include "Core/HW/WiimoteEmu/ExtensionPort.h"
|
||||
|
@ -69,8 +68,6 @@
|
|||
#include "Core/System.h"
|
||||
#include "Core/WiiUtils.h"
|
||||
|
||||
#include "DiscIO/Enums.h"
|
||||
|
||||
#include "InputCommon/GCPadStatus.h"
|
||||
|
||||
#include "VideoCommon/VideoBackendBase.h"
|
||||
|
@ -130,10 +127,8 @@ std::string MovieManager::GetInputDisplay()
|
|||
if (!IsMovieActive())
|
||||
{
|
||||
m_controllers = {};
|
||||
m_wiimotes = {};
|
||||
|
||||
const auto& si = m_system.GetSerialInterface();
|
||||
for (int i = 0; i < 4; ++i)
|
||||
for (int i = 0; i < SerialInterface::MAX_SI_CHANNELS; ++i)
|
||||
{
|
||||
if (si.GetDeviceType(i) == SerialInterface::SIDEVICE_GC_GBA_EMULATED)
|
||||
m_controllers[i] = ControllerType::GBA;
|
||||
|
@ -141,6 +136,10 @@ std::string MovieManager::GetInputDisplay()
|
|||
m_controllers[i] = ControllerType::GC;
|
||||
else
|
||||
m_controllers[i] = ControllerType::None;
|
||||
}
|
||||
m_wiimotes = {};
|
||||
for (int i = 0; i < MAX_BBMOTES; ++i)
|
||||
{
|
||||
m_wiimotes[i] = Config::Get(Config::GetInfoForWiimoteSource(i)) != WiimoteSource::None;
|
||||
}
|
||||
}
|
||||
|
@ -148,15 +147,15 @@ std::string MovieManager::GetInputDisplay()
|
|||
std::string input_display;
|
||||
{
|
||||
std::lock_guard guard(m_input_display_lock);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
for (int i = 0; i < SerialInterface::MAX_SI_CHANNELS; ++i)
|
||||
{
|
||||
if (IsUsingPad(i))
|
||||
input_display += m_input_display[i] + '\n';
|
||||
}
|
||||
for (int i = 0; i < 4; ++i)
|
||||
for (int i = 0; i < MAX_BBMOTES; ++i)
|
||||
{
|
||||
if (IsUsingWiimote(i))
|
||||
input_display += m_input_display[i + 4] + '\n';
|
||||
input_display += m_input_display[i + SerialInterface::MAX_SI_CHANNELS] + '\n';
|
||||
}
|
||||
}
|
||||
return input_display;
|
||||
|
@ -475,7 +474,7 @@ void MovieManager::ChangeWiiPads(bool instantly)
|
|||
{
|
||||
WiimoteEnabledArray wiimotes{};
|
||||
|
||||
for (int i = 0; i < MAX_WIIMOTES; ++i)
|
||||
for (int i = 0; i < MAX_BBMOTES; ++i)
|
||||
{
|
||||
wiimotes[i] = Config::Get(Config::GetInfoForWiimoteSource(i)) != WiimoteSource::None;
|
||||
}
|
||||
|
@ -485,7 +484,7 @@ void MovieManager::ChangeWiiPads(bool instantly)
|
|||
return;
|
||||
|
||||
const auto bt = WiiUtils::GetBluetoothEmuDevice();
|
||||
for (int i = 0; i < MAX_WIIMOTES; ++i)
|
||||
for (int i = 0; i < MAX_BBMOTES; ++i)
|
||||
{
|
||||
const bool is_using_wiimote = IsUsingWiimote(i);
|
||||
|
||||
|
@ -676,7 +675,8 @@ static std::string GenerateInputDisplayString(ControllerState padState, int cont
|
|||
static std::string GenerateWiiInputDisplayString(int remoteID, const DataReportBuilder& rpt,
|
||||
ExtensionNumber ext, const EncryptionKey& key)
|
||||
{
|
||||
std::string display_str = fmt::format("R{}:", remoteID + 1);
|
||||
std::string display_str =
|
||||
(remoteID == WIIMOTE_BALANCE_BOARD ? "BB:" : fmt::format("R{}:", remoteID + 1));
|
||||
|
||||
if (rpt.HasCore())
|
||||
{
|
||||
|
@ -797,6 +797,24 @@ static std::string GenerateWiiInputDisplayString(int remoteID, const DataReportB
|
|||
display_str += Analog2DToString(right_stick.x, right_stick.y, " R-ANA", 31);
|
||||
}
|
||||
|
||||
// Balance board
|
||||
if (rpt.HasExt() && ext == ExtensionNumber::BALANCE_BOARD)
|
||||
{
|
||||
const u8* const extData = rpt.GetExtDataPtr();
|
||||
|
||||
BalanceBoardExt::DataFormat bb;
|
||||
memcpy(&bb, extData, sizeof(bb));
|
||||
key.Decrypt((u8*)&bb, 0, sizeof(bb));
|
||||
|
||||
const double tr = BalanceBoardExt::ConvertToKilograms(Common::swap16(bb.sensor_tr));
|
||||
const double br = BalanceBoardExt::ConvertToKilograms(Common::swap16(bb.sensor_br));
|
||||
const double tl = BalanceBoardExt::ConvertToKilograms(Common::swap16(bb.sensor_tl));
|
||||
const double bl = BalanceBoardExt::ConvertToKilograms(Common::swap16(bb.sensor_bl));
|
||||
|
||||
display_str +=
|
||||
fmt::format(" TR:{:5.2f}kg BR:{:5.2f}kg TL:{:5.2f}kg BL:{:5.2f}kg", tr, br, tl, bl);
|
||||
}
|
||||
|
||||
return display_str;
|
||||
}
|
||||
|
||||
|
@ -864,7 +882,7 @@ void MovieManager::CheckWiimoteStatus(int wiimote, const DataReportBuilder& rpt,
|
|||
std::string display_str = GenerateWiiInputDisplayString(wiimote, rpt, ext, key);
|
||||
|
||||
std::lock_guard guard(m_input_display_lock);
|
||||
m_input_display[wiimote + 4] = std::move(display_str);
|
||||
m_input_display[wiimote + SerialInterface::MAX_SI_CHANNELS] = std::move(display_str);
|
||||
}
|
||||
|
||||
if (IsRecordingInput())
|
||||
|
@ -886,7 +904,7 @@ void MovieManager::RecordWiimote(int wiimote, const u8* data, u8 size)
|
|||
// NOTE: EmuThread / Host Thread
|
||||
void MovieManager::ReadHeader()
|
||||
{
|
||||
for (int i = 0; i < 4; ++i)
|
||||
for (int i = 0; i < SerialInterface::MAX_SI_CHANNELS; ++i)
|
||||
{
|
||||
if (m_temp_header.GBAControllers & (1 << i))
|
||||
m_controllers[i] = ControllerType::GBA;
|
||||
|
@ -894,8 +912,13 @@ void MovieManager::ReadHeader()
|
|||
m_controllers[i] = ControllerType::GC;
|
||||
else
|
||||
m_controllers[i] = ControllerType::None;
|
||||
m_wiimotes[i] = (m_temp_header.controllers & (1 << (i + 4))) != 0;
|
||||
}
|
||||
for (int i = 0; i < MAX_WIIMOTES; i++)
|
||||
{
|
||||
m_wiimotes[i] =
|
||||
(m_temp_header.controllers & (1 << (i + SerialInterface::MAX_SI_CHANNELS))) != 0;
|
||||
}
|
||||
m_wiimotes[WIIMOTE_BALANCE_BOARD] = m_temp_header.bBalanceBoard;
|
||||
m_recording_start_time = m_temp_header.recordingStartTime;
|
||||
if (m_rerecords < m_temp_header.numRerecords)
|
||||
m_rerecords = m_temp_header.numRerecords;
|
||||
|
@ -1374,15 +1397,19 @@ void MovieManager::SaveRecording(const std::string& filename)
|
|||
header.bWii = m_system.IsWii();
|
||||
header.controllers = 0;
|
||||
header.GBAControllers = 0;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
for (int i = 0; i < SerialInterface::MAX_SI_CHANNELS; ++i)
|
||||
{
|
||||
if (IsUsingGBA(i))
|
||||
header.GBAControllers |= 1 << i;
|
||||
if (IsUsingPad(i))
|
||||
header.controllers |= 1 << i;
|
||||
if (IsUsingWiimote(i) && m_system.IsWii())
|
||||
header.controllers |= 1 << (i + 4);
|
||||
}
|
||||
for (int i = 0; i < MAX_WIIMOTES; i++)
|
||||
{
|
||||
if (IsUsingWiimote(i) && m_system.IsWii())
|
||||
header.controllers |= 1 << (i + SerialInterface::MAX_SI_CHANNELS);
|
||||
}
|
||||
header.bBalanceBoard = IsUsingWiimote(WIIMOTE_BALANCE_BOARD);
|
||||
|
||||
header.bFromSaveState = m_recording_from_save_state;
|
||||
header.frameCount = m_total_frames;
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#include <vector>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Core/HW/SI/SI.h"
|
||||
#include "Core/HW/Wiimote.h"
|
||||
|
||||
struct BootParameters;
|
||||
|
||||
|
@ -50,8 +52,8 @@ enum class ControllerType
|
|||
GC,
|
||||
GBA,
|
||||
};
|
||||
using ControllerTypeArray = std::array<ControllerType, 4>;
|
||||
using WiimoteEnabledArray = std::array<bool, 4>;
|
||||
using ControllerTypeArray = std::array<ControllerType, SerialInterface::MAX_SI_CHANNELS>;
|
||||
using WiimoteEnabledArray = std::array<bool, MAX_BBMOTES>;
|
||||
|
||||
// GameCube Controller State
|
||||
#pragma pack(push, 1)
|
||||
|
@ -133,7 +135,8 @@ struct DTMHeader
|
|||
u8 GBAControllers; // GBA Controllers plugged in (the bits are ports 1-4)
|
||||
bool bWidescreen; // true indicates SYSCONF aspect ratio is 16:9, false for 4:3
|
||||
u8 countryCode; // SYSCONF country code
|
||||
std::array<u8, 5> reserved; // Padding for any new config options
|
||||
bool bBalanceBoard;
|
||||
std::array<u8, 4> reserved; // Padding for any new config options
|
||||
std::array<char, 40> discChange; // Name of iso file to switch to, for two disc games.
|
||||
std::array<u8, 20> revision; // Git hash
|
||||
u32 DSPiromHash;
|
||||
|
@ -237,8 +240,8 @@ private:
|
|||
u32 m_rerecords = 0;
|
||||
PlayMode m_play_mode = PlayMode::None;
|
||||
|
||||
std::array<ControllerType, 4> m_controllers{};
|
||||
std::array<bool, 4> m_wiimotes{};
|
||||
ControllerTypeArray m_controllers{};
|
||||
WiimoteEnabledArray m_wiimotes{};
|
||||
ControllerState m_pad_state{};
|
||||
DTMHeader m_temp_header{};
|
||||
std::vector<u8> m_temp_input;
|
||||
|
@ -273,7 +276,8 @@ private:
|
|||
|
||||
// m_input_display is used by both CPU and GPU (is mutable).
|
||||
std::mutex m_input_display_lock;
|
||||
std::array<std::string, 8> m_input_display;
|
||||
std::array<std::string, static_cast<size_t>(SerialInterface::MAX_SI_CHANNELS) + MAX_BBMOTES>
|
||||
m_input_display;
|
||||
|
||||
Core::System& m_system;
|
||||
};
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
#include "Core/State.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <filesystem>
|
||||
#include <locale>
|
||||
|
@ -12,7 +11,6 @@
|
|||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
|
@ -31,12 +29,10 @@
|
|||
#include "Common/MsgHandler.h"
|
||||
#include "Common/Thread.h"
|
||||
#include "Common/TimeUtil.h"
|
||||
#include "Common/Timer.h"
|
||||
#include "Common/Version.h"
|
||||
#include "Common/WorkQueueThread.h"
|
||||
|
||||
#include "Core/AchievementManager.h"
|
||||
#include "Core/Config/AchievementSettings.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
|
@ -46,7 +42,7 @@
|
|||
#include "Core/HW/Wiimote.h"
|
||||
#include "Core/Host.h"
|
||||
#include "Core/Movie.h"
|
||||
#include "Core/NetPlayClient.h"
|
||||
#include "Core/NetPlayProto.h"
|
||||
#include "Core/PowerPC/PowerPC.h"
|
||||
#include "Core/System.h"
|
||||
|
||||
|
@ -99,7 +95,7 @@ static size_t s_state_writes_in_queue;
|
|||
static std::condition_variable s_state_write_queue_is_empty;
|
||||
|
||||
// Don't forget to increase this after doing changes on the savestate system
|
||||
constexpr u32 STATE_VERSION = 169; // Last changed in PR 13074
|
||||
constexpr u32 STATE_VERSION = 170; // Last changed in PR 13337
|
||||
|
||||
// Increase this if the StateExtendedHeader definition changes
|
||||
constexpr u32 EXTENDED_HEADER_VERSION = 1; // Last changed in PR 12217
|
||||
|
|
|
@ -337,6 +337,7 @@
|
|||
<ClInclude Include="Core\HW\WiimoteEmu\Dynamics.h" />
|
||||
<ClInclude Include="Core\HW\WiimoteEmu\DesiredWiimoteState.h" />
|
||||
<ClInclude Include="Core\HW\WiimoteEmu\Encryption.h" />
|
||||
<ClInclude Include="Core\HW\WiimoteEmu\Extension\BalanceBoard.h" />
|
||||
<ClInclude Include="Core\HW\WiimoteEmu\Extension\Classic.h" />
|
||||
<ClInclude Include="Core\HW\WiimoteEmu\Extension\DesiredExtensionState.h" />
|
||||
<ClInclude Include="Core\HW\WiimoteEmu\Extension\DrawsomeTablet.h" />
|
||||
|
@ -1001,6 +1002,7 @@
|
|||
<ClCompile Include="Core\HW\WiimoteEmu\Dynamics.cpp" />
|
||||
<ClCompile Include="Core\HW\WiimoteEmu\EmuSubroutines.cpp" />
|
||||
<ClCompile Include="Core\HW\WiimoteEmu\Encryption.cpp" />
|
||||
<ClCompile Include="Core\HW\WiimoteEmu\Extension\BalanceBoard.cpp" />
|
||||
<ClCompile Include="Core\HW\WiimoteEmu\Extension\Classic.cpp" />
|
||||
<ClCompile Include="Core\HW\WiimoteEmu\Extension\DrawsomeTablet.cpp" />
|
||||
<ClCompile Include="Core\HW\WiimoteEmu\Extension\Drums.cpp" />
|
||||
|
|
|
@ -113,6 +113,8 @@ add_executable(dolphin-emu
|
|||
Config/LogConfigWidget.h
|
||||
Config/LogWidget.cpp
|
||||
Config/LogWidget.h
|
||||
Config/Mapping/BalanceBoardGeneral.cpp
|
||||
Config/Mapping/BalanceBoardGeneral.h
|
||||
Config/Mapping/FreeLookGeneral.cpp
|
||||
Config/Mapping/FreeLookGeneral.h
|
||||
Config/Mapping/FreeLookRotation.cpp
|
||||
|
@ -364,6 +366,8 @@ add_executable(dolphin-emu
|
|||
SkylanderPortal/SkylanderModifyDialog.h
|
||||
SkylanderPortal/SkylanderPortalWindow.cpp
|
||||
SkylanderPortal/SkylanderPortalWindow.h
|
||||
TAS/BalanceBoardWidget.cpp
|
||||
TAS/BalanceBoardWidget.h
|
||||
TAS/GCTASInputWindow.cpp
|
||||
TAS/GCTASInputWindow.h
|
||||
TAS/GBATASInputWindow.cpp
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright 2025 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "DolphinQt/Config/Mapping/BalanceBoardGeneral.h"
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QFormLayout>
|
||||
#include <QGridLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
|
||||
#include "Core/HW/Wiimote.h"
|
||||
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
|
||||
|
||||
#include "DolphinQt/Config/Mapping/MappingWindow.h"
|
||||
|
||||
#include "InputCommon/InputConfig.h"
|
||||
|
||||
BalanceBoardGeneral::BalanceBoardGeneral(MappingWindow* window) : MappingWidget(window)
|
||||
{
|
||||
auto* layout = new QHBoxLayout;
|
||||
|
||||
auto& get_group = BalanceBoard::GetBalanceBoardGroup;
|
||||
using BBG = WiimoteEmu::BalanceBoardGroup;
|
||||
|
||||
layout->addWidget(CreateGroupBox(tr("Buttons"), get_group(GetPort(), BBG::Buttons)));
|
||||
layout->addWidget(CreateGroupBox(tr("Balance"), get_group(GetPort(), BBG::Balance)));
|
||||
layout->addWidget(CreateGroupBox(tr("Options"), get_group(GetPort(), BBG::Options)));
|
||||
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void BalanceBoardGeneral::LoadSettings()
|
||||
{
|
||||
BalanceBoard::LoadConfig();
|
||||
}
|
||||
|
||||
void BalanceBoardGeneral::SaveSettings()
|
||||
{
|
||||
BalanceBoard::GetConfig()->SaveConfig();
|
||||
}
|
||||
|
||||
InputConfig* BalanceBoardGeneral::GetConfig()
|
||||
{
|
||||
return BalanceBoard::GetConfig();
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright 2025 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DolphinQt/Config/Mapping/MappingWidget.h"
|
||||
|
||||
class BalanceBoardGeneral final : public MappingWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit BalanceBoardGeneral(MappingWindow* window);
|
||||
|
||||
InputConfig* GetConfig() override;
|
||||
void LoadSettings() override;
|
||||
void SaveSettings() override;
|
||||
};
|
|
@ -24,6 +24,7 @@
|
|||
#include "Common/IniFile.h"
|
||||
#include "Common/StringUtil.h"
|
||||
|
||||
#include "DolphinQt/Config/Mapping/BalanceBoardGeneral.h"
|
||||
#include "DolphinQt/Config/Mapping/FreeLookGeneral.h"
|
||||
#include "DolphinQt/Config/Mapping/FreeLookRotation.h"
|
||||
#include "DolphinQt/Config/Mapping/GBAPadEmu.h"
|
||||
|
@ -448,6 +449,13 @@ void MappingWindow::SetMappingType(MappingWindow::Type type)
|
|||
ShowExtensionMotionTabs(false);
|
||||
break;
|
||||
}
|
||||
case Type::MAPPING_BALANCE_BOARD_EMU:
|
||||
{
|
||||
widget = new BalanceBoardGeneral(this);
|
||||
setWindowTitle(tr("Balance Board"));
|
||||
AddWidget(tr("General and Options"), widget);
|
||||
break;
|
||||
}
|
||||
case Type::MAPPING_HOTKEYS:
|
||||
{
|
||||
widget = new HotkeyGeneral(this);
|
||||
|
|
|
@ -41,6 +41,7 @@ public:
|
|||
MAPPING_GC_MICROPHONE,
|
||||
// Wii
|
||||
MAPPING_WIIMOTE_EMU,
|
||||
MAPPING_BALANCE_BOARD_EMU,
|
||||
// Hotkeys
|
||||
MAPPING_HOTKEYS,
|
||||
// Freelook
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
#include <QScreen>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
|
||||
#include "Common/Config/Config.h"
|
||||
|
||||
#include "Core/Config/MainSettings.h"
|
||||
|
@ -26,7 +23,6 @@
|
|||
#include "Core/HW/Wiimote.h"
|
||||
#include "Core/HW/WiimoteReal/WiimoteReal.h"
|
||||
#include "Core/IOS/IOS.h"
|
||||
#include "Core/IOS/USB/Bluetooth/BTReal.h"
|
||||
#include "Core/NetPlayProto.h"
|
||||
#include "Core/System.h"
|
||||
#include "Core/WiiUtils.h"
|
||||
|
@ -38,8 +34,6 @@
|
|||
#include "DolphinQt/QtUtils/SignalBlocking.h"
|
||||
#include "DolphinQt/Settings.h"
|
||||
|
||||
#include "UICommon/UICommon.h"
|
||||
|
||||
WiimoteControllersWidget::WiimoteControllersWidget(QWidget* parent) : QWidget(parent)
|
||||
{
|
||||
CreateLayout();
|
||||
|
@ -111,7 +105,6 @@ void WiimoteControllersWidget::CreateLayout()
|
|||
m_wiimote_pt_labels[1] = new QLabel(tr("Reset all saved Wii Remote pairings"));
|
||||
m_wiimote_emu = new QRadioButton(tr("Emulate the Wii's Bluetooth adapter"));
|
||||
m_wiimote_continuous_scanning = new QCheckBox(tr("Continuous Scanning"));
|
||||
m_wiimote_real_balance_board = new QCheckBox(tr("Real Balance Board"));
|
||||
m_wiimote_speaker_data = new QCheckBox(tr("Enable Speaker Data"));
|
||||
m_wiimote_ciface = new QCheckBox(tr("Connect Wii Remotes for Emulated Controllers"));
|
||||
|
||||
|
@ -136,12 +129,22 @@ void WiimoteControllersWidget::CreateLayout()
|
|||
|
||||
for (size_t i = 0; i < m_wiimote_groups.size(); i++)
|
||||
{
|
||||
auto* wm_label = m_wiimote_labels[i] = new QLabel(tr("Wii Remote %1").arg(i + 1));
|
||||
auto text = (i == WIIMOTE_BALANCE_BOARD ? tr("Balance Board") : tr("Wii Remote %1").arg(i + 1));
|
||||
auto* wm_label = m_wiimote_labels[i] = new QLabel(text);
|
||||
auto* wm_box = m_wiimote_boxes[i] = new QComboBox();
|
||||
auto* wm_button = m_wiimote_buttons[i] = new NonDefaultQPushButton(tr("Configure"));
|
||||
|
||||
for (const auto& item : {tr("None"), tr("Emulated Wii Remote"), tr("Real Wii Remote")})
|
||||
wm_box->addItem(item);
|
||||
wm_box->addItem(tr("None"));
|
||||
if (i == WIIMOTE_BALANCE_BOARD)
|
||||
{
|
||||
wm_box->addItem(tr("Emulated Balance Board"));
|
||||
wm_box->addItem(tr("Real Balance Board"));
|
||||
}
|
||||
else
|
||||
{
|
||||
wm_box->addItem(tr("Emulated Wii Remote"));
|
||||
wm_box->addItem(tr("Real Wii Remote"));
|
||||
}
|
||||
|
||||
int wm_row = m_wiimote_layout->rowCount();
|
||||
m_wiimote_layout->addWidget(wm_label, wm_row, 1);
|
||||
|
@ -149,7 +152,6 @@ void WiimoteControllersWidget::CreateLayout()
|
|||
m_wiimote_layout->addWidget(wm_button, wm_row, 3);
|
||||
}
|
||||
|
||||
m_wiimote_layout->addWidget(m_wiimote_real_balance_board, m_wiimote_layout->rowCount(), 1, 1, -1);
|
||||
m_wiimote_layout->addWidget(m_wiimote_speaker_data, m_wiimote_layout->rowCount(), 1, 1, -1);
|
||||
|
||||
m_wiimote_layout->addWidget(m_wiimote_ciface, m_wiimote_layout->rowCount(), 0, 1, -1);
|
||||
|
@ -185,8 +187,6 @@ void WiimoteControllersWidget::ConnectWidgets()
|
|||
LoadSettings(Core::GetState(Core::System::GetInstance()));
|
||||
});
|
||||
|
||||
connect(m_wiimote_real_balance_board, &QCheckBox::toggled, this,
|
||||
&WiimoteControllersWidget::SaveSettings);
|
||||
connect(m_wiimote_speaker_data, &QCheckBox::toggled, this,
|
||||
&WiimoteControllersWidget::SaveSettings);
|
||||
connect(m_wiimote_sync, &QPushButton::clicked, this,
|
||||
|
@ -248,16 +248,15 @@ void WiimoteControllersWidget::OnWiimoteRefreshPressed()
|
|||
void WiimoteControllersWidget::OnWiimoteConfigure(size_t index)
|
||||
{
|
||||
MappingWindow::Type type;
|
||||
switch (m_wiimote_boxes[index]->currentIndex())
|
||||
if (index == WIIMOTE_BALANCE_BOARD)
|
||||
{
|
||||
type = MappingWindow::Type::MAPPING_BALANCE_BOARD_EMU;
|
||||
// Balance Board is a single entry its own InputConfig object.
|
||||
index = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
case 0: // None
|
||||
case 2: // Real Wii Remote
|
||||
return;
|
||||
case 1: // Emulated Wii Remote
|
||||
type = MappingWindow::Type::MAPPING_WIIMOTE_EMU;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
MappingWindow* window = new MappingWindow(this, type, static_cast<int>(index));
|
||||
|
@ -274,8 +273,6 @@ void WiimoteControllersWidget::LoadSettings(Core::State state)
|
|||
SignalBlocking(m_wiimote_boxes[i])
|
||||
->setCurrentIndex(int(Config::Get(Config::GetInfoForWiimoteSource(int(i)))));
|
||||
}
|
||||
SignalBlocking(m_wiimote_real_balance_board)
|
||||
->setChecked(Config::Get(Config::WIIMOTE_BB_SOURCE) == WiimoteSource::Real);
|
||||
SignalBlocking(m_wiimote_speaker_data)
|
||||
->setChecked(Config::Get(Config::MAIN_WIIMOTE_ENABLE_SPEAKER));
|
||||
SignalBlocking(m_wiimote_ciface)
|
||||
|
@ -308,7 +305,7 @@ void WiimoteControllersWidget::LoadSettings(Core::State state)
|
|||
for (auto* pt_label : m_wiimote_pt_labels)
|
||||
pt_label->setEnabled(enable_passthrough);
|
||||
|
||||
const int num_local_wiimotes = is_netplay ? NetPlay::NumLocalWiimotes() : 4;
|
||||
const int num_local_wiimotes = is_netplay ? NetPlay::NumLocalWiimotes() : MAX_BBMOTES;
|
||||
for (size_t i = 0; i < m_wiimote_groups.size(); i++)
|
||||
{
|
||||
m_wiimote_labels[i]->setEnabled(enable_emu_bt);
|
||||
|
@ -319,7 +316,6 @@ void WiimoteControllersWidget::LoadSettings(Core::State state)
|
|||
static_cast<int>(i) < num_local_wiimotes);
|
||||
}
|
||||
|
||||
m_wiimote_real_balance_board->setEnabled(enable_emu_bt && !running_netplay);
|
||||
m_wiimote_speaker_data->setEnabled(enable_emu_bt && !running_netplay);
|
||||
|
||||
const bool ciface_wiimotes = m_wiimote_ciface->isChecked();
|
||||
|
@ -342,10 +338,6 @@ void WiimoteControllersWidget::SaveSettings()
|
|||
Config::SetBaseOrCurrent(Config::MAIN_BLUETOOTH_PASSTHROUGH_ENABLED,
|
||||
m_wiimote_passthrough->isChecked());
|
||||
|
||||
const WiimoteSource bb_source =
|
||||
m_wiimote_real_balance_board->isChecked() ? WiimoteSource::Real : WiimoteSource::None;
|
||||
Config::SetBaseOrCurrent(Config::WIIMOTE_BB_SOURCE, bb_source);
|
||||
|
||||
for (size_t i = 0; i < m_wiimote_groups.size(); i++)
|
||||
{
|
||||
const int index = m_wiimote_boxes[i]->currentIndex();
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
#include <array>
|
||||
|
||||
#include "Core/HW/Wiimote.h"
|
||||
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
class QHBoxLayout;
|
||||
|
@ -42,10 +44,10 @@ private:
|
|||
|
||||
QGroupBox* m_wiimote_box;
|
||||
QGridLayout* m_wiimote_layout;
|
||||
std::array<QLabel*, 4> m_wiimote_labels;
|
||||
std::array<QComboBox*, 4> m_wiimote_boxes;
|
||||
std::array<QPushButton*, 4> m_wiimote_buttons;
|
||||
std::array<QHBoxLayout*, 4> m_wiimote_groups;
|
||||
std::array<QLabel*, MAX_BBMOTES> m_wiimote_labels;
|
||||
std::array<QComboBox*, MAX_BBMOTES> m_wiimote_boxes;
|
||||
std::array<QPushButton*, MAX_BBMOTES> m_wiimote_buttons;
|
||||
std::array<QHBoxLayout*, MAX_BBMOTES> m_wiimote_groups;
|
||||
std::array<QLabel*, 2> m_wiimote_pt_labels;
|
||||
|
||||
QRadioButton* m_wiimote_emu;
|
||||
|
@ -53,7 +55,6 @@ private:
|
|||
QPushButton* m_wiimote_sync;
|
||||
QPushButton* m_wiimote_reset;
|
||||
QCheckBox* m_wiimote_continuous_scanning;
|
||||
QCheckBox* m_wiimote_real_balance_board;
|
||||
QCheckBox* m_wiimote_speaker_data;
|
||||
QCheckBox* m_wiimote_ciface;
|
||||
QPushButton* m_wiimote_refresh;
|
||||
|
|
|
@ -90,6 +90,7 @@
|
|||
<ClCompile Include="Config\InfoWidget.cpp" />
|
||||
<ClCompile Include="Config\LogConfigWidget.cpp" />
|
||||
<ClCompile Include="Config\LogWidget.cpp" />
|
||||
<ClCompile Include="Config\Mapping\BalanceBoardGeneral.cpp" />
|
||||
<ClCompile Include="Config\Mapping\FreeLookGeneral.cpp" />
|
||||
<ClCompile Include="Config\Mapping\FreeLookRotation.cpp" />
|
||||
<ClCompile Include="Config\Mapping\GBAPadEmu.cpp" />
|
||||
|
@ -219,6 +220,7 @@
|
|||
<ClCompile Include="Settings\WiiPane.cpp" />
|
||||
<ClCompile Include="SkylanderPortal\SkylanderModifyDialog.cpp" />
|
||||
<ClCompile Include="SkylanderPortal\SkylanderPortalWindow.cpp" />
|
||||
<ClCompile Include="TAS\BalanceBoardWidget.cpp" />
|
||||
<ClCompile Include="TAS\GCTASInputWindow.cpp" />
|
||||
<ClCompile Include="TAS\GBATASInputWindow.cpp" />
|
||||
<ClCompile Include="TAS\IRWidget.cpp" />
|
||||
|
@ -312,6 +314,7 @@
|
|||
<QtMoc Include="Config\InfoWidget.h" />
|
||||
<QtMoc Include="Config\LogConfigWidget.h" />
|
||||
<QtMoc Include="Config\LogWidget.h" />
|
||||
<QtMoc Include="Config\Mapping\BalanceBoardGeneral.h" />
|
||||
<QtMoc Include="Config\Mapping\FreeLookGeneral.h" />
|
||||
<QtMoc Include="Config\Mapping\FreeLookRotation.h" />
|
||||
<QtMoc Include="Config\Mapping\GBAPadEmu.h" />
|
||||
|
@ -424,6 +427,7 @@
|
|||
<QtMoc Include="Settings\USBDeviceAddToWhitelistDialog.h" />
|
||||
<QtMoc Include="Settings\WiiPane.h" />
|
||||
<QtMoc Include="SkylanderPortal\SkylanderPortalWindow.h" />
|
||||
<QtMoc Include="TAS\BalanceBoardWidget.h" />
|
||||
<QtMoc Include="TAS\GCTASInputWindow.h" />
|
||||
<QtMoc Include="TAS\GBATASInputWindow.h" />
|
||||
<QtMoc Include="TAS\IRWidget.h" />
|
||||
|
|
|
@ -42,7 +42,6 @@
|
|||
#include "Core/Boot/Boot.h"
|
||||
#include "Core/BootManager.h"
|
||||
#include "Core/CommonTitles.h"
|
||||
#include "Core/Config/AchievementSettings.h"
|
||||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/Config/NetplaySettings.h"
|
||||
#include "Core/Config/UISettings.h"
|
||||
|
@ -56,10 +55,8 @@
|
|||
#include "Core/HW/ProcessorInterface.h"
|
||||
#include "Core/HW/SI/SI_Device.h"
|
||||
#include "Core/HW/Wiimote.h"
|
||||
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
|
||||
#include "Core/HotkeyManager.h"
|
||||
#include "Core/IOS/USB/Bluetooth/BTEmu.h"
|
||||
#include "Core/IOS/USB/Bluetooth/WiimoteDevice.h"
|
||||
#include "Core/Movie.h"
|
||||
#include "Core/NetPlayClient.h"
|
||||
#include "Core/NetPlayProto.h"
|
||||
|
@ -108,7 +105,6 @@
|
|||
#include "DolphinQt/QtUtils/FileOpenEventFilter.h"
|
||||
#include "DolphinQt/QtUtils/ModalMessageBox.h"
|
||||
#include "DolphinQt/QtUtils/ParallelProgressDialog.h"
|
||||
#include "DolphinQt/QtUtils/QueueOnObject.h"
|
||||
#include "DolphinQt/QtUtils/RunOnObject.h"
|
||||
#include "DolphinQt/QtUtils/SetWindowDecorations.h"
|
||||
#include "DolphinQt/QtUtils/WindowActivationEventFilter.h"
|
||||
|
@ -126,18 +122,14 @@
|
|||
#include "DolphinQt/WiiUpdate.h"
|
||||
|
||||
#include "InputCommon/ControllerInterface/ControllerInterface.h"
|
||||
#include "InputCommon/GCAdapter.h"
|
||||
|
||||
#include "UICommon/DiscordPresence.h"
|
||||
#include "UICommon/GameFile.h"
|
||||
#include "UICommon/ResourcePack/Manager.h"
|
||||
#include "UICommon/ResourcePack/Manifest.h"
|
||||
#include "UICommon/ResourcePack/ResourcePack.h"
|
||||
|
||||
#include "UICommon/UICommon.h"
|
||||
|
||||
#include "VideoCommon/NetPlayChatUI.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
|
||||
#ifdef HAVE_XRANDR
|
||||
#include "UICommon/X11Utils.h"
|
||||
|
@ -350,12 +342,12 @@ MainWindow::~MainWindow()
|
|||
delete m_render_widget;
|
||||
delete m_netplay_dialog;
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
delete m_gc_tas_input_windows[i];
|
||||
delete m_gba_tas_input_windows[i];
|
||||
delete m_wii_tas_input_windows[i];
|
||||
}
|
||||
for (auto& window : m_gc_tas_input_windows)
|
||||
delete window;
|
||||
for (auto& window : m_gba_tas_input_windows)
|
||||
delete window;
|
||||
for (auto& window : m_wii_tas_input_windows)
|
||||
delete window;
|
||||
|
||||
ShutdownControllers();
|
||||
|
||||
|
@ -458,13 +450,17 @@ void MainWindow::CreateComponents()
|
|||
m_render_widget = new RenderWidget;
|
||||
m_stack = new QStackedWidget(this);
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
for (int i = 0; i != num_gc_controllers; ++i)
|
||||
{
|
||||
m_gc_tas_input_windows[i] = new GCTASInputWindow(nullptr, i);
|
||||
m_gba_tas_input_windows[i] = new GBATASInputWindow(nullptr, i);
|
||||
m_wii_tas_input_windows[i] = new WiiTASInputWindow(nullptr, i);
|
||||
}
|
||||
|
||||
for (int i = 0; i != MAX_WIIMOTES; ++i)
|
||||
m_wii_tas_input_windows[i] = new WiiTASInputWindow(nullptr, i);
|
||||
|
||||
m_wii_tas_input_windows[WIIMOTE_BALANCE_BOARD] = new BalanceBoardTASInputWindow(nullptr);
|
||||
|
||||
m_jit_widget = new JITWidget(m_system, this);
|
||||
m_log_widget = new LogWidget(this);
|
||||
m_log_config_widget = new LogConfigWidget(this);
|
||||
|
@ -1873,7 +1869,7 @@ void MainWindow::OnStartRecording()
|
|||
Movie::ControllerTypeArray controllers{};
|
||||
Movie::WiimoteEnabledArray wiimotes{};
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
for (int i = 0; i < num_gc_controllers; i++)
|
||||
{
|
||||
const SerialInterface::SIDevices si_device = Config::Get(Config::GetInfoForSIDevice(i));
|
||||
if (si_device == SerialInterface::SIDEVICE_GC_GBA_EMULATED)
|
||||
|
@ -1882,9 +1878,11 @@ void MainWindow::OnStartRecording()
|
|||
controllers[i] = Movie::ControllerType::GC;
|
||||
else
|
||||
controllers[i] = Movie::ControllerType::None;
|
||||
wiimotes[i] = Config::Get(Config::GetInfoForWiimoteSource(i)) != WiimoteSource::None;
|
||||
}
|
||||
|
||||
for (int i = 0; i != MAX_BBMOTES; ++i)
|
||||
wiimotes[i] = Config::Get(Config::GetInfoForWiimoteSource(i)) != WiimoteSource::None;
|
||||
|
||||
if (movie.BeginRecordingInput(controllers, wiimotes))
|
||||
{
|
||||
emit RecordingStatusChanged(true);
|
||||
|
@ -1949,7 +1947,7 @@ void MainWindow::ShowTASInput()
|
|||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_wii_controllers; i++)
|
||||
for (int i = 0; i != MAX_BBMOTES; ++i)
|
||||
{
|
||||
if (Config::Get(Config::GetInfoForWiimoteSource(i)) == WiimoteSource::Emulated &&
|
||||
(!Core::IsRunning(m_system) || m_system.IsWii()))
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#include <string>
|
||||
|
||||
#include "Core/Boot/Boot.h"
|
||||
#include "Core/HW/SI/SI.h"
|
||||
#include "Core/HW/Wiimote.h"
|
||||
|
||||
class QMenu;
|
||||
class QStackedWidget;
|
||||
|
@ -51,7 +53,7 @@ class SkylanderPortalWindow;
|
|||
class ThreadWidget;
|
||||
class ToolBar;
|
||||
class WatchWidget;
|
||||
class WiiTASInputWindow;
|
||||
class WiiBaseTASInputWindow;
|
||||
struct WindowSystemInfo;
|
||||
|
||||
namespace Core
|
||||
|
@ -252,11 +254,10 @@ private:
|
|||
NetPlayDialog* m_netplay_dialog;
|
||||
DiscordHandler* m_netplay_discord;
|
||||
NetPlaySetupDialog* m_netplay_setup_dialog;
|
||||
static constexpr int num_gc_controllers = 4;
|
||||
static constexpr int num_gc_controllers = SerialInterface::MAX_SI_CHANNELS;
|
||||
std::array<GCTASInputWindow*, num_gc_controllers> m_gc_tas_input_windows{};
|
||||
std::array<GBATASInputWindow*, num_gc_controllers> m_gba_tas_input_windows{};
|
||||
static constexpr int num_wii_controllers = 4;
|
||||
std::array<WiiTASInputWindow*, num_wii_controllers> m_wii_tas_input_windows{};
|
||||
std::array<QDialog*, MAX_BBMOTES> m_wii_tas_input_windows{};
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
AchievementsWindow* m_achievements_window = nullptr;
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
// Copyright 2025 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "DolphinQt/TAS/BalanceBoardWidget.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QSpinBox>
|
||||
|
||||
#include "Core/HW/WiimoteEmu/Extension/BalanceBoard.h"
|
||||
|
||||
BalanceBoardWidget::BalanceBoardWidget(QWidget* parent, QDoubleSpinBox* total_weight_spinbox,
|
||||
const std::array<QDoubleSpinBox*, 4>& sensors)
|
||||
: QWidget(parent)
|
||||
{
|
||||
setMouseTracking(false);
|
||||
setToolTip(tr("Left click to set the balance value.\n"
|
||||
"Right click to return to perfect balance."));
|
||||
|
||||
using BW = BalanceBoardWidget;
|
||||
|
||||
auto const connect_sensor = [&](QDoubleSpinBox* spinbox, double* this_value) {
|
||||
connect(this, &BW::UpdateSensorWidgets, this, [this, spinbox, this_value]() {
|
||||
QSignalBlocker blocker{this};
|
||||
spinbox->setValue(*this_value);
|
||||
});
|
||||
connect(spinbox, &QDoubleSpinBox::valueChanged, this,
|
||||
[this, spinbox, this_value](double new_value) {
|
||||
if (signalsBlocked())
|
||||
return;
|
||||
*this_value = spinbox->value();
|
||||
update();
|
||||
UpdateTotalWidget();
|
||||
});
|
||||
};
|
||||
|
||||
connect_sensor(sensors[0], &m_sensor_tr);
|
||||
connect_sensor(sensors[1], &m_sensor_br);
|
||||
connect_sensor(sensors[2], &m_sensor_tl);
|
||||
connect_sensor(sensors[3], &m_sensor_bl);
|
||||
|
||||
connect(this, &BW::UpdateTotalWidget, this, [this, box = total_weight_spinbox]() {
|
||||
QSignalBlocker blocker{this};
|
||||
box->setValue(GetTotalWeight());
|
||||
});
|
||||
connect(total_weight_spinbox, &QDoubleSpinBox::valueChanged, this, [this](double new_total) {
|
||||
if (signalsBlocked())
|
||||
return;
|
||||
SetTotal(new_total);
|
||||
});
|
||||
|
||||
SetTotal(WiimoteEmu::BalanceBoardExt::DEFAULT_WEIGHT);
|
||||
UpdateTotalWidget();
|
||||
}
|
||||
|
||||
double BalanceBoardWidget::GetTotalWeight()
|
||||
{
|
||||
return m_sensor_tr + m_sensor_br + m_sensor_tl + m_sensor_bl;
|
||||
}
|
||||
|
||||
void BalanceBoardWidget::SetTotal(double total)
|
||||
{
|
||||
const auto cob = GetCenterOfBalance();
|
||||
const auto quarter_weight = total * 0.25;
|
||||
m_sensor_tr = quarter_weight;
|
||||
m_sensor_br = quarter_weight;
|
||||
m_sensor_tl = quarter_weight;
|
||||
m_sensor_bl = quarter_weight;
|
||||
SetCenterOfBalance(cob);
|
||||
}
|
||||
|
||||
void BalanceBoardWidget::paintEvent(QPaintEvent* event)
|
||||
{
|
||||
QPainter painter(this);
|
||||
|
||||
painter.setRenderHint(QPainter::Antialiasing, true);
|
||||
painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
|
||||
|
||||
painter.setBrush(Qt::white);
|
||||
painter.drawRect(0, 0, width() - 1, height() - 1);
|
||||
|
||||
painter.drawLine(0, height() / 2, width(), height() / 2);
|
||||
painter.drawLine(width() / 2, 0, width() / 2, height());
|
||||
|
||||
auto cob = GetCenterOfBalance();
|
||||
|
||||
// Convert to widget space.
|
||||
cob.x += 1;
|
||||
cob.y = 1 - cob.y;
|
||||
cob *= Common::DVec2(width(), height()) * 0.5;
|
||||
|
||||
painter.drawLine(width() / 2, height() / 2, cob.x, cob.y);
|
||||
|
||||
const int wh_avg = (width() + height()) / 2;
|
||||
const int radius = wh_avg / 30;
|
||||
|
||||
painter.setBrush(Qt::blue);
|
||||
painter.drawEllipse(QPointF{cob.x, cob.y}, radius, radius);
|
||||
}
|
||||
|
||||
void BalanceBoardWidget::mousePressEvent(QMouseEvent* event)
|
||||
{
|
||||
if (event->button() != Qt::RightButton)
|
||||
return;
|
||||
|
||||
SetCenterOfBalance({0, 0});
|
||||
}
|
||||
|
||||
void BalanceBoardWidget::mouseMoveEvent(QMouseEvent* event)
|
||||
{
|
||||
if (event->buttons() != Qt::LeftButton)
|
||||
return;
|
||||
|
||||
// Convert from widget space to value space.
|
||||
const double com_x = std::clamp((event->pos().x() * 2.0) / width() - 1, -1.0, 1.0);
|
||||
const double com_y = std::clamp(1 - (event->pos().y() * 2.0) / height(), -1.0, 1.0);
|
||||
|
||||
SetCenterOfBalance({com_x, com_y});
|
||||
}
|
||||
|
||||
void BalanceBoardWidget::SetCenterOfBalance(Common::DVec2 cob)
|
||||
{
|
||||
std::tie(m_sensor_tr, m_sensor_br, m_sensor_tl, m_sensor_bl) = std::tuple_cat(
|
||||
WiimoteEmu::BalanceBoardExt::CenterOfBalanceToSensors(cob, GetTotalWeight()).data);
|
||||
|
||||
update();
|
||||
UpdateSensorWidgets();
|
||||
}
|
||||
|
||||
Common::DVec2 BalanceBoardWidget::GetCenterOfBalance() const
|
||||
{
|
||||
return WiimoteEmu::BalanceBoardExt::SensorsToCenterOfBalance(
|
||||
{m_sensor_tr, m_sensor_br, m_sensor_tl, m_sensor_bl});
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright 2025 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include "Common/Matrix.h"
|
||||
|
||||
class QDoubleSpinBox;
|
||||
|
||||
class BalanceBoardWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
signals:
|
||||
void UpdateSensorWidgets();
|
||||
void UpdateTotalWidget();
|
||||
|
||||
public:
|
||||
explicit BalanceBoardWidget(QWidget* parent, QDoubleSpinBox* total,
|
||||
const std::array<QDoubleSpinBox*, 4>& sensors);
|
||||
|
||||
void SetTotal(double total_weight);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent* event) override;
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
void mouseMoveEvent(QMouseEvent* event) override;
|
||||
|
||||
private:
|
||||
double GetTotalWeight();
|
||||
void SetCenterOfBalance(Common::DVec2);
|
||||
Common::DVec2 GetCenterOfBalance() const;
|
||||
|
||||
double m_sensor_tr = 0;
|
||||
double m_sensor_br = 0;
|
||||
double m_sensor_tl = 0;
|
||||
double m_sensor_bl = 0;
|
||||
};
|
|
@ -54,14 +54,14 @@ void IRWidget::paintEvent(QPaintEvent* event)
|
|||
painter.drawLine(PADDING + w / 2, PADDING, PADDING + w / 2, PADDING + h);
|
||||
|
||||
// convert from value space to widget space
|
||||
u16 x = PADDING + ((m_x * w) / IR_MAX_X);
|
||||
u16 y = PADDING + (h - (m_y * h) / IR_MAX_Y);
|
||||
const int x = PADDING + ((m_x * w) / IR_MAX_X);
|
||||
const int y = PADDING + (h - (m_y * h) / IR_MAX_Y);
|
||||
|
||||
painter.drawLine(PADDING + w / 2, PADDING + h / 2, x, y);
|
||||
|
||||
painter.setBrush(Qt::blue);
|
||||
int wh_avg = (w + h) / 2;
|
||||
int radius = wh_avg / 30;
|
||||
const int wh_avg = (w + h) / 2;
|
||||
const int radius = wh_avg / 30;
|
||||
painter.drawEllipse(x - radius, y - radius, radius * 2, radius * 2);
|
||||
}
|
||||
|
||||
|
@ -90,8 +90,8 @@ void IRWidget::handleMouseEvent(QMouseEvent* event)
|
|||
else
|
||||
{
|
||||
// convert from widget space to value space
|
||||
int new_x = (event->pos().x() * IR_MAX_X) / width();
|
||||
int new_y = IR_MAX_Y - (event->pos().y() * IR_MAX_Y) / height();
|
||||
const int new_x = (event->pos().x() * IR_MAX_X) / width();
|
||||
const int new_y = IR_MAX_Y - (event->pos().y() * IR_MAX_Y) / height();
|
||||
|
||||
m_x = std::max(0, std::min(static_cast<int>(IR_MAX_X), new_x));
|
||||
m_y = std::max(0, std::min(static_cast<int>(IR_MAX_Y), new_y));
|
||||
|
|
|
@ -57,13 +57,13 @@ void StickWidget::paintEvent(QPaintEvent* event)
|
|||
painter.drawLine(PADDING + diameter / 2, PADDING, PADDING + diameter / 2, PADDING + diameter);
|
||||
|
||||
// convert from value space to widget space
|
||||
u16 x = PADDING + ((m_x * diameter) / m_max_x);
|
||||
u16 y = PADDING + (diameter - (m_y * diameter) / m_max_y);
|
||||
const int x = PADDING + ((m_x * diameter) / m_max_x);
|
||||
const int y = PADDING + (diameter - (m_y * diameter) / m_max_y);
|
||||
|
||||
painter.drawLine(PADDING + diameter / 2, PADDING + diameter / 2, x, y);
|
||||
|
||||
painter.setBrush(Qt::blue);
|
||||
int neutral_radius = diameter / 30;
|
||||
const int neutral_radius = diameter / 30;
|
||||
painter.drawEllipse(x - neutral_radius, y - neutral_radius, neutral_radius * 2,
|
||||
neutral_radius * 2);
|
||||
}
|
||||
|
@ -93,8 +93,8 @@ void StickWidget::handleMouseEvent(QMouseEvent* event)
|
|||
else
|
||||
{
|
||||
// convert from widget space to value space
|
||||
int new_x = (event->pos().x() * m_max_x) / width();
|
||||
int new_y = m_max_y - (event->pos().y() * m_max_y) / height();
|
||||
const int new_x = (event->pos().x() * m_max_x) / width();
|
||||
const int new_y = m_max_y - (event->pos().y() * m_max_y) / height();
|
||||
|
||||
m_x = std::max(0, std::min(static_cast<int>(m_max_x), new_x));
|
||||
m_y = std::max(0, std::min(static_cast<int>(m_max_y), new_y));
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include <QApplication>
|
||||
#include <QCheckBox>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QEvent>
|
||||
#include <QGroupBox>
|
||||
#include <QHBoxLayout>
|
||||
|
@ -17,8 +18,6 @@
|
|||
#include <QSpinBox>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
#include "DolphinQt/Host.h"
|
||||
#include "DolphinQt/QtUtils/AspectRatioWidget.h"
|
||||
#include "DolphinQt/QtUtils/QueueOnObject.h"
|
||||
|
@ -202,7 +201,7 @@ TASSpinBox* TASInputWindow::CreateSliderValuePair(
|
|||
}
|
||||
|
||||
// The shortcut_widget argument needs to specify the container widget that will be hidden/shown.
|
||||
// This is done to avoid ambigous shortcuts
|
||||
// This is done to avoid ambiguous shortcuts
|
||||
TASSpinBox* TASInputWindow::CreateSliderValuePair(QBoxLayout* layout, int default_, int max,
|
||||
QKeySequence shortcut_key_sequence,
|
||||
Qt::Orientation orientation,
|
||||
|
@ -237,6 +236,60 @@ TASSpinBox* TASInputWindow::CreateSliderValuePair(QBoxLayout* layout, int defaul
|
|||
return value;
|
||||
}
|
||||
|
||||
QDoubleSpinBox* TASInputWindow::CreateWeightSliderValuePair(std::string_view group_name,
|
||||
std::string_view control_name,
|
||||
InputOverrider* overrider,
|
||||
QBoxLayout* layout, int min, int max,
|
||||
QKeySequence shortcut_key_sequence,
|
||||
QWidget* shortcut_widget)
|
||||
{
|
||||
QDoubleSpinBox* value =
|
||||
CreateWeightSliderValuePair(layout, min, max, shortcut_key_sequence, shortcut_widget);
|
||||
|
||||
InputOverrider::OverrideFunction func = [this, value](ControlState controller_state) {
|
||||
return GetSpinBox(value, controller_state);
|
||||
};
|
||||
|
||||
overrider->AddFunction(group_name, control_name, std::move(func));
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// The shortcut_widget argument needs to specify the container widget that will be hidden/shown.
|
||||
// This is done to avoid ambiguous shortcuts
|
||||
QDoubleSpinBox* TASInputWindow::CreateWeightSliderValuePair(QBoxLayout* layout, int min, int max,
|
||||
QKeySequence shortcut_key_sequence,
|
||||
QWidget* shortcut_widget)
|
||||
{
|
||||
auto* value = new QDoubleSpinBox();
|
||||
value->setRange(min, max);
|
||||
value->setDecimals(2);
|
||||
value->setSuffix(QStringLiteral("kg"));
|
||||
auto* slider = new QSlider(Qt::Orientation::Horizontal);
|
||||
slider->setRange(min * 100, max * 100);
|
||||
slider->setFocusPolicy(Qt::ClickFocus);
|
||||
slider->setSingleStep(100);
|
||||
slider->setPageStep(1000);
|
||||
slider->setTickPosition(QSlider::TickPosition::TicksBelow);
|
||||
|
||||
connect(slider, &QSlider::valueChanged, value, [value](int i) { value->setValue(i / 100.0); });
|
||||
connect(value, &QDoubleSpinBox::valueChanged, slider, [slider](double d) {
|
||||
QSignalBlocker blocker{slider};
|
||||
slider->setValue((int)(d * 100));
|
||||
});
|
||||
|
||||
auto* shortcut = new QShortcut(shortcut_key_sequence, shortcut_widget);
|
||||
connect(shortcut, &QShortcut::activated, [value] {
|
||||
value->setFocus();
|
||||
value->selectAll();
|
||||
});
|
||||
|
||||
layout->addWidget(slider);
|
||||
layout->addWidget(value);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
std::optional<ControlState> TASInputWindow::GetButton(TASCheckBox* checkbox,
|
||||
ControlState controller_state)
|
||||
{
|
||||
|
@ -272,6 +325,27 @@ std::optional<ControlState> TASInputWindow::GetSpinBox(TASSpinBox* spin, int zer
|
|||
return (spin->GetValue() - zero) / scale;
|
||||
}
|
||||
|
||||
std::optional<ControlState> TASInputWindow::GetSpinBox(QDoubleSpinBox* spin,
|
||||
ControlState controller_state)
|
||||
{
|
||||
if (m_use_controller->isChecked())
|
||||
{
|
||||
if (!m_spinbox_most_recent_values_double.count(spin) ||
|
||||
m_spinbox_most_recent_values_double[spin] != controller_state)
|
||||
{
|
||||
QueueOnObjectBlocking(spin, [spin, controller_state] { spin->setValue(controller_state); });
|
||||
}
|
||||
|
||||
m_spinbox_most_recent_values_double[spin] = controller_state;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_spinbox_most_recent_values_double.clear();
|
||||
}
|
||||
|
||||
return spin->value();
|
||||
}
|
||||
|
||||
void TASInputWindow::changeEvent(QEvent* const event)
|
||||
{
|
||||
if (event->type() == QEvent::ActivationChange)
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
class QBoxLayout;
|
||||
class QCheckBox;
|
||||
class QDialog;
|
||||
class QDoubleSpinBox;
|
||||
class QEvent;
|
||||
class QGroupBox;
|
||||
class QSpinBox;
|
||||
|
@ -68,6 +69,14 @@ protected:
|
|||
TASSpinBox* CreateSliderValuePair(QBoxLayout* layout, int default_, int max,
|
||||
QKeySequence shortcut_key_sequence, Qt::Orientation orientation,
|
||||
QWidget* shortcut_widget);
|
||||
QDoubleSpinBox* CreateWeightSliderValuePair(std::string_view group_name,
|
||||
std::string_view control_name,
|
||||
InputOverrider* overrider, QBoxLayout* layout,
|
||||
int min, int max, QKeySequence shortcut_key_sequence,
|
||||
QWidget* shortcut_widget);
|
||||
QDoubleSpinBox* CreateWeightSliderValuePair(QBoxLayout* layout, int min, int max,
|
||||
QKeySequence shortcut_key_sequence,
|
||||
QWidget* shortcut_widget);
|
||||
|
||||
void changeEvent(QEvent* event) override;
|
||||
|
||||
|
@ -82,4 +91,7 @@ private:
|
|||
ControlState controller_state);
|
||||
std::optional<ControlState> GetSpinBox(TASSpinBox* spin, int zero, ControlState controller_state,
|
||||
ControlState scale);
|
||||
std::optional<ControlState> GetSpinBox(QDoubleSpinBox* spin, ControlState controller_state);
|
||||
|
||||
std::map<QDoubleSpinBox*, u16> m_spinbox_most_recent_values_double;
|
||||
};
|
||||
|
|
|
@ -19,22 +19,22 @@
|
|||
|
||||
#include "Core/Core.h"
|
||||
#include "Core/HW/Wiimote.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/BalanceBoard.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/Classic.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/Extension.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/Nunchuk.h"
|
||||
#include "Core/HW/WiimoteEmu/MotionPlus.h"
|
||||
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
|
||||
#include "Core/HW/WiimoteReal/WiimoteReal.h"
|
||||
#include "Core/System.h"
|
||||
|
||||
#include "DolphinQt/QtUtils/AspectRatioWidget.h"
|
||||
#include "DolphinQt/QtUtils/QueueOnObject.h"
|
||||
#include "DolphinQt/QtUtils/SetWindowDecorations.h"
|
||||
#include "DolphinQt/TAS/BalanceBoardWidget.h"
|
||||
#include "DolphinQt/TAS/IRWidget.h"
|
||||
#include "DolphinQt/TAS/TASCheckBox.h"
|
||||
#include "DolphinQt/TAS/TASSpinBox.h"
|
||||
|
||||
#include "InputCommon/ControllerEmu/ControlGroup/Attachments.h"
|
||||
#include "InputCommon/ControllerEmu/ControllerEmu.h"
|
||||
#include "InputCommon/ControllerEmu/StickGate.h"
|
||||
#include "InputCommon/InputConfig.h"
|
||||
|
@ -55,26 +55,26 @@ WiiTASInputWindow::WiiTASInputWindow(QWidget* parent, int num) : TASInputWindow(
|
|||
const int ir_y_center = static_cast<int>(std::round(IRWidget::IR_MAX_Y / 2.));
|
||||
|
||||
auto* x_layout = new QHBoxLayout;
|
||||
m_ir_x_value = CreateSliderValuePair(
|
||||
auto* const ir_x_value = CreateSliderValuePair(
|
||||
WiimoteEmu::Wiimote::IR_GROUP, ControllerEmu::ReshapableInput::X_INPUT_OVERRIDE,
|
||||
&m_wiimote_overrider, x_layout, ir_x_center, ir_x_center, IRWidget::IR_MIN_X,
|
||||
IRWidget::IR_MAX_X, ir_x_shortcut_key_sequence, Qt::Horizontal, m_ir_box);
|
||||
|
||||
auto* y_layout = new QVBoxLayout;
|
||||
m_ir_y_value = CreateSliderValuePair(
|
||||
auto* const ir_y_value = CreateSliderValuePair(
|
||||
WiimoteEmu::Wiimote::IR_GROUP, ControllerEmu::ReshapableInput::Y_INPUT_OVERRIDE,
|
||||
&m_wiimote_overrider, y_layout, ir_y_center, ir_y_center, IRWidget::IR_MIN_Y,
|
||||
IRWidget::IR_MAX_Y, ir_y_shortcut_key_sequence, Qt::Vertical, m_ir_box);
|
||||
m_ir_y_value->setMaximumWidth(60);
|
||||
ir_y_value->setMaximumWidth(60);
|
||||
|
||||
auto* visual = new IRWidget(this);
|
||||
visual->SetX(ir_x_center);
|
||||
visual->SetY(ir_y_center);
|
||||
|
||||
connect(m_ir_x_value, &QSpinBox::valueChanged, visual, &IRWidget::SetX);
|
||||
connect(m_ir_y_value, &QSpinBox::valueChanged, visual, &IRWidget::SetY);
|
||||
connect(visual, &IRWidget::ChangedX, m_ir_x_value, &QSpinBox::setValue);
|
||||
connect(visual, &IRWidget::ChangedY, m_ir_y_value, &QSpinBox::setValue);
|
||||
connect(ir_x_value, &QSpinBox::valueChanged, visual, &IRWidget::SetX);
|
||||
connect(ir_y_value, &QSpinBox::valueChanged, visual, &IRWidget::SetY);
|
||||
connect(visual, &IRWidget::ChangedX, ir_x_value, &QSpinBox::setValue);
|
||||
connect(visual, &IRWidget::ChangedY, ir_y_value, &QSpinBox::setValue);
|
||||
|
||||
auto* visual_ar = new AspectRatioWidget(visual, IRWidget::IR_MAX_X, IRWidget::IR_MAX_Y);
|
||||
|
||||
|
@ -223,48 +223,49 @@ WiiTASInputWindow::WiiTASInputWindow(QWidget* parent, int num) : TASInputWindow(
|
|||
triggers_layout->addLayout(r_trigger_layout);
|
||||
m_triggers_box->setLayout(triggers_layout);
|
||||
|
||||
m_a_button = CreateButton(QStringLiteral("&A"), WiimoteEmu::Wiimote::BUTTONS_GROUP,
|
||||
auto* const a_button = CreateButton(QStringLiteral("&A"), WiimoteEmu::Wiimote::BUTTONS_GROUP,
|
||||
WiimoteEmu::Wiimote::A_BUTTON, &m_wiimote_overrider);
|
||||
m_b_button = CreateButton(QStringLiteral("&B"), WiimoteEmu::Wiimote::BUTTONS_GROUP,
|
||||
auto* const b_button = CreateButton(QStringLiteral("&B"), WiimoteEmu::Wiimote::BUTTONS_GROUP,
|
||||
WiimoteEmu::Wiimote::B_BUTTON, &m_wiimote_overrider);
|
||||
m_1_button = CreateButton(QStringLiteral("&1"), WiimoteEmu::Wiimote::BUTTONS_GROUP,
|
||||
auto* const button_1 = CreateButton(QStringLiteral("&1"), WiimoteEmu::Wiimote::BUTTONS_GROUP,
|
||||
WiimoteEmu::Wiimote::ONE_BUTTON, &m_wiimote_overrider);
|
||||
m_2_button = CreateButton(QStringLiteral("&2"), WiimoteEmu::Wiimote::BUTTONS_GROUP,
|
||||
auto* const button_2 = CreateButton(QStringLiteral("&2"), WiimoteEmu::Wiimote::BUTTONS_GROUP,
|
||||
WiimoteEmu::Wiimote::TWO_BUTTON, &m_wiimote_overrider);
|
||||
m_plus_button = CreateButton(QStringLiteral("&+"), WiimoteEmu::Wiimote::BUTTONS_GROUP,
|
||||
auto* const plus_button = CreateButton(QStringLiteral("&+"), WiimoteEmu::Wiimote::BUTTONS_GROUP,
|
||||
WiimoteEmu::Wiimote::PLUS_BUTTON, &m_wiimote_overrider);
|
||||
m_minus_button = CreateButton(QStringLiteral("&-"), WiimoteEmu::Wiimote::BUTTONS_GROUP,
|
||||
auto* const minus_button = CreateButton(QStringLiteral("&-"), WiimoteEmu::Wiimote::BUTTONS_GROUP,
|
||||
WiimoteEmu::Wiimote::MINUS_BUTTON, &m_wiimote_overrider);
|
||||
m_home_button = CreateButton(QStringLiteral("&HOME"), WiimoteEmu::Wiimote::BUTTONS_GROUP,
|
||||
auto* const home_button =
|
||||
CreateButton(QStringLiteral("&HOME"), WiimoteEmu::Wiimote::BUTTONS_GROUP,
|
||||
WiimoteEmu::Wiimote::HOME_BUTTON, &m_wiimote_overrider);
|
||||
|
||||
m_left_button = CreateButton(QStringLiteral("&Left"), WiimoteEmu::Wiimote::DPAD_GROUP,
|
||||
auto* const left_button = CreateButton(QStringLiteral("&Left"), WiimoteEmu::Wiimote::DPAD_GROUP,
|
||||
DIRECTION_LEFT, &m_wiimote_overrider);
|
||||
m_up_button = CreateButton(QStringLiteral("&Up"), WiimoteEmu::Wiimote::DPAD_GROUP, DIRECTION_UP,
|
||||
&m_wiimote_overrider);
|
||||
m_down_button = CreateButton(QStringLiteral("&Down"), WiimoteEmu::Wiimote::DPAD_GROUP,
|
||||
auto* const up_button = CreateButton(QStringLiteral("&Up"), WiimoteEmu::Wiimote::DPAD_GROUP,
|
||||
DIRECTION_UP, &m_wiimote_overrider);
|
||||
auto* const down_button = CreateButton(QStringLiteral("&Down"), WiimoteEmu::Wiimote::DPAD_GROUP,
|
||||
DIRECTION_DOWN, &m_wiimote_overrider);
|
||||
m_right_button = CreateButton(QStringLiteral("&Right"), WiimoteEmu::Wiimote::DPAD_GROUP,
|
||||
auto* const right_button = CreateButton(QStringLiteral("&Right"), WiimoteEmu::Wiimote::DPAD_GROUP,
|
||||
DIRECTION_RIGHT, &m_wiimote_overrider);
|
||||
|
||||
m_c_button = CreateButton(QStringLiteral("&C"), WiimoteEmu::Nunchuk::BUTTONS_GROUP,
|
||||
auto* const c_button = CreateButton(QStringLiteral("&C"), WiimoteEmu::Nunchuk::BUTTONS_GROUP,
|
||||
WiimoteEmu::Nunchuk::C_BUTTON, &m_nunchuk_overrider);
|
||||
m_z_button = CreateButton(QStringLiteral("&Z"), WiimoteEmu::Nunchuk::BUTTONS_GROUP,
|
||||
auto* const z_button = CreateButton(QStringLiteral("&Z"), WiimoteEmu::Nunchuk::BUTTONS_GROUP,
|
||||
WiimoteEmu::Nunchuk::Z_BUTTON, &m_nunchuk_overrider);
|
||||
|
||||
auto* buttons_layout = new QGridLayout;
|
||||
buttons_layout->addWidget(m_a_button, 0, 0);
|
||||
buttons_layout->addWidget(m_b_button, 0, 1);
|
||||
buttons_layout->addWidget(m_1_button, 0, 2);
|
||||
buttons_layout->addWidget(m_2_button, 0, 3);
|
||||
buttons_layout->addWidget(m_plus_button, 0, 4);
|
||||
buttons_layout->addWidget(m_minus_button, 0, 5);
|
||||
buttons_layout->addWidget(a_button, 0, 0);
|
||||
buttons_layout->addWidget(b_button, 0, 1);
|
||||
buttons_layout->addWidget(button_1, 0, 2);
|
||||
buttons_layout->addWidget(button_2, 0, 3);
|
||||
buttons_layout->addWidget(plus_button, 0, 4);
|
||||
buttons_layout->addWidget(minus_button, 0, 5);
|
||||
|
||||
buttons_layout->addWidget(m_home_button, 1, 0);
|
||||
buttons_layout->addWidget(m_left_button, 1, 1);
|
||||
buttons_layout->addWidget(m_up_button, 1, 2);
|
||||
buttons_layout->addWidget(m_down_button, 1, 3);
|
||||
buttons_layout->addWidget(m_right_button, 1, 4);
|
||||
buttons_layout->addWidget(home_button, 1, 0);
|
||||
buttons_layout->addWidget(left_button, 1, 1);
|
||||
buttons_layout->addWidget(up_button, 1, 2);
|
||||
buttons_layout->addWidget(down_button, 1, 3);
|
||||
buttons_layout->addWidget(right_button, 1, 4);
|
||||
|
||||
buttons_layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Expanding), 0, 7);
|
||||
|
||||
|
@ -272,63 +273,70 @@ WiiTASInputWindow::WiiTASInputWindow(QWidget* parent, int num) : TASInputWindow(
|
|||
m_remote_buttons_box->setLayout(buttons_layout);
|
||||
|
||||
auto* nunchuk_buttons_layout = new QHBoxLayout;
|
||||
nunchuk_buttons_layout->addWidget(m_c_button);
|
||||
nunchuk_buttons_layout->addWidget(m_z_button);
|
||||
nunchuk_buttons_layout->addWidget(c_button);
|
||||
nunchuk_buttons_layout->addWidget(z_button);
|
||||
nunchuk_buttons_layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Expanding));
|
||||
|
||||
m_nunchuk_buttons_box = new QGroupBox(tr("Nunchuk Buttons"));
|
||||
m_nunchuk_buttons_box->setLayout(nunchuk_buttons_layout);
|
||||
|
||||
m_classic_a_button = CreateButton(QStringLiteral("&A"), WiimoteEmu::Classic::BUTTONS_GROUP,
|
||||
WiimoteEmu::Classic::A_BUTTON, &m_classic_overrider);
|
||||
m_classic_b_button = CreateButton(QStringLiteral("&B"), WiimoteEmu::Classic::BUTTONS_GROUP,
|
||||
WiimoteEmu::Classic::B_BUTTON, &m_classic_overrider);
|
||||
m_classic_x_button = CreateButton(QStringLiteral("&X"), WiimoteEmu::Classic::BUTTONS_GROUP,
|
||||
WiimoteEmu::Classic::X_BUTTON, &m_classic_overrider);
|
||||
m_classic_y_button = CreateButton(QStringLiteral("&Y"), WiimoteEmu::Classic::BUTTONS_GROUP,
|
||||
WiimoteEmu::Classic::Y_BUTTON, &m_classic_overrider);
|
||||
m_classic_zl_button = CreateButton(QStringLiteral("&ZL"), WiimoteEmu::Classic::BUTTONS_GROUP,
|
||||
WiimoteEmu::Classic::ZL_BUTTON, &m_classic_overrider);
|
||||
m_classic_zr_button = CreateButton(QStringLiteral("ZR"), WiimoteEmu::Classic::BUTTONS_GROUP,
|
||||
WiimoteEmu::Classic::ZR_BUTTON, &m_classic_overrider);
|
||||
m_classic_plus_button = CreateButton(QStringLiteral("&+"), WiimoteEmu::Classic::BUTTONS_GROUP,
|
||||
WiimoteEmu::Classic::PLUS_BUTTON, &m_classic_overrider);
|
||||
m_classic_minus_button = CreateButton(QStringLiteral("&-"), WiimoteEmu::Classic::BUTTONS_GROUP,
|
||||
WiimoteEmu::Classic::MINUS_BUTTON, &m_classic_overrider);
|
||||
m_classic_home_button = CreateButton(QStringLiteral("&HOME"), WiimoteEmu::Classic::BUTTONS_GROUP,
|
||||
WiimoteEmu::Classic::HOME_BUTTON, &m_classic_overrider);
|
||||
auto& wcbg = WiimoteEmu::Classic::BUTTONS_GROUP;
|
||||
using WiimoteEmu::Classic;
|
||||
auto* const classic_a_button =
|
||||
CreateButton(QStringLiteral("&A"), wcbg, Classic::A_BUTTON, &m_classic_overrider);
|
||||
auto* const classic_b_button =
|
||||
CreateButton(QStringLiteral("&B"), wcbg, Classic::B_BUTTON, &m_classic_overrider);
|
||||
auto* const classic_x_button =
|
||||
CreateButton(QStringLiteral("&X"), wcbg, Classic::X_BUTTON, &m_classic_overrider);
|
||||
auto* const classic_y_button =
|
||||
CreateButton(QStringLiteral("&Y"), wcbg, Classic::Y_BUTTON, &m_classic_overrider);
|
||||
auto* const classic_zl_button =
|
||||
CreateButton(QStringLiteral("&ZL"), wcbg, Classic::ZL_BUTTON, &m_classic_overrider);
|
||||
auto* const classic_zr_button =
|
||||
CreateButton(QStringLiteral("ZR"), wcbg, Classic::ZR_BUTTON, &m_classic_overrider);
|
||||
auto* const classic_plus_button =
|
||||
CreateButton(QStringLiteral("&+"), wcbg, Classic::PLUS_BUTTON, &m_classic_overrider);
|
||||
auto* const classic_minus_button =
|
||||
CreateButton(QStringLiteral("&-"), wcbg, Classic::MINUS_BUTTON, &m_classic_overrider);
|
||||
auto* const classic_home_button =
|
||||
CreateButton(QStringLiteral("&HOME"), wcbg, Classic::HOME_BUTTON, &m_classic_overrider);
|
||||
|
||||
m_classic_l_button = CreateButton(QStringLiteral("&L"), WiimoteEmu::Classic::TRIGGERS_GROUP,
|
||||
auto* const classic_l_button =
|
||||
CreateButton(QStringLiteral("&L"), WiimoteEmu::Classic::TRIGGERS_GROUP,
|
||||
WiimoteEmu::Classic::L_DIGITAL, &m_classic_overrider);
|
||||
m_classic_r_button = CreateButton(QStringLiteral("&R"), WiimoteEmu::Classic::TRIGGERS_GROUP,
|
||||
auto* const classic_r_button =
|
||||
CreateButton(QStringLiteral("&R"), WiimoteEmu::Classic::TRIGGERS_GROUP,
|
||||
WiimoteEmu::Classic::R_DIGITAL, &m_classic_overrider);
|
||||
|
||||
m_classic_left_button = CreateButton(QStringLiteral("L&eft"), WiimoteEmu::Classic::DPAD_GROUP,
|
||||
DIRECTION_LEFT, &m_classic_overrider);
|
||||
m_classic_up_button = CreateButton(QStringLiteral("&Up"), WiimoteEmu::Classic::DPAD_GROUP,
|
||||
DIRECTION_UP, &m_classic_overrider);
|
||||
m_classic_down_button = CreateButton(QStringLiteral("&Down"), WiimoteEmu::Classic::DPAD_GROUP,
|
||||
DIRECTION_DOWN, &m_classic_overrider);
|
||||
m_classic_right_button = CreateButton(QStringLiteral("R&ight"), WiimoteEmu::Classic::DPAD_GROUP,
|
||||
DIRECTION_RIGHT, &m_classic_overrider);
|
||||
auto* const classic_left_button =
|
||||
CreateButton(QStringLiteral("L&eft"), WiimoteEmu::Classic::DPAD_GROUP, DIRECTION_LEFT,
|
||||
&m_classic_overrider);
|
||||
auto* const classic_up_button = CreateButton(
|
||||
QStringLiteral("&Up"), WiimoteEmu::Classic::DPAD_GROUP, DIRECTION_UP, &m_classic_overrider);
|
||||
auto* const classic_down_button =
|
||||
CreateButton(QStringLiteral("&Down"), WiimoteEmu::Classic::DPAD_GROUP, DIRECTION_DOWN,
|
||||
&m_classic_overrider);
|
||||
auto* const classic_right_button =
|
||||
CreateButton(QStringLiteral("R&ight"), WiimoteEmu::Classic::DPAD_GROUP, DIRECTION_RIGHT,
|
||||
&m_classic_overrider);
|
||||
|
||||
auto* classic_buttons_layout = new QGridLayout;
|
||||
classic_buttons_layout->addWidget(m_classic_a_button, 0, 0);
|
||||
classic_buttons_layout->addWidget(m_classic_b_button, 0, 1);
|
||||
classic_buttons_layout->addWidget(m_classic_x_button, 0, 2);
|
||||
classic_buttons_layout->addWidget(m_classic_y_button, 0, 3);
|
||||
classic_buttons_layout->addWidget(m_classic_l_button, 0, 4);
|
||||
classic_buttons_layout->addWidget(m_classic_r_button, 0, 5);
|
||||
classic_buttons_layout->addWidget(m_classic_zl_button, 0, 6);
|
||||
classic_buttons_layout->addWidget(m_classic_zr_button, 0, 7);
|
||||
classic_buttons_layout->addWidget(classic_a_button, 0, 0);
|
||||
classic_buttons_layout->addWidget(classic_b_button, 0, 1);
|
||||
classic_buttons_layout->addWidget(classic_x_button, 0, 2);
|
||||
classic_buttons_layout->addWidget(classic_y_button, 0, 3);
|
||||
classic_buttons_layout->addWidget(classic_l_button, 0, 4);
|
||||
classic_buttons_layout->addWidget(classic_r_button, 0, 5);
|
||||
classic_buttons_layout->addWidget(classic_zl_button, 0, 6);
|
||||
classic_buttons_layout->addWidget(classic_zr_button, 0, 7);
|
||||
|
||||
classic_buttons_layout->addWidget(m_classic_plus_button, 1, 0);
|
||||
classic_buttons_layout->addWidget(m_classic_minus_button, 1, 1);
|
||||
classic_buttons_layout->addWidget(m_classic_home_button, 1, 2);
|
||||
classic_buttons_layout->addWidget(m_classic_left_button, 1, 3);
|
||||
classic_buttons_layout->addWidget(m_classic_up_button, 1, 4);
|
||||
classic_buttons_layout->addWidget(m_classic_down_button, 1, 5);
|
||||
classic_buttons_layout->addWidget(m_classic_right_button, 1, 6);
|
||||
classic_buttons_layout->addWidget(classic_plus_button, 1, 0);
|
||||
classic_buttons_layout->addWidget(classic_minus_button, 1, 1);
|
||||
classic_buttons_layout->addWidget(classic_home_button, 1, 2);
|
||||
classic_buttons_layout->addWidget(classic_left_button, 1, 3);
|
||||
classic_buttons_layout->addWidget(classic_up_button, 1, 4);
|
||||
classic_buttons_layout->addWidget(classic_down_button, 1, 5);
|
||||
classic_buttons_layout->addWidget(classic_right_button, 1, 6);
|
||||
|
||||
classic_buttons_layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Expanding), 0, 8);
|
||||
|
||||
|
@ -349,21 +357,96 @@ WiiTASInputWindow::WiiTASInputWindow(QWidget* parent, int num) : TASInputWindow(
|
|||
setLayout(layout);
|
||||
}
|
||||
|
||||
BalanceBoardTASInputWindow::BalanceBoardTASInputWindow(QWidget* parent) : TASInputWindow{parent}
|
||||
{
|
||||
setWindowTitle(tr("Wii TAS Input %1 - Balance Board").arg(WIIMOTE_BALANCE_BOARD + 1));
|
||||
|
||||
const QKeySequence balance_tl_keyseq = QKeySequence(Qt::ALT | Qt::Key_L);
|
||||
const QKeySequence balance_tr_keyseq = QKeySequence(Qt::ALT | Qt::Key_R);
|
||||
const QKeySequence balance_bl_keyseq = QKeySequence(Qt::ALT | Qt::SHIFT | Qt::Key_L);
|
||||
const QKeySequence balance_br_keyseq = QKeySequence(Qt::ALT | Qt::SHIFT | Qt::Key_R);
|
||||
const QKeySequence balance_weight_keyseq = QKeySequence(Qt::ALT | Qt::Key_W);
|
||||
|
||||
auto* const balance_board_box =
|
||||
new QGroupBox(QStringLiteral("%1 (%2/%3/%4)")
|
||||
.arg(tr("Balance"), balance_tl_keyseq.toString(QKeySequence::NativeText),
|
||||
balance_tr_keyseq.toString(QKeySequence::NativeText),
|
||||
balance_weight_keyseq.toString(QKeySequence::NativeText)));
|
||||
SetQWidgetWindowDecorations(balance_board_box);
|
||||
|
||||
// How heavy do the TASers want to pretend to be?
|
||||
constexpr int max_total_kg = 136;
|
||||
// FYI, a lifted board shows about -4kg (the weight of the board itself).
|
||||
// But I don't think TASers necessarily need to simulate that.
|
||||
// They'd probably prefer the bottom of the slider be 0kg.
|
||||
constexpr int min_total_kg = 0;
|
||||
constexpr int max_sensor_value = max_total_kg;
|
||||
constexpr int min_sensor_value = min_total_kg / 4;
|
||||
|
||||
using BBExt = WiimoteEmu::BalanceBoardExt;
|
||||
auto& balance_group = BBExt::BALANCE_GROUP;
|
||||
auto* bal_top_layout = new QHBoxLayout;
|
||||
auto* const balance_value_tl = CreateWeightSliderValuePair(
|
||||
balance_group, BBExt::SENSOR_TL, &m_balance_board_overrider, bal_top_layout, min_sensor_value,
|
||||
max_sensor_value, balance_tl_keyseq, balance_board_box);
|
||||
auto* const balance_value_tr = CreateWeightSliderValuePair(
|
||||
balance_group, BBExt::SENSOR_TR, &m_balance_board_overrider, bal_top_layout, min_sensor_value,
|
||||
max_sensor_value, balance_tr_keyseq, balance_board_box);
|
||||
|
||||
auto* bal_bottom_layout = new QHBoxLayout;
|
||||
auto* const balance_value_bl = CreateWeightSliderValuePair(
|
||||
balance_group, BBExt::SENSOR_BL, &m_balance_board_overrider, bal_bottom_layout,
|
||||
min_sensor_value, max_sensor_value, balance_bl_keyseq, balance_board_box);
|
||||
auto* const balance_value_br = CreateWeightSliderValuePair(
|
||||
balance_group, BBExt::SENSOR_BR, &m_balance_board_overrider, bal_bottom_layout,
|
||||
min_sensor_value, max_sensor_value, balance_br_keyseq, balance_board_box);
|
||||
|
||||
auto* bal_weight_layout = new QHBoxLayout;
|
||||
auto* const total_weight_value = CreateWeightSliderValuePair(
|
||||
bal_weight_layout, min_total_kg, max_total_kg, balance_weight_keyseq, balance_board_box);
|
||||
|
||||
auto* bal_visual = new BalanceBoardWidget(
|
||||
this, total_weight_value,
|
||||
{balance_value_tr, balance_value_br, balance_value_tl, balance_value_bl});
|
||||
|
||||
auto* bal_ar = new AspectRatioWidget(bal_visual, 20, 12);
|
||||
bal_ar->setMinimumHeight(120);
|
||||
auto* bal_visual_layout = new QHBoxLayout;
|
||||
bal_visual_layout->addWidget(bal_ar);
|
||||
|
||||
auto* const bboard_power_button =
|
||||
CreateButton(QStringLiteral("&Power Button"), WiimoteEmu::BalanceBoard::BUTTONS_GROUP_NAME,
|
||||
WiimoteEmu::BalanceBoard::BUTTON_POWER_NAME, &m_wiimote_overrider);
|
||||
|
||||
auto* bal_layout = new QVBoxLayout;
|
||||
bal_layout->addLayout(bal_top_layout);
|
||||
bal_layout->addLayout(bal_visual_layout);
|
||||
bal_layout->addLayout(bal_bottom_layout);
|
||||
bal_layout->addLayout(bal_weight_layout);
|
||||
bal_layout->addWidget(bboard_power_button);
|
||||
|
||||
balance_board_box->setLayout(bal_layout);
|
||||
|
||||
auto* top_layout = new QHBoxLayout;
|
||||
top_layout->addWidget(balance_board_box);
|
||||
|
||||
auto* layout = new QVBoxLayout;
|
||||
layout->addLayout(top_layout);
|
||||
layout->addWidget(m_settings_box);
|
||||
|
||||
setLayout(layout);
|
||||
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
WiimoteEmu::Wiimote* WiiTASInputWindow::GetWiimote()
|
||||
{
|
||||
return static_cast<WiimoteEmu::Wiimote*>(Wiimote::GetConfig()->GetController(m_num));
|
||||
}
|
||||
|
||||
ControllerEmu::Attachments* WiiTASInputWindow::GetAttachments()
|
||||
{
|
||||
return static_cast<ControllerEmu::Attachments*>(
|
||||
GetWiimote()->GetWiimoteGroup(WiimoteEmu::WiimoteGroup::Attachments));
|
||||
}
|
||||
|
||||
WiimoteEmu::Extension* WiiTASInputWindow::GetExtension()
|
||||
{
|
||||
return static_cast<WiimoteEmu::Extension*>(
|
||||
GetAttachments()->GetAttachmentList()[m_active_extension].get());
|
||||
return GetWiimote()->GetActiveExtension();
|
||||
}
|
||||
|
||||
void WiiTASInputWindow::UpdateExtension(const int extension)
|
||||
|
@ -425,69 +508,59 @@ void WiiTASInputWindow::LoadExtensionAndMotionPlus()
|
|||
QueueOnObject(this, [this, attached] { UpdateMotionPlus(attached); });
|
||||
});
|
||||
m_attachment_callback_id =
|
||||
GetAttachments()->GetAttachmentSetting().AddCallback([this](const int extension_index) {
|
||||
wiimote->GetAttachmentSetting().AddCallback([this](const int extension_index) {
|
||||
QueueOnObject(this, [this, extension_index] { UpdateExtension(extension_index); });
|
||||
});
|
||||
}
|
||||
|
||||
void WiiTASInputWindow::UpdateControlVisibility()
|
||||
{
|
||||
m_ir_box->hide();
|
||||
m_remote_accelerometer_box->hide();
|
||||
m_remote_gyroscope_box->hide();
|
||||
m_nunchuk_buttons_box->hide();
|
||||
m_remote_buttons_box->hide();
|
||||
m_nunchuk_accelerometer_box->hide();
|
||||
m_nunchuk_stick_box->hide();
|
||||
m_classic_right_stick_box->hide();
|
||||
m_classic_left_stick_box->hide();
|
||||
m_classic_buttons_box->hide();
|
||||
m_triggers_box->hide();
|
||||
|
||||
const auto show_box = [](QGroupBox* box) {
|
||||
SetQWidgetWindowDecorations(box);
|
||||
box->show();
|
||||
};
|
||||
|
||||
if (m_active_extension == WiimoteEmu::ExtensionNumber::CLASSIC)
|
||||
{
|
||||
setWindowTitle(tr("Wii TAS Input %1 - Classic Controller").arg(m_num + 1));
|
||||
|
||||
show_box(m_classic_right_stick_box);
|
||||
show_box(m_classic_left_stick_box);
|
||||
show_box(m_triggers_box);
|
||||
show_box(m_classic_buttons_box);
|
||||
}
|
||||
else
|
||||
{
|
||||
show_box(m_ir_box);
|
||||
show_box(m_remote_accelerometer_box);
|
||||
show_box(m_remote_buttons_box);
|
||||
if (m_is_motion_plus_attached)
|
||||
show_box(m_remote_gyroscope_box);
|
||||
|
||||
if (m_active_extension == WiimoteEmu::ExtensionNumber::NUNCHUK)
|
||||
{
|
||||
setWindowTitle(tr("Wii TAS Input %1 - Wii Remote + Nunchuk").arg(m_num + 1));
|
||||
SetQWidgetWindowDecorations(m_ir_box);
|
||||
m_ir_box->show();
|
||||
SetQWidgetWindowDecorations(m_nunchuk_stick_box);
|
||||
m_nunchuk_stick_box->show();
|
||||
m_classic_right_stick_box->hide();
|
||||
m_classic_left_stick_box->hide();
|
||||
SetQWidgetWindowDecorations(m_remote_accelerometer_box);
|
||||
m_remote_accelerometer_box->show();
|
||||
m_remote_gyroscope_box->setVisible(m_is_motion_plus_attached);
|
||||
SetQWidgetWindowDecorations(m_nunchuk_accelerometer_box);
|
||||
m_nunchuk_accelerometer_box->show();
|
||||
m_triggers_box->hide();
|
||||
SetQWidgetWindowDecorations(m_nunchuk_buttons_box);
|
||||
m_nunchuk_buttons_box->show();
|
||||
SetQWidgetWindowDecorations(m_remote_buttons_box);
|
||||
m_remote_buttons_box->show();
|
||||
m_classic_buttons_box->hide();
|
||||
}
|
||||
else if (m_active_extension == WiimoteEmu::ExtensionNumber::CLASSIC)
|
||||
{
|
||||
setWindowTitle(tr("Wii TAS Input %1 - Classic Controller").arg(m_num + 1));
|
||||
m_ir_box->hide();
|
||||
m_nunchuk_stick_box->hide();
|
||||
SetQWidgetWindowDecorations(m_classic_right_stick_box);
|
||||
m_classic_right_stick_box->show();
|
||||
SetQWidgetWindowDecorations(m_classic_left_stick_box);
|
||||
m_classic_left_stick_box->show();
|
||||
m_remote_accelerometer_box->hide();
|
||||
m_remote_gyroscope_box->hide();
|
||||
m_nunchuk_accelerometer_box->hide();
|
||||
SetQWidgetWindowDecorations(m_triggers_box);
|
||||
m_triggers_box->show();
|
||||
m_remote_buttons_box->hide();
|
||||
m_nunchuk_buttons_box->hide();
|
||||
SetQWidgetWindowDecorations(m_classic_buttons_box);
|
||||
m_classic_buttons_box->show();
|
||||
|
||||
show_box(m_nunchuk_stick_box);
|
||||
show_box(m_nunchuk_accelerometer_box);
|
||||
show_box(m_nunchuk_buttons_box);
|
||||
}
|
||||
else
|
||||
{
|
||||
setWindowTitle(tr("Wii TAS Input %1 - Wii Remote").arg(m_num + 1));
|
||||
m_ir_box->show();
|
||||
m_nunchuk_stick_box->hide();
|
||||
m_classic_right_stick_box->hide();
|
||||
m_classic_left_stick_box->hide();
|
||||
SetQWidgetWindowDecorations(m_remote_accelerometer_box);
|
||||
m_remote_accelerometer_box->show();
|
||||
m_remote_gyroscope_box->setVisible(m_is_motion_plus_attached);
|
||||
m_nunchuk_accelerometer_box->hide();
|
||||
m_triggers_box->hide();
|
||||
SetQWidgetWindowDecorations(m_remote_buttons_box);
|
||||
m_remote_buttons_box->show();
|
||||
m_nunchuk_buttons_box->hide();
|
||||
m_classic_buttons_box->hide();
|
||||
}
|
||||
}
|
||||
|
||||
// Without these calls, switching between attachments can result in the Stick/IRWidgets being
|
||||
|
@ -501,10 +574,7 @@ void WiiTASInputWindow::hideEvent(QHideEvent* const event)
|
|||
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);
|
||||
}
|
||||
|
@ -516,6 +586,32 @@ void WiiTASInputWindow::showEvent(QShowEvent* const event)
|
|||
TASInputWindow::showEvent(event);
|
||||
}
|
||||
|
||||
WiimoteEmu::BalanceBoard* BalanceBoardTASInputWindow::GetBalanceBoard() const
|
||||
{
|
||||
return static_cast<WiimoteEmu::BalanceBoard*>(BalanceBoard::GetConfig()->GetController(0));
|
||||
}
|
||||
|
||||
void BalanceBoardTASInputWindow::hideEvent(QHideEvent* const event)
|
||||
{
|
||||
auto* const board = GetBalanceBoard();
|
||||
|
||||
board->ClearInputOverrideFunction();
|
||||
board->GetActiveExtension()->ClearInputOverrideFunction();
|
||||
|
||||
TASInputWindow::hideEvent(event);
|
||||
}
|
||||
|
||||
void BalanceBoardTASInputWindow::showEvent(QShowEvent* const event)
|
||||
{
|
||||
auto* const board = GetBalanceBoard();
|
||||
|
||||
board->SetInputOverrideFunction(m_wiimote_overrider.GetInputOverrideFunction());
|
||||
board->GetActiveExtension()->SetInputOverrideFunction(
|
||||
m_balance_board_overrider.GetInputOverrideFunction());
|
||||
|
||||
TASInputWindow::showEvent(event);
|
||||
}
|
||||
|
||||
void WiiTASInputWindow::UpdateInputOverrideFunction()
|
||||
{
|
||||
WiimoteEmu::Wiimote* const wiimote = GetWiimote();
|
||||
|
|
|
@ -18,13 +18,9 @@ namespace WiimoteEmu
|
|||
{
|
||||
class Extension;
|
||||
class Wiimote;
|
||||
class BalanceBoard;
|
||||
} // namespace WiimoteEmu
|
||||
|
||||
namespace ControllerEmu
|
||||
{
|
||||
class Attachments;
|
||||
}
|
||||
|
||||
class WiiTASInputWindow : public TASInputWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -39,7 +35,6 @@ public:
|
|||
|
||||
private:
|
||||
WiimoteEmu::Wiimote* GetWiimote();
|
||||
ControllerEmu::Attachments* GetAttachments();
|
||||
WiimoteEmu::Extension* GetExtension();
|
||||
|
||||
void LoadExtensionAndMotionPlus();
|
||||
|
@ -56,36 +51,6 @@ private:
|
|||
InputOverrider m_nunchuk_overrider;
|
||||
InputOverrider m_classic_overrider;
|
||||
|
||||
TASCheckBox* m_a_button;
|
||||
TASCheckBox* m_b_button;
|
||||
TASCheckBox* m_1_button;
|
||||
TASCheckBox* m_2_button;
|
||||
TASCheckBox* m_plus_button;
|
||||
TASCheckBox* m_minus_button;
|
||||
TASCheckBox* m_home_button;
|
||||
TASCheckBox* m_left_button;
|
||||
TASCheckBox* m_up_button;
|
||||
TASCheckBox* m_down_button;
|
||||
TASCheckBox* m_right_button;
|
||||
TASCheckBox* m_c_button;
|
||||
TASCheckBox* m_z_button;
|
||||
TASCheckBox* m_classic_a_button;
|
||||
TASCheckBox* m_classic_b_button;
|
||||
TASCheckBox* m_classic_x_button;
|
||||
TASCheckBox* m_classic_y_button;
|
||||
TASCheckBox* m_classic_plus_button;
|
||||
TASCheckBox* m_classic_minus_button;
|
||||
TASCheckBox* m_classic_l_button;
|
||||
TASCheckBox* m_classic_r_button;
|
||||
TASCheckBox* m_classic_zl_button;
|
||||
TASCheckBox* m_classic_zr_button;
|
||||
TASCheckBox* m_classic_home_button;
|
||||
TASCheckBox* m_classic_left_button;
|
||||
TASCheckBox* m_classic_up_button;
|
||||
TASCheckBox* m_classic_down_button;
|
||||
TASCheckBox* m_classic_right_button;
|
||||
TASSpinBox* m_ir_x_value;
|
||||
TASSpinBox* m_ir_y_value;
|
||||
QGroupBox* m_remote_accelerometer_box;
|
||||
QGroupBox* m_remote_gyroscope_box;
|
||||
QGroupBox* m_nunchuk_accelerometer_box;
|
||||
|
@ -98,3 +63,18 @@ private:
|
|||
QGroupBox* m_classic_buttons_box;
|
||||
QGroupBox* m_triggers_box;
|
||||
};
|
||||
|
||||
class BalanceBoardTASInputWindow : public TASInputWindow
|
||||
{
|
||||
public:
|
||||
explicit BalanceBoardTASInputWindow(QWidget* parent);
|
||||
|
||||
private:
|
||||
void hideEvent(QHideEvent* event) override;
|
||||
void showEvent(QShowEvent* event) override;
|
||||
|
||||
WiimoteEmu::BalanceBoard* GetBalanceBoard() const;
|
||||
|
||||
InputOverrider m_wiimote_overrider;
|
||||
InputOverrider m_balance_board_overrider;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue