|
|
@@ -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 |