Large diffs are not rendered by default.

@@ -0,0 +1,270 @@
// Copyright 2020 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#pragma once

#include <array>
#include <chrono>
#include <memory>
#include <vector>

#include "Core/HW/WiimoteCommon/DataReport.h"
#include "Core/HW/WiimoteCommon/WiimoteReport.h"
#include "Core/HW/WiimoteEmu/Camera.h"
#include "Core/HW/WiimoteEmu/Extension/Classic.h"
#include "Core/HW/WiimoteEmu/Extension/Nunchuk.h"
#include "Core/HW/WiimoteEmu/MotionPlus.h"
#include "Core/HW/WiimoteReal/WiimoteReal.h"
#include "InputCommon/ControllerInterface/Device.h"

namespace ciface::Wiimote
{
using namespace WiimoteCommon;

void AddDevice(std::unique_ptr<WiimoteReal::Wiimote>);
void ReleaseDevices(std::optional<u32> count = std::nullopt);

class Device final : public Core::Device
{
public:
Device(std::unique_ptr<WiimoteReal::Wiimote> wiimote);
~Device();

std::string GetName() const override;
std::string GetSource() const override;

void UpdateInput() override;

private:
using Clock = std::chrono::steady_clock;

enum class ExtensionID
{
Nunchuk,
Classic,
Unsupported,
};

class MotionPlusState
{
public:
void SetCalibrationData(const WiimoteEmu::MotionPlus::CalibrationData&);
void ProcessData(const WiimoteEmu::MotionPlus::DataFormat&);

using PassthroughMode = WiimoteEmu::MotionPlus::PassthroughMode;

// State is unknown by default.
std::optional<PassthroughMode> current_mode;

// The last known state of the passthrough port flag.
// Used to detect passthrough extension port events.
std::optional<bool> passthrough_port;

Common::Vec3 gyro_data = {};

std::optional<WiimoteEmu::MotionPlus::CalibrationBlocks> calibration;
};

struct NunchukState
{
using CalibrationData = WiimoteEmu::Nunchuk::CalibrationData;

void SetCalibrationData(const CalibrationData&);
void ProcessData(const WiimoteEmu::Nunchuk::DataFormat&);

Common::Vec2 stick = {};
Common::Vec3 accel = {};

u8 buttons = 0;

struct Calibration
{
CalibrationData::AccelCalibration accel;
CalibrationData::StickCalibration stick;
};

std::optional<Calibration> calibration;
};

struct ClassicState
{
using CalibrationData = WiimoteEmu::Classic::CalibrationData;

void SetCalibrationData(const CalibrationData&);
void ProcessData(const WiimoteEmu::Classic::DataFormat&);

std::array<Common::Vec2, 2> sticks = {};
std::array<float, 2> triggers = {};

u16 buttons = 0;

struct Calibration
{
CalibrationData::StickCalibration left_stick;
CalibrationData::StickCalibration right_stick;

CalibrationData::TriggerCalibration left_trigger;
CalibrationData::TriggerCalibration right_trigger;
};

std::optional<Calibration> calibration;
};

struct IRState
{
static u32 GetDesiredIRSensitivity();

void ProcessData(const std::array<WiimoteEmu::IRBasic, 2>&);
bool IsFullyConfigured() const;

u32 current_sensitivity = u32(-1);
bool enabled = false;
bool mode_set = false;

// Average of visible IR "objects".
Common::Vec2 center_position = {};

bool is_hidden = true;
};

class ReportHandler
{
public:
enum class HandlerResult
{
Handled,
NotHandled,
};

ReportHandler(Clock::time_point expired_time);

template <typename R, typename T>
void AddHandler(std::function<R(const T&)>);

HandlerResult TryToHandleReport(const WiimoteReal::Report& report);

bool IsExpired() const;

private:
const Clock::time_point m_expired_time;
std::vector<std::function<HandlerResult(const WiimoteReal::Report& report)>> m_callbacks;
};

using AckReportHandler = std::function<ReportHandler::HandlerResult(const InputReportAck& reply)>;

static AckReportHandler MakeAckHandler(OutputReportID report_id,
std::function<void(WiimoteCommon::ErrorCode)> callback);

// TODO: Make parameter const. (need to modify DataReportManipulator)
void ProcessInputReport(WiimoteReal::Report& report);
void ProcessMotionPlusExtensionData(const u8* data, u32 size);
void ProcessNormalExtensionData(const u8* data, u32 size);
void ProcessExtensionEvent(bool connected);
void ProcessExtensionID(u8 id_0, u8 id_4, u8 id_5);
void ProcessStatusReport(const InputReportStatus&);

void RunTasks();

bool IsPerformingTask() const;

template <typename T>
void QueueReport(T&& report, std::function<void(ErrorCode)> ack_callback = {});

template <typename... T>
void AddReportHandler(T&&... callbacks);

using ReadResponse = std::optional<std::vector<u8>>;

void ReadData(AddressSpace space, u8 slave, u16 address, u16 size,
std::function<void(ReadResponse)> callback);

void AddReadDataReplyHandler(AddressSpace space, u8 slave, u16 address, u16 size,
std::vector<u8> starting_data,
std::function<void(ReadResponse)> callback);

template <typename T = std::initializer_list<u8>, typename C>
void WriteData(AddressSpace space, u8 slave, u16 address, T&& data, C&& callback);

void ReadActiveExtensionID();
void SetIRSensitivity(u32 level);
void ConfigureSpeaker();
void ConfigureIRCamera();

u8 GetDesiredLEDValue() const;

void TriggerMotionPlusModeChange();
void TriggerMotionPlusCalibration();

bool IsMotionPlusStateKnown() const;
bool IsMotionPlusActive() const;
bool IsMotionPlusInDesiredMode() const;

bool IsWaitingForMotionPlus() const;
void WaitForMotionPlus();
void HandleMotionPlusNonResponse();

void UpdateRumble();
void UpdateOrientation();
void UpdateExtensionNumberInput();

std::unique_ptr<WiimoteReal::Wiimote> m_wiimote;

// Buttons.
DataReportManipulator::CoreData m_core_data = {};

// Accelerometer.
Common::Vec3 m_accel_data = {};
std::optional<AccelCalibrationData::Calibration> m_accel_calibration;

// Pitch, Roll, Yaw inputs.
Common::Vec3 m_rotation_inputs = {};

MotionPlusState m_mplus_state = {};
NunchukState m_nunchuk_state = {};
ClassicState m_classic_state = {};
IRState m_ir_state = {};

// Used to poll for M+ periodically and wait for it to reset.
Clock::time_point m_mplus_wait_time = Clock::now();

// The desired mode is set based on the attached normal extension.
std::optional<MotionPlusState::PassthroughMode> m_mplus_desired_mode;

// Status report is requested every so often to update the battery level.
Clock::time_point m_status_outdated_time = Clock::now();
u8 m_battery = 0;
u8 m_leds = 0;

bool m_speaker_configured = false;

// The last known state of the extension port status flag.
// Used to detect extension port events.
std::optional<bool> m_extension_port;

// Note this refers to the passthrough extension when M+ is active.
std::optional<ExtensionID> m_extension_id;

// Rumble state must be saved to set the proper flag in every output report.
bool m_rumble = false;

// For pulse of rumble motor to simulate multiple levels.
ControlState m_rumble_level = 0;
Clock::time_point m_last_rumble_change = Clock::now();

// Assume mode is disabled so one gets set.
InputReportID m_reporting_mode = InputReportID::ReportDisabled;

// Used only to provide a value for a specialty "input". (for attached extension passthrough)
WiimoteEmu::ExtensionNumber m_extension_number_input = WiimoteEmu::ExtensionNumber::NONE;
bool m_mplus_attached_input = false;

// Holds callbacks for output report replies.
std::list<ReportHandler> m_report_handlers;

// World rotation. (used to rotate IR data and provide pitch, roll, yaw inputs)
Common::Matrix33 m_orientation = Common::Matrix33::Identity();
Clock::time_point m_last_report_time = Clock::now();
};

} // namespace ciface::Wiimote
@@ -75,6 +75,7 @@
<ClCompile Include="ControlReference\ExpressionParser.cpp" />
<ClCompile Include="ControllerInterface\ForceFeedback\ForceFeedbackDevice.cpp" />
<ClCompile Include="ControllerInterface\Win32\Win32.cpp" />
<ClCompile Include="ControllerInterface\Wiimote\Wiimote.cpp" />
<ClCompile Include="ControllerInterface\XInput\XInput.cpp" />
<ClCompile Include="ControlReference\FunctionExpression.cpp" />
<ClCompile Include="GCAdapter.cpp">
@@ -122,6 +123,7 @@
<ClInclude Include="ControlReference\ExpressionParser.h" />
<ClInclude Include="ControllerInterface\ForceFeedback\ForceFeedbackDevice.h" />
<ClInclude Include="ControllerInterface\Win32\Win32.h" />
<ClInclude Include="ControllerInterface\Wiimote\Wiimote.h" />
<ClInclude Include="ControllerInterface\XInput\XInput.h" />
<ClInclude Include="GCAdapter.h" />
<ClInclude Include="GCPadStatus.h" />
@@ -139,4 +141,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>
@@ -110,6 +110,9 @@
<ClCompile Include="ControllerInterface\Win32\Win32.cpp">
<Filter>ControllerInterface\Win32</Filter>
</ClCompile>
<ClCompile Include="ControllerInterface\Wiimote\Wiimote.cpp">
<Filter>ControllerInterface\Wiimote</Filter>
</ClCompile>
<ClCompile Include="ControlReference\ExpressionParser.cpp">
<Filter>ControllerInterface</Filter>
</ClCompile>
@@ -218,6 +221,9 @@
<ClInclude Include="ControllerInterface\Win32\Win32.h">
<Filter>ControllerInterface\Win32</Filter>
</ClInclude>
<ClInclude Include="ControllerInterface\Wiimote\Wiimote.h">
<Filter>ControllerInterface\Wiimote</Filter>
</ClInclude>
<ClInclude Include="ControlReference\ExpressionParser.h">
<Filter>ControllerInterface</Filter>
</ClInclude>
@@ -248,4 +254,4 @@
<ItemGroup>
<Text Include="CMakeLists.txt" />
</ItemGroup>
</Project>
</Project>