Add all the files

This commit is contained in:
Exzap 2022-08-22 22:21:23 +02:00
parent e3db07a16a
commit d60742f52b
1445 changed files with 430238 additions and 0 deletions

View file

@ -0,0 +1,258 @@
#include "input/emulated/ClassicController.h"
#include "input/api/Controller.h"
#include "input/api/SDL/SDLController.h"
ClassicController::ClassicController(size_t player_index)
: WPADController(player_index, kDataFormat_CLASSIC)
{
}
uint32 ClassicController::get_emulated_button_flag(uint32 id) const
{
return s_get_emulated_button_flag(id);
}
uint32 ClassicController::s_get_emulated_button_flag(uint32 id)
{
switch (id)
{
case kButtonId_A:
return kCLButton_A;
case kButtonId_B:
return kCLButton_B;
case kButtonId_X:
return kCLButton_X;
case kButtonId_Y:
return kCLButton_Y;
case kButtonId_Plus:
return kCLButton_Plus;
case kButtonId_Minus:
return kCLButton_Minus;
case kButtonId_Up:
return kCLButton_Up;
case kButtonId_Down:
return kCLButton_Down;
case kButtonId_Left:
return kCLButton_Left;
case kButtonId_Right:
return kCLButton_Right;
case kButtonId_L:
return kCLButton_L;
case kButtonId_ZL:
return kCLButton_ZL;
case kButtonId_R:
return kCLButton_R;
case kButtonId_ZR:
return kCLButton_ZR;
}
return 0;
}
std::string_view ClassicController::get_button_name(ButtonId id)
{
switch (id)
{
case kButtonId_A: return "A";
case kButtonId_B: return "B";
case kButtonId_X: return "X";
case kButtonId_Y: return "Y";
case kButtonId_L: return "L";
case kButtonId_R: return "R";
case kButtonId_ZL: return "ZL";
case kButtonId_ZR: return "ZR";
case kButtonId_Plus: return "+";
case kButtonId_Minus: return "-";
case kButtonId_Home: return "home";
case kButtonId_Up: return "up";
case kButtonId_Down: return "down";
case kButtonId_Left: return "left";
case kButtonId_Right: return "right";
case kButtonId_StickL_Up: return "up";
case kButtonId_StickL_Down: return "down";
case kButtonId_StickL_Left: return "left";
case kButtonId_StickL_Right: return "right";
case kButtonId_StickR_Up: return "up";
case kButtonId_StickR_Down: return "down";
case kButtonId_StickR_Left: return "left";
case kButtonId_StickR_Right: return "right";
default:
return "";
}
}
glm::vec2 ClassicController::get_axis() const
{
const auto left = get_axis_value(kButtonId_StickL_Left);
const auto right = get_axis_value(kButtonId_StickL_Right);
const auto up = get_axis_value(kButtonId_StickL_Up);
const auto down = get_axis_value(kButtonId_StickL_Down);
glm::vec2 result;
result.x = (left > right) ? -left : right;
result.y = (up > down) ? up : -down;
return length(result) > 1.0f ? normalize(result) : result;
}
glm::vec2 ClassicController::get_rotation() const
{
const auto left = get_axis_value(kButtonId_StickR_Left);
const auto right = get_axis_value(kButtonId_StickR_Right);
const auto up = get_axis_value(kButtonId_StickR_Up);
const auto down = get_axis_value(kButtonId_StickR_Down);
glm::vec2 result;
result.x = (left > right) ? -left : right;
result.y = (up > down) ? up : -down;
return length(result) > 1.0f ? normalize(result) : result;
}
glm::vec2 ClassicController::get_trigger() const
{
const auto left = get_axis_value(kButtonId_ZL);
const auto right = get_axis_value(kButtonId_ZR);
return { left, right };
}
bool ClassicController::set_default_mapping(const std::shared_ptr<ControllerBase>& controller)
{
std::vector<std::pair<uint64, uint64>> mapping;
switch (controller->api())
{
case InputAPI::SDLController: {
const auto sdl_controller = std::static_pointer_cast<SDLController>(controller);
if (sdl_controller->get_guid() == SDLController::kLeftJoyCon)
{
mapping =
{
{kButtonId_L, kButton9},
{kButtonId_ZL, kTriggerXP},
{kButtonId_Minus, kButton4},
{kButtonId_Up, kButton11},
{kButtonId_Down, kButton12},
{kButtonId_Left, kButton13},
{kButtonId_Right, kButton14},
{kButtonId_StickL_Up, kAxisYN},
{kButtonId_StickL_Down, kAxisYP},
{kButtonId_StickL_Left, kAxisXN},
{kButtonId_StickL_Right, kAxisXP},
};
}
else if (sdl_controller->get_guid() == SDLController::kRightJoyCon)
{
mapping =
{
{kButtonId_A, kButton0},
{kButtonId_B, kButton1},
{kButtonId_X, kButton2},
{kButtonId_Y, kButton3},
{kButtonId_R, kButton10},
{kButtonId_ZR, kTriggerYP},
{kButtonId_Plus, kButton6},
{kButtonId_StickR_Up, kRotationYN},
{kButtonId_StickR_Down, kRotationYP},
{kButtonId_StickR_Left, kRotationXN},
{kButtonId_StickR_Right, kRotationXP},
};
}
else
{
mapping =
{
{kButtonId_A, kButton1},
{kButtonId_B, kButton0},
{kButtonId_X, kButton3},
{kButtonId_Y, kButton2},
{kButtonId_L, kButton9},
{kButtonId_R, kButton10},
{kButtonId_ZL, kTriggerXP},
{kButtonId_ZR, kTriggerYP},
{kButtonId_Plus, kButton6},
{kButtonId_Minus, kButton4},
{kButtonId_Up, kButton11},
{kButtonId_Down, kButton12},
{kButtonId_Left, kButton13},
{kButtonId_Right, kButton14},
{kButtonId_StickL_Up, kAxisYN},
{kButtonId_StickL_Down, kAxisYP},
{kButtonId_StickL_Left, kAxisXN},
{kButtonId_StickL_Right, kAxisXP},
{kButtonId_StickR_Up, kRotationYN},
{kButtonId_StickR_Down, kRotationYP},
{kButtonId_StickR_Left, kRotationXN},
{kButtonId_StickR_Right, kRotationXP},
};
}
}
case InputAPI::XInput:
{
mapping =
{
{kButtonId_A, kButton13},
{kButtonId_B, kButton12},
{kButtonId_X, kButton15},
{kButtonId_Y, kButton14},
{kButtonId_L, kButton8},
{kButtonId_R, kButton9},
{kButtonId_ZL, kTriggerXP},
{kButtonId_ZR, kTriggerYP},
{kButtonId_Plus, kButton4},
{kButtonId_Minus, kButton5},
{kButtonId_Up, kButton0},
{kButtonId_Down, kButton1},
{kButtonId_Left, kButton2},
{kButtonId_Right, kButton3},
{kButtonId_StickL_Up, kAxisYP},
{kButtonId_StickL_Down, kAxisYN},
{kButtonId_StickL_Left, kAxisXN},
{kButtonId_StickL_Right, kAxisXP},
{kButtonId_StickR_Up, kRotationYP},
{kButtonId_StickR_Down, kRotationYN},
{kButtonId_StickR_Left, kRotationXN},
{kButtonId_StickR_Right, kRotationXP},
};
break;
}
}
bool mapping_updated = false;
std::for_each(mapping.cbegin(), mapping.cend(), [this, &controller, &mapping_updated](const auto& m)
{
if (m_mappings.find(m.first) == m_mappings.cend())
{
set_mapping(m.first, controller, m.second);
mapping_updated = true;
}
});
return mapping_updated;
}

View file

@ -0,0 +1,72 @@
#pragma once
#include "input/emulated/WPADController.h"
class ClassicController : public WPADController
{
public:
enum ButtonId
{
kButtonId_None,
kButtonId_A,
kButtonId_B,
kButtonId_X,
kButtonId_Y,
kButtonId_L,
kButtonId_R,
kButtonId_ZL,
kButtonId_ZR,
kButtonId_Plus,
kButtonId_Minus,
kButtonId_Home,
kButtonId_Up,
kButtonId_Down,
kButtonId_Left,
kButtonId_Right,
kButtonId_StickL_Up,
kButtonId_StickL_Down,
kButtonId_StickL_Left,
kButtonId_StickL_Right,
kButtonId_StickR_Up,
kButtonId_StickR_Down,
kButtonId_StickR_Left,
kButtonId_StickR_Right,
kButtonId_Max,
};
ClassicController(size_t player_index);
Type type() const override { return Type::Classic; }
WPADDeviceType get_device_type() const override { return kWAPDevClassic; }
uint32 get_emulated_button_flag(uint32 id) const override;
size_t get_highest_mapping_id() const override { return kButtonId_Max; }
bool is_axis_mapping(uint64 mapping) const override { return mapping >= kButtonId_StickL_Up && mapping <= kButtonId_StickR_Right; }
glm::vec2 get_axis() const override;
glm::vec2 get_rotation() const override;
glm::vec2 get_trigger() const override;
static uint32 s_get_emulated_button_flag(uint32 id);
static std::string_view get_button_name(ButtonId id);
bool is_start_down() const override { return is_mapping_down(kButtonId_Plus); }
bool is_left_down() const override { return is_mapping_down(kButtonId_Left); }
bool is_right_down() const override { return is_mapping_down(kButtonId_Right); }
bool is_up_down() const override { return is_mapping_down(kButtonId_Up); }
bool is_down_down() const override { return is_mapping_down(kButtonId_Down); }
bool is_a_down() const override { return is_mapping_down(kButtonId_A); }
bool is_b_down() const override { return is_mapping_down(kButtonId_B); }
bool is_home_down() const override { return is_mapping_down(kButtonId_Home); }
bool set_default_mapping(const std::shared_ptr<ControllerBase>& controller) override;
};

View file

@ -0,0 +1,341 @@
#include "input/emulated/EmulatedController.h"
#include "input/api/Controller.h"
#if BOOST_OS_WINDOWS
#include "input/api/Wiimote/NativeWiimoteController.h"
#endif
std::string_view EmulatedController::type_to_string(Type type)
{
switch (type)
{
case VPAD: return "Wii U GamePad";
case Pro: return "Wii U Pro Controller";
case Classic: return "Wii U Classic Controller";
case Wiimote: return "Wiimote";
}
throw std::runtime_error(fmt::format("unknown emulated controller: {}", to_underlying(type)));
}
EmulatedController::Type EmulatedController::type_from_string(std::string_view str)
{
if (str == "Wii U GamePad")
return VPAD;
else if (str == "Wii U Pro Controller")
return Pro;
else if (str == "Wii U Classic Controller")
return Classic;
else if (str == "Wiimote")
return Wiimote;
throw std::runtime_error(fmt::format("unknown emulated controller: {}", str));
}
EmulatedController::EmulatedController(size_t player_index)
: m_player_index(player_index)
{
}
void EmulatedController::calibrate()
{
std::shared_lock lock(m_mutex);
for (const auto& controller : m_controllers)
{
controller->calibrate();
}
}
void EmulatedController::connect()
{
std::shared_lock lock(m_mutex);
for (const auto& controller : m_controllers)
{
controller->connect();
}
}
void EmulatedController::update()
{
std::shared_lock lock(m_mutex);
for(const auto& controller : m_controllers)
{
controller->update();
}
}
void EmulatedController::controllers_update_states()
{
std::shared_lock lock(m_mutex);
for (const auto& controller : m_controllers)
{
controller->update_state();
}
}
void EmulatedController::start_rumble()
{
m_rumble = true;
std::shared_lock lock(m_mutex);
for (const auto& controller : m_controllers)
{
controller->start_rumble();
}
}
void EmulatedController::stop_rumble()
{
if (!m_rumble)
return;
m_rumble = false;
std::shared_lock lock(m_mutex);
for (const auto& controller : m_controllers)
{
controller->stop_rumble();
}
}
bool EmulatedController::is_battery_low() const
{
std::shared_lock lock(m_mutex);
return std::any_of(m_controllers.cbegin(), m_controllers.cend(), [](const auto& c) {return c->has_low_battery(); });
}
bool EmulatedController::has_motion() const
{
std::shared_lock lock(m_mutex);
return std::any_of(m_controllers.cbegin(), m_controllers.cend(), [](const auto& c) {return c->use_motion(); });
}
MotionSample EmulatedController::get_motion_data() const
{
std::shared_lock lock(m_mutex);
for (const auto& controller : m_controllers)
{
if (controller->use_motion())
return controller->get_motion_sample();
}
return {};
}
bool EmulatedController::has_second_motion() const
{
int motion = 0;
std::shared_lock lock(m_mutex);
for(const auto& controller : m_controllers)
{
if(controller->use_motion())
{
// if wiimote has nunchuck connected, we use its acceleration
#if BOOST_OS_WINDOWS
if(controller->api() == InputAPI::Wiimote)
{
if(((NativeWiimoteController*)controller.get())->get_extension() == NativeWiimoteController::Nunchuck)
{
return true;
}
}
#endif
motion++;
}
}
return motion >= 2;
}
MotionSample EmulatedController::get_second_motion_data() const
{
int motion = 0;
std::shared_lock lock(m_mutex);
for (const auto& controller : m_controllers)
{
if (controller->use_motion())
{
// if wiimote has nunchuck connected, we use its acceleration
#if BOOST_OS_WINDOWS
if (controller->api() == InputAPI::Wiimote)
{
if (((NativeWiimoteController*)controller.get())->get_extension() == NativeWiimoteController::Nunchuck)
{
return ((NativeWiimoteController*)controller.get())->get_nunchuck_motion_sample();
}
}
#endif
motion++;
if(motion == 2)
{
return controller->get_motion_sample();
}
}
}
return {};
}
bool EmulatedController::has_position() const
{
std::shared_lock lock(m_mutex);
return std::any_of(m_controllers.cbegin(), m_controllers.cend(), [](const auto& c) {return c->has_position(); });
}
glm::vec2 EmulatedController::get_position() const
{
std::shared_lock lock(m_mutex);
for (const auto& controller : m_controllers)
{
if (controller->has_position())
return controller->get_position();
}
return {};
}
glm::vec2 EmulatedController::get_prev_position() const
{
std::shared_lock lock(m_mutex);
for (const auto& controller : m_controllers)
{
if (controller->has_position())
return controller->get_prev_position();
}
return {};
}
std::shared_ptr<ControllerBase> EmulatedController::find_controller(std::string_view uuid, InputAPI::Type type) const
{
std::scoped_lock lock(m_mutex);
const auto it = std::find_if(m_controllers.cbegin(), m_controllers.cend(), [uuid, type](const auto& c) { return c->api() == type && c->uuid() == uuid; });
if (it != m_controllers.cend())
return *it;
return {};
}
void EmulatedController::add_controller(std::shared_ptr<ControllerBase> controller)
{
controller->connect();
#if BOOST_OS_WINDOWS
if (const auto wiimote = std::dynamic_pointer_cast<NativeWiimoteController>(controller)) {
wiimote->set_player_index(m_player_index);
}
#endif
std::scoped_lock lock(m_mutex);
m_controllers.emplace_back(std::move(controller));
}
void EmulatedController::remove_controller(const std::shared_ptr<ControllerBase>& controller)
{
std::scoped_lock lock(m_mutex);
const auto it = std::find(m_controllers.cbegin(), m_controllers.cend(), controller);
if (it != m_controllers.cend())
{
m_controllers.erase(it);
for(auto m = m_mappings.begin(); m != m_mappings.end();)
{
if(auto mc = m->second.controller.lock())
{
if(*mc == *controller)
{
m = m_mappings.erase(m);
continue;
}
}
++m;
}
}
}
void EmulatedController::clear_controllers()
{
std::scoped_lock lock(m_mutex);
m_controllers.clear();
}
float EmulatedController::get_axis_value(uint64 mapping) const
{
const auto it = m_mappings.find(mapping);
if (it != m_mappings.cend())
{
if (const auto controller = it->second.controller.lock()) {
return controller->get_axis_value(it->second.button);
}
}
return 0;
}
bool EmulatedController::is_mapping_down(uint64 mapping) const
{
const auto it = m_mappings.find(mapping);
if (it != m_mappings.cend())
{
if (const auto controller = it->second.controller.lock()) {
return controller->get_state().buttons.test(it->second.button);
}
}
return false;
}
std::string EmulatedController::get_mapping_name(uint64 mapping) const
{
const auto it = m_mappings.find(mapping);
if (it != m_mappings.cend())
{
if (const auto controller = it->second.controller.lock()) {
return controller->get_button_name(it->second.button);
}
}
return {};
}
std::shared_ptr<ControllerBase> EmulatedController::get_mapping_controller(uint64 mapping) const
{
const auto it = m_mappings.find(mapping);
if (it != m_mappings.cend())
{
if (const auto controller = it->second.controller.lock()) {
return controller;
}
}
return {};
}
void EmulatedController::delete_mapping(uint64 mapping)
{
m_mappings.erase(mapping);
}
void EmulatedController::clear_mappings()
{
m_mappings.clear();
}
void EmulatedController::set_mapping(uint64 mapping, const std::shared_ptr<ControllerBase>& controller,
uint64 button)
{
m_mappings[mapping] = { controller, button };
}
bool EmulatedController::operator==(const EmulatedController& o) const
{
return type() == o.type() && m_player_index == o.m_player_index;
}
bool EmulatedController::operator!=(const EmulatedController& o) const
{
return !(*this == o);
}

View file

@ -0,0 +1,140 @@
#pragma once
#include <pugixml.hpp>
class ControllerBase;
#include "input/motion/MotionSample.h"
#include "input/api/ControllerState.h"
#include "input/api/InputAPI.h"
#include "util/helpers/helpers.h"
// mapping = wii u controller button id
// button = api button id
class EmulatedController
{
friend class InputManager;
public:
EmulatedController(size_t player_index);
virtual ~EmulatedController() = default;
virtual void load(const pugi::xml_node& node){};
virtual void save(pugi::xml_node& node){};
enum Type
{
VPAD,
Pro,
Classic,
Wiimote,
MAX
};
virtual Type type() const = 0;
std::string_view type_string() const { return type_to_string(type()); }
static std::string_view type_to_string(Type type);
static Type type_from_string(std::string_view str);
size_t player_index() const { return m_player_index; }
const std::string& get_profile_name() const { return m_profile_name; }
bool has_profile_name() const { return !m_profile_name.empty() && m_profile_name != "default"; }
void calibrate();
void connect();
virtual void update();
void controllers_update_states();
virtual glm::vec2 get_axis() const = 0;
virtual glm::vec2 get_rotation() const = 0;
virtual glm::vec2 get_trigger() const = 0;
void start_rumble();
void stop_rumble();
bool is_battery_low() const;
bool has_motion() const;
MotionSample get_motion_data() const;
// some controllers (nunchuck) provide extra motion data
bool has_second_motion() const;
MotionSample get_second_motion_data() const;
bool has_position() const;
glm::vec2 get_position() const;
glm::vec2 get_prev_position() const;
std::shared_ptr<ControllerBase> find_controller(std::string_view uuid, InputAPI::Type type) const;
void add_controller(std::shared_ptr<ControllerBase> controller);
void remove_controller(const std::shared_ptr<ControllerBase>& controller);
void clear_controllers();
const std::vector<std::shared_ptr<ControllerBase>>& get_controllers() const { return m_controllers; }
bool is_mapping_down(uint64 mapping) const;
std::string get_mapping_name(uint64 mapping) const;
std::shared_ptr<ControllerBase> get_mapping_controller(uint64 mapping) const;
void delete_mapping(uint64 mapping);
void clear_mappings();
void set_mapping(uint64 mapping, const std::shared_ptr<ControllerBase>& controller_base, uint64 button);
virtual uint32 get_emulated_button_flag(uint32 mapping) const = 0;
bool operator==(const EmulatedController& o) const;
bool operator!=(const EmulatedController& o) const;
virtual size_t get_highest_mapping_id() const = 0;
virtual bool is_axis_mapping(uint64 mapping) const = 0;
virtual bool is_start_down() const = 0;
virtual bool is_left_down() const = 0;
virtual bool is_right_down() const = 0;
virtual bool is_up_down() const = 0;
virtual bool is_down_down() const = 0;
virtual bool is_a_down() const = 0;
virtual bool is_b_down() const = 0;
virtual bool is_home_down() const = 0;
bool was_home_button_down() { return std::exchange(m_homebutton_down, false); }
virtual bool set_default_mapping(const std::shared_ptr<ControllerBase>& controller) { return false; }
protected:
size_t m_player_index;
std::string m_profile_name = "default";
mutable std::shared_mutex m_mutex;
std::vector<std::shared_ptr<ControllerBase>> m_controllers;
float get_axis_value(uint64 mapping) const;
bool m_rumble = false;
struct Mapping
{
std::weak_ptr<ControllerBase> controller;
uint64 button;
};
std::unordered_map<uint64, Mapping> m_mappings;
bool m_homebutton_down = false;
};
using EmulatedControllerPtr = std::shared_ptr<EmulatedController>;
template <>
struct fmt::formatter<EmulatedController::Type> : formatter<string_view> {
template <typename FormatContext>
auto format(EmulatedController::Type v, FormatContext& ctx) {
switch (v)
{
case EmulatedController::Type::VPAD: return formatter<string_view>::format("Wii U Gamepad", ctx);
case EmulatedController::Type::Pro: return formatter<string_view>::format("Wii U Pro Controller", ctx);
case EmulatedController::Type::Classic: return formatter<string_view>::format("Wii U Classic Controller Pro", ctx);
case EmulatedController::Type::Wiimote: return formatter<string_view>::format("Wiimote", ctx);
}
throw std::invalid_argument(fmt::format("invalid emulated controller type with value {}", to_underlying(v)));
}
};

View file

@ -0,0 +1,272 @@
#include "input/emulated/ProController.h"
#include "input/api/Controller.h"
#include "input/api/SDL/SDLController.h"
ProController::ProController(size_t player_index)
: WPADController(player_index, kDataFormat_URCC)
{
}
uint32 ProController::get_emulated_button_flag(uint32 id) const
{
return s_get_emulated_button_flag(id);
}
uint32 ProController::s_get_emulated_button_flag(uint32 id)
{
switch (id)
{
case kButtonId_A:
return kProButton_A;
case kButtonId_B:
return kProButton_B;
case kButtonId_X:
return kProButton_X;
case kButtonId_Y:
return kProButton_Y;
case kButtonId_Plus:
return kProButton_Plus;
case kButtonId_Minus:
return kProButton_Minus;
case kButtonId_Up:
return kProButton_Up;
case kButtonId_Down:
return kProButton_Down;
case kButtonId_Left:
return kProButton_Left;
case kButtonId_Right:
return kProButton_Right;
case kButtonId_StickL:
return kProButton_StickL;
case kButtonId_StickR:
return kProButton_StickR;
case kButtonId_L:
return kProButton_L;
case kButtonId_ZL:
return kProButton_ZL;
case kButtonId_R:
return kProButton_R;
case kButtonId_ZR:
return kProButton_ZR;
default:
return 0;
}
}
std::string_view ProController::get_button_name(ButtonId id)
{
switch (id)
{
case kButtonId_A: return "A";
case kButtonId_B: return "B";
case kButtonId_X: return "X";
case kButtonId_Y: return "Y";
case kButtonId_L: return "L";
case kButtonId_R: return "R";
case kButtonId_ZL: return "ZL";
case kButtonId_ZR: return "ZR";
case kButtonId_Plus: return "+";
case kButtonId_Minus: return "-";
case kButtonId_Up: return "up";
case kButtonId_Down: return "down";
case kButtonId_Left: return "left";
case kButtonId_Right: return "right";
case kButtonId_StickL: return "click";
case kButtonId_StickR: return "click";
case kButtonId_StickL_Up: return "up";
case kButtonId_StickL_Down: return "down";
case kButtonId_StickL_Left: return "left";
case kButtonId_StickL_Right: return "right";
case kButtonId_StickR_Up: return "up";
case kButtonId_StickR_Down: return "down";
case kButtonId_StickR_Left: return "left";
case kButtonId_StickR_Right: return "right";
case kButtonId_Home: return "home";
default:
cemu_assert_debug(false);
return "";
}
}
glm::vec2 ProController::get_axis() const
{
const auto left = get_axis_value(kButtonId_StickL_Left);
const auto right = get_axis_value(kButtonId_StickL_Right);
const auto up = get_axis_value(kButtonId_StickL_Up);
const auto down = get_axis_value(kButtonId_StickL_Down);
glm::vec2 result;
result.x = (left > right) ? -left : right;
result.y = (up > down) ? up : -down;
return result;
}
glm::vec2 ProController::get_rotation() const
{
const auto left = get_axis_value(kButtonId_StickR_Left);
const auto right = get_axis_value(kButtonId_StickR_Right);
const auto up = get_axis_value(kButtonId_StickR_Up);
const auto down = get_axis_value(kButtonId_StickR_Down);
glm::vec2 result;
result.x = (left > right) ? -left : right;
result.y = (up > down) ? up : -down;
return result;
}
glm::vec2 ProController::get_trigger() const
{
const auto left = get_axis_value(kButtonId_ZL);
const auto right = get_axis_value(kButtonId_ZR);
return { left, right };
}
bool ProController::set_default_mapping(const std::shared_ptr<ControllerBase>& controller)
{
std::vector<std::pair<uint64, uint64>> mapping;
switch (controller->api())
{
case InputAPI::SDLController: {
const auto sdl_controller = std::static_pointer_cast<SDLController>(controller);
if (sdl_controller->get_guid() == SDLController::kLeftJoyCon)
{
mapping =
{
{kButtonId_L, kButton9},
{kButtonId_ZL, kTriggerXP},
{kButtonId_Minus, kButton4},
{kButtonId_Up, kButton11},
{kButtonId_Down, kButton12},
{kButtonId_Left, kButton13},
{kButtonId_Right, kButton14},
{kButtonId_StickL, kButton7},
{kButtonId_StickL_Up, kAxisYN},
{kButtonId_StickL_Down, kAxisYP},
{kButtonId_StickL_Left, kAxisXN},
{kButtonId_StickL_Right, kAxisXP},
};
}
else if (sdl_controller->get_guid() == SDLController::kRightJoyCon)
{
mapping =
{
{kButtonId_A, kButton0},
{kButtonId_B, kButton1},
{kButtonId_X, kButton2},
{kButtonId_Y, kButton3},
{kButtonId_R, kButton10},
{kButtonId_ZR, kTriggerYP},
{kButtonId_Plus, kButton6},
{kButtonId_StickR, kButton8},
{kButtonId_StickR_Up, kRotationYN},
{kButtonId_StickR_Down, kRotationYP},
{kButtonId_StickR_Left, kRotationXN},
{kButtonId_StickR_Right, kRotationXP},
};
}
else
{
mapping =
{
{kButtonId_A, kButton1},
{kButtonId_B, kButton0},
{kButtonId_X, kButton3},
{kButtonId_Y, kButton2},
{kButtonId_L, kButton9},
{kButtonId_R, kButton10},
{kButtonId_ZL, kTriggerXP},
{kButtonId_ZR, kTriggerYP},
{kButtonId_Plus, kButton6},
{kButtonId_Minus, kButton4},
{kButtonId_Up, kButton11},
{kButtonId_Down, kButton12},
{kButtonId_Left, kButton13},
{kButtonId_Right, kButton14},
{kButtonId_StickL, kButton7},
{kButtonId_StickR, kButton8},
{kButtonId_StickL_Up, kAxisYN},
{kButtonId_StickL_Down, kAxisYP},
{kButtonId_StickL_Left, kAxisXN},
{kButtonId_StickL_Right, kAxisXP},
{kButtonId_StickR_Up, kRotationYN},
{kButtonId_StickR_Down, kRotationYP},
{kButtonId_StickR_Left, kRotationXN},
{kButtonId_StickR_Right, kRotationXP},
};
}
break;
}
case InputAPI::XInput:
{
mapping =
{
{kButtonId_A, kButton13},
{kButtonId_B, kButton12},
{kButtonId_X, kButton15},
{kButtonId_Y, kButton14},
{kButtonId_L, kButton8},
{kButtonId_R, kButton9},
{kButtonId_ZL, kTriggerXP},
{kButtonId_ZR, kTriggerYP},
{kButtonId_Plus, kButton4},
{kButtonId_Minus, kButton5},
{kButtonId_Up, kButton0},
{kButtonId_Down, kButton1},
{kButtonId_Left, kButton2},
{kButtonId_Right, kButton3},
{kButtonId_StickL, kButton6},
{kButtonId_StickR, kButton7},
{kButtonId_StickL_Up, kAxisYP},
{kButtonId_StickL_Down, kAxisYN},
{kButtonId_StickL_Left, kAxisXN},
{kButtonId_StickL_Right, kAxisXP},
{kButtonId_StickR_Up, kRotationYP},
{kButtonId_StickR_Down, kRotationYN},
{kButtonId_StickR_Left, kRotationXN},
{kButtonId_StickR_Right, kRotationXP},
};
break;
}
}
bool mapping_updated = false;
std::for_each(mapping.cbegin(), mapping.cend(), [this, &controller, &mapping_updated](const auto& m)
{
if (m_mappings.find(m.first) == m_mappings.cend())
{
set_mapping(m.first, controller, m.second);
mapping_updated = true;
}
});
return mapping_updated;
}

View file

@ -0,0 +1,74 @@
#pragma once
#include "input/emulated/WPADController.h"
class ProController : public WPADController
{
public:
enum ButtonId
{
kButtonId_None,
kButtonId_A,
kButtonId_B,
kButtonId_X,
kButtonId_Y,
kButtonId_L,
kButtonId_R,
kButtonId_ZL,
kButtonId_ZR,
kButtonId_Plus,
kButtonId_Minus,
kButtonId_Home,
kButtonId_Up,
kButtonId_Down,
kButtonId_Left,
kButtonId_Right,
kButtonId_StickL,
kButtonId_StickR,
kButtonId_StickL_Up,
kButtonId_StickL_Down,
kButtonId_StickL_Left,
kButtonId_StickL_Right,
kButtonId_StickR_Up,
kButtonId_StickR_Down,
kButtonId_StickR_Left,
kButtonId_StickR_Right,
kButtonId_Max,
};
ProController(size_t player_index);
Type type() const override { return Type::Pro; }
WPADDeviceType get_device_type() const override { return kWAPDevURCC; }
uint32 get_emulated_button_flag(uint32 id) const override;
size_t get_highest_mapping_id() const override { return kButtonId_Max; }
bool is_axis_mapping(uint64 mapping) const override { return mapping >= kButtonId_StickL_Up && mapping <= kButtonId_StickR_Right; }
glm::vec2 get_axis() const override;
glm::vec2 get_rotation() const override;
glm::vec2 get_trigger() const override;
static uint32 s_get_emulated_button_flag(uint32 id);
static std::string_view get_button_name(ButtonId id);
bool is_start_down() const override { return is_mapping_down(kButtonId_Plus); }
bool is_left_down() const override { return is_mapping_down(kButtonId_Left); }
bool is_right_down() const override { return is_mapping_down(kButtonId_Right); }
bool is_up_down() const override { return is_mapping_down(kButtonId_Up); }
bool is_down_down() const override { return is_mapping_down(kButtonId_Down); }
bool is_a_down() const override { return is_mapping_down(kButtonId_A); }
bool is_b_down() const override { return is_mapping_down(kButtonId_B); }
bool is_home_down() const override { return is_mapping_down(kButtonId_Home); }
bool set_default_mapping(const std::shared_ptr<ControllerBase>& controller) override;
};

View file

@ -0,0 +1,687 @@
#include "input/emulated/VPADController.h"
#include "input/api/Controller.h"
#include "input/api/SDL/SDLController.h"
#include "gui/guiWrapper.h"
#include "input/InputManager.h"
#include "Cafe/HW/Latte/Core/Latte.h"
#include "Cafe/CafeSystem.h"
enum ControllerVPADMapping2 : uint32
{
VPAD_A = 0x8000,
VPAD_B = 0x4000,
VPAD_X = 0x2000,
VPAD_Y = 0x1000,
VPAD_L = 0x0020,
VPAD_R = 0x0010,
VPAD_ZL = 0x0080,
VPAD_ZR = 0x0040,
VPAD_PLUS = 0x0008,
VPAD_MINUS = 0x0004,
VPAD_HOME = 0x0002,
VPAD_UP = 0x0200,
VPAD_DOWN = 0x0100,
VPAD_LEFT = 0x0800,
VPAD_RIGHT = 0x0400,
VPAD_STICK_R = 0x00020000,
VPAD_STICK_L = 0x00040000,
VPAD_STICK_L_UP = 0x10000000,
VPAD_STICK_L_DOWN = 0x08000000,
VPAD_STICK_L_LEFT = 0x40000000,
VPAD_STICK_L_RIGHT = 0x20000000,
VPAD_STICK_R_UP = 0x01000000,
VPAD_STICK_R_DOWN = 0x00800000,
VPAD_STICK_R_LEFT = 0x04000000,
VPAD_STICK_R_RIGHT = 0x02000000,
// special flag
VPAD_REPEAT = 0x80000000,
};
void VPADController::VPADRead(VPADStatus_t& status, const BtnRepeat& repeat)
{
controllers_update_states();
m_mic_active = false;
m_screen_active = false;
for (uint32 i = kButtonId_A; i < kButtonId_Max; ++i)
{
// axis will be aplied later
if (is_axis_mapping(i))
continue;
if (is_mapping_down(i))
{
const uint32 value = get_emulated_button_flag(i);
if (value == 0)
{
// special buttons
if (i == kButtonId_Mic)
m_mic_active = true;
else if (i == kButtonId_Screen)
m_screen_active = true;
continue;
}
status.hold |= value;
}
}
m_homebutton_down |= is_home_down();
const auto axis = get_axis();
status.leftStick.x = axis.x;
status.leftStick.y = axis.y;
constexpr float kAxisThreshold = 0.5f;
constexpr float kHoldAxisThreshold = 0.1f;
const uint32 last_hold = m_last_holdvalue;
if (axis.x <= -kAxisThreshold || (HAS_FLAG(last_hold, VPAD_STICK_L_LEFT) && axis.x <= -kHoldAxisThreshold))
status.hold |= VPAD_STICK_L_LEFT;
else if (axis.x >= kAxisThreshold || (HAS_FLAG(last_hold, VPAD_STICK_L_RIGHT) && axis.x >= kHoldAxisThreshold))
status.hold |= VPAD_STICK_L_RIGHT;
if (axis.y <= -kAxisThreshold || (HAS_FLAG(last_hold, VPAD_STICK_L_DOWN) && axis.y <= -kHoldAxisThreshold))
status.hold |= VPAD_STICK_L_DOWN;
else if (axis.y >= kAxisThreshold || (HAS_FLAG(last_hold, VPAD_STICK_L_UP) && axis.y >= kHoldAxisThreshold))
status.hold |= VPAD_STICK_L_UP;
const auto rotation = get_rotation();
status.rightStick.x = rotation.x;
status.rightStick.y = rotation.y;
if (rotation.x <= -kAxisThreshold || (HAS_FLAG(last_hold, VPAD_STICK_R_LEFT) && rotation.x <= -kHoldAxisThreshold))
status.hold |= VPAD_STICK_R_LEFT;
else if (rotation.x >= kAxisThreshold || (HAS_FLAG(last_hold, VPAD_STICK_R_RIGHT) && rotation.x >=
kHoldAxisThreshold))
status.hold |= VPAD_STICK_R_RIGHT;
if (rotation.y <= -kAxisThreshold || (HAS_FLAG(last_hold, VPAD_STICK_R_DOWN) && rotation.y <= -kHoldAxisThreshold))
status.hold |= VPAD_STICK_R_DOWN;
else if (rotation.y >= kAxisThreshold || (HAS_FLAG(last_hold, VPAD_STICK_R_UP) && rotation.y >= kHoldAxisThreshold))
status.hold |= VPAD_STICK_R_UP;
// button repeat
const auto now = std::chrono::high_resolution_clock::now();
if (status.hold != m_last_holdvalue)
{
m_last_hold_change = m_last_pulse = now;
}
if (repeat.pulse > 0)
{
if (m_last_hold_change + std::chrono::milliseconds(repeat.delay) >= now)
{
if ((m_last_pulse + std::chrono::milliseconds(repeat.pulse)) < now)
{
m_last_pulse = now;
status.hold |= VPAD_REPEAT;
}
}
}
// general
status.release = m_last_holdvalue & ~status.hold;
status.trig = ~m_last_holdvalue & status.hold;
m_last_holdvalue = status.hold;
// touch
update_touch(status);
// motion
status.dir.x = {1, 0, 0};
status.dir.y = {0, 1, 0};
status.dir.z = {0, 0, 1};
status.accXY = {1.0f, 0.0f};
update_motion(status);
}
void VPADController::update()
{
EmulatedController::update();
if (!CafeSystem::IsTitleRunning())
return;
std::unique_lock lock(m_rumble_mutex);
if (m_rumble_queue.empty())
{
m_parser = 0;
lock.unlock();
stop_rumble();
return;
}
const auto tick = now_cached();
if (std::chrono::duration_cast<std::chrono::milliseconds>(tick - m_last_rumble_check).count() < 1000 / 60)
return;
m_last_rumble_check = tick;
const auto& it = m_rumble_queue.front();
if (it[m_parser])
start_rumble();
else
stop_rumble();
++m_parser;
if (m_parser >= it.size())
{
m_rumble_queue.pop();
m_parser = 0;
}
}
void VPADController::update_touch(VPADStatus_t& status)
{
status.tpData.touch = kTpTouchOff;
status.tpData.validity = kTpInvalid;
// keep x,y from previous update
// NGDK (Neko Game Development Kit 2) games (e.g. Mysterios Cities of Gold) rely on x/y remaining intact after touch is released
status.tpData.x = (uint16)m_last_touch_position.x;
status.tpData.y = (uint16)m_last_touch_position.y;
auto& instance = InputManager::instance();
bool pad_view;
if (has_position())
{
const auto mouse = get_position();
status.tpData.touch = kTpTouchOn;
status.tpData.validity = kTpValid;
status.tpData.x = (uint16)(mouse.x * 3883.0f + 92.0f);
status.tpData.y = (uint16)(4095.0f - mouse.y * 3694.0f - 254.0f);
m_last_touch_position = glm::ivec2{status.tpData.x, status.tpData.y};
}
else if (const auto left_mouse = instance.get_left_down_mouse_info(&pad_view))
{
glm::ivec2 image_pos, image_size;
LatteRenderTarget_getScreenImageArea(&image_pos.x, &image_pos.y, &image_size.x, &image_size.y, nullptr, nullptr, pad_view);
glm::vec2 relative_mouse_pos = left_mouse.value() - image_pos;
relative_mouse_pos = { std::min(relative_mouse_pos.x, (float)image_size.x), std::min(relative_mouse_pos.y, (float)image_size.y) };
relative_mouse_pos = { std::max(relative_mouse_pos.x, 0.0f), std::max(relative_mouse_pos.y, 0.0f) };
relative_mouse_pos /= image_size;
status.tpData.touch = kTpTouchOn;
status.tpData.validity = kTpValid;
status.tpData.x = (uint16)((relative_mouse_pos.x * 3883.0f) + 92.0f);
status.tpData.y = (uint16)(4095.0f - (relative_mouse_pos.y * 3694.0f) - 254.0f);
m_last_touch_position = glm::ivec2{ status.tpData.x, status.tpData.y };
/*cemuLog_force("TDATA: {},{} -> {},{} -> {},{} -> {},{} -> {},{} -> {},{}",
left_mouse->x, left_mouse->y,
(left_mouse.value() - image_pos).x, (left_mouse.value() - image_pos).y,
relative_mouse_pos.x, relative_mouse_pos.y,
(uint16)(relative_mouse_pos.x * 3883.0 + 92.0), (uint16)(4095.0 - relative_mouse_pos.y * 3694.0 - 254.0),
status.tpData.x.value(), status.tpData.y.value(), status.tpData.x.bevalue(), status.tpData.y.bevalue()
);*/
}
status.tpProcessed1 = status.tpData;
status.tpProcessed2 = status.tpData;
}
void VPADController::update_motion(VPADStatus_t& status)
{
if (has_motion())
{
auto motionSample = get_motion_data();
glm::vec3 acc;
motionSample.getVPADAccelerometer(&acc[0]);
//const auto& acc = motionSample.getVPADAccelerometer();
status.acc.x = acc.x;
status.acc.y = acc.y;
status.acc.z = acc.z;
status.accMagnitude = motionSample.getVPADAccMagnitude();
status.accAcceleration = motionSample.getVPADAccAcceleration();
glm::vec3 gyroChange;
motionSample.getVPADGyroChange(&gyroChange[0]);
//const auto& gyroChange = motionSample.getVPADGyroChange();
status.gyroChange.x = gyroChange.x;
status.gyroChange.y = gyroChange.y;
status.gyroChange.z = gyroChange.z;
//debug_printf("GyroChange %7.2lf %7.2lf %7.2lf\n", (float)status.gyroChange.x, (float)status.gyroChange.y, (float)status.gyroChange.z);
glm::vec3 gyroOrientation;
motionSample.getVPADOrientation(&gyroOrientation[0]);
//const auto& gyroOrientation = motionSample.getVPADOrientation();
status.gyroOrientation.x = gyroOrientation.x;
status.gyroOrientation.y = gyroOrientation.y;
status.gyroOrientation.z = gyroOrientation.z;
float attitude[9];
motionSample.getVPADAttitudeMatrix(attitude);
status.dir.x.x = attitude[0];
status.dir.x.y = attitude[1];
status.dir.x.z = attitude[2];
status.dir.y.x = attitude[3];
status.dir.y.y = attitude[4];
status.dir.y.z = attitude[5];
status.dir.z.x = attitude[6];
status.dir.z.y = attitude[7];
status.dir.z.z = attitude[8];
return;
}
bool pad_view;
auto& input_manager = InputManager::instance();
if (const auto right_mouse = input_manager.get_right_down_mouse_info(&pad_view))
{
const Vector2<float> mousePos(right_mouse->x, right_mouse->y);
int w, h;
if (pad_view)
gui_getPadWindowSize(&w, &h);
else
gui_getWindowSize(&w, &h);
float wx = mousePos.x / w;
float wy = mousePos.y / h;
static glm::vec3 m_lastGyroRotation{}, m_startGyroRotation{};
static bool m_startGyroRotationSet{};
float rotX = (wy * 2 - 1.0f) * 135.0f; // up/down best
float rotY = (wx * 2 - 1.0f) * -180.0f; // left/right
float rotZ = input_manager.m_mouse_wheel * 14.0f + m_lastGyroRotation.z;
input_manager.m_mouse_wheel = 0.0f;
if (!m_startGyroRotationSet)
{
m_startGyroRotation = {rotX, rotY, rotZ};
m_startGyroRotationSet = true;
}
/* debug_printf("\n\ngyro:\n<%.02f, %.02f, %.02f>\n\n",
rotX, rotY, rotZ);*/
Quaternion<float> q(rotX, rotY, rotZ);
auto rot = q.GetTransposedRotationMatrix();
/*m_forward = std::get<0>(rot);
m_right = std::get<1>(rot);
m_up = std::get<2>(rot);*/
status.dir.x = std::get<0>(rot);
status.dir.y = std::get<1>(rot);
status.dir.z = std::get<2>(rot);
/*debug_printf("rot:\n<%.02f, %.02f, %.02f>\n<%.02f, %.02f, %.02f>\n<%.02f, %.02f, %.02f>\n\n",
(float)status.dir.x.x, (float)status.dir.x.y, (float)status.dir.x.z,
(float)status.dir.y.x, (float)status.dir.y.y, (float)status.dir.y.z,
(float)status.dir.z.x, (float)status.dir.z.y, (float)status.dir.z.z);*/
glm::vec3 rotation(rotX - m_lastGyroRotation.x, (rotY - m_lastGyroRotation.y) * 15.0f,
rotZ - m_lastGyroRotation.z);
rotation.x = std::min(1.0f, std::max(-1.0f, rotation.x / 360.0f));
rotation.y = std::min(1.0f, std::max(-1.0f, rotation.y / 360.0f));
rotation.z = std::min(1.0f, std::max(-1.0f, rotation.z / 360.0f));
/*debug_printf("\n\ngyro:\n<%.02f, %.02f, %.02f>\n\n",
rotation.x, rotation.y, rotation.z);*/
constexpr float pi2 = (float)(M_PI * 2);
status.gyroChange = {rotation.x, rotation.y, rotation.z};
status.gyroOrientation = {rotation.x, rotation.y, rotation.z};
//status.angle = { rotation.x / pi2, rotation.y / pi2, rotation.z / pi2 };
status.acc = {rotation.x, rotation.y, rotation.z};
status.accAcceleration = 1.0f;
status.accMagnitude = 1.0f;
status.accXY = {1.0f, 0.0f};
m_lastGyroRotation = {rotX, rotY, rotZ};
}
}
std::string_view VPADController::get_button_name(ButtonId id)
{
switch (id)
{
case kButtonId_A: return "A";
case kButtonId_B: return "B";
case kButtonId_X: return "X";
case kButtonId_Y: return "Y";
case kButtonId_L: return "L";
case kButtonId_R: return "R";
case kButtonId_ZL: return "ZL";
case kButtonId_ZR: return "ZR";
case kButtonId_Plus: return "+";
case kButtonId_Minus: return "-";
case kButtonId_Up: return "up";
case kButtonId_Down: return "down";
case kButtonId_Left: return "left";
case kButtonId_Right: return "right";
case kButtonId_StickL: return "click";
case kButtonId_StickR: return "click";
case kButtonId_StickL_Up: return "up";
case kButtonId_StickL_Down: return "down";
case kButtonId_StickL_Left: return "left";
case kButtonId_StickL_Right: return "right";
case kButtonId_StickR_Up: return "up";
case kButtonId_StickR_Down: return "down";
case kButtonId_StickR_Left: return "left";
case kButtonId_StickR_Right: return "right";
case kButtonId_Home: return "home";
default:
cemu_assert_debug(false);
return "";
}
}
void VPADController::clear_rumble()
{
std::scoped_lock lock(m_rumble_mutex);
while (!m_rumble_queue.empty())
m_rumble_queue.pop();
m_parser = 0;
}
bool VPADController::push_rumble(uint8* pattern, uint8 length)
{
if (pattern == nullptr || length == 0)
{
clear_rumble();
return true;
}
std::scoped_lock lock(m_rumble_mutex);
if (m_rumble_queue.size() >= 5)
{
forceLogDebug_printf("too many cmds");
return false;
}
// len = max 15 bytes of data = 120 bits = 1 seconds
// we will use 60 hz for 1 second
std::vector<bool> bitset;
int byte = 0;
int len = (int)length;
while (len > 0)
{
const uint8 p = pattern[byte];
for (int j = 0; j < 8 && j < len; j += 2)
{
const bool set = (p & (3 << j)) != 0;
bitset.push_back(set);
}
++byte;
len -= 8;
}
m_rumble_queue.emplace(std::move(bitset));
m_last_rumble_check = {};
return true;
}
uint32 VPADController::get_emulated_button_flag(uint32 id) const
{
switch (id)
{
case kButtonId_A: return VPAD_A;
case kButtonId_B: return VPAD_B;
case kButtonId_X: return VPAD_X;
case kButtonId_Y: return VPAD_Y;
case kButtonId_L: return VPAD_L;
case kButtonId_R: return VPAD_R;
case kButtonId_ZL: return VPAD_ZL;
case kButtonId_ZR: return VPAD_ZR;
case kButtonId_Plus: return VPAD_PLUS;
case kButtonId_Minus: return VPAD_MINUS;
case kButtonId_Up: return VPAD_UP;
case kButtonId_Down: return VPAD_DOWN;
case kButtonId_Left: return VPAD_LEFT;
case kButtonId_Right: return VPAD_RIGHT;
case kButtonId_StickL: return VPAD_STICK_L;
case kButtonId_StickR: return VPAD_STICK_R;
case kButtonId_StickL_Up: return VPAD_STICK_L_UP;
case kButtonId_StickL_Down: return VPAD_STICK_L_DOWN;
case kButtonId_StickL_Left: return VPAD_STICK_L_LEFT;
case kButtonId_StickL_Right: return VPAD_STICK_L_RIGHT;
case kButtonId_StickR_Up: return VPAD_STICK_R_UP;
case kButtonId_StickR_Down: return VPAD_STICK_R_DOWN;
case kButtonId_StickR_Left: return VPAD_STICK_R_LEFT;
case kButtonId_StickR_Right: return VPAD_STICK_R_RIGHT;
}
return 0;
}
glm::vec2 VPADController::get_axis() const
{
const auto left = get_axis_value(kButtonId_StickL_Left);
const auto right = get_axis_value(kButtonId_StickL_Right);
const auto up = get_axis_value(kButtonId_StickL_Up);
const auto down = get_axis_value(kButtonId_StickL_Down);
glm::vec2 result;
result.x = (left > right) ? -left : right;
result.y = (up > down) ? up : -down;
return length(result) > 1.0f ? normalize(result) : result;
}
glm::vec2 VPADController::get_rotation() const
{
const auto left = get_axis_value(kButtonId_StickR_Left);
const auto right = get_axis_value(kButtonId_StickR_Right);
const auto up = get_axis_value(kButtonId_StickR_Up);
const auto down = get_axis_value(kButtonId_StickR_Down);
glm::vec2 result;
result.x = (left > right) ? -left : right;
result.y = (up > down) ? up : -down;
return length(result) > 1.0f ? normalize(result) : result;
}
glm::vec2 VPADController::get_trigger() const
{
const auto left = get_axis_value(kButtonId_ZL);
const auto right = get_axis_value(kButtonId_ZR);
return {left, right};
}
bool VPADController::set_default_mapping(const std::shared_ptr<ControllerBase>& controller)
{
std::vector<std::pair<uint64, uint64>> mapping;
switch (controller->api())
{
case InputAPI::SDLController: {
const auto sdl_controller = std::static_pointer_cast<SDLController>(controller);
if (sdl_controller->get_guid() == SDLController::kLeftJoyCon)
{
mapping =
{
{kButtonId_L, kButton9},
{kButtonId_ZL, kTriggerXP},
{kButtonId_Minus, kButton4},
{kButtonId_Up, kButton11},
{kButtonId_Down, kButton12},
{kButtonId_Left, kButton13},
{kButtonId_Right, kButton14},
{kButtonId_StickL, kButton7},
{kButtonId_StickL_Up, kAxisYN},
{kButtonId_StickL_Down, kAxisYP},
{kButtonId_StickL_Left, kAxisXN},
{kButtonId_StickL_Right, kAxisXP},
{kButtonId_Mic, kButton15},
};
}
else if (sdl_controller->get_guid() == SDLController::kRightJoyCon)
{
mapping =
{
{kButtonId_A, kButton0},
{kButtonId_B, kButton1},
{kButtonId_X, kButton2},
{kButtonId_Y, kButton3},
{kButtonId_R, kButton10},
{kButtonId_ZR, kTriggerYP},
{kButtonId_Plus, kButton6},
{kButtonId_StickR, kButton8},
{kButtonId_StickR_Up, kRotationYN},
{kButtonId_StickR_Down, kRotationYP},
{kButtonId_StickR_Left, kRotationXN},
{kButtonId_StickR_Right, kRotationXP},
};
}
else if (sdl_controller->get_guid() == SDLController::kSwitchProController)
{
// Switch Pro Controller is similar to default mapping, but with a/b and x/y swapped
mapping =
{
{kButtonId_A, kButton0},
{kButtonId_B, kButton1},
{kButtonId_X, kButton2},
{kButtonId_Y, kButton3},
{kButtonId_L, kButton9},
{kButtonId_R, kButton10},
{kButtonId_ZL, kTriggerXP},
{kButtonId_ZR, kTriggerYP},
{kButtonId_Plus, kButton6},
{kButtonId_Minus, kButton4},
{kButtonId_Up, kButton11},
{kButtonId_Down, kButton12},
{kButtonId_Left, kButton13},
{kButtonId_Right, kButton14},
{kButtonId_StickL, kButton7},
{kButtonId_StickR, kButton8},
{kButtonId_StickL_Up, kAxisYN},
{kButtonId_StickL_Down, kAxisYP},
{kButtonId_StickL_Left, kAxisXN},
{kButtonId_StickL_Right, kAxisXP},
{kButtonId_StickR_Up, kRotationYN},
{kButtonId_StickR_Down, kRotationYP},
{kButtonId_StickR_Left, kRotationXN},
{kButtonId_StickR_Right, kRotationXP},
};
}
else
{
mapping =
{
{kButtonId_A, kButton1},
{kButtonId_B, kButton0},
{kButtonId_X, kButton3},
{kButtonId_Y, kButton2},
{kButtonId_L, kButton9},
{kButtonId_R, kButton10},
{kButtonId_ZL, kTriggerXP},
{kButtonId_ZR, kTriggerYP},
{kButtonId_Plus, kButton6},
{kButtonId_Minus, kButton4},
{kButtonId_Up, kButton11},
{kButtonId_Down, kButton12},
{kButtonId_Left, kButton13},
{kButtonId_Right, kButton14},
{kButtonId_StickL, kButton7},
{kButtonId_StickR, kButton8},
{kButtonId_StickL_Up, kAxisYN},
{kButtonId_StickL_Down, kAxisYP},
{kButtonId_StickL_Left, kAxisXN},
{kButtonId_StickL_Right, kAxisXP},
{kButtonId_StickR_Up, kRotationYN},
{kButtonId_StickR_Down, kRotationYP},
{kButtonId_StickR_Left, kRotationXN},
{kButtonId_StickR_Right, kRotationXP},
};
}
break;
}
case InputAPI::XInput:
{
mapping =
{
{kButtonId_A, kButton13},
{kButtonId_B, kButton12},
{kButtonId_X, kButton15},
{kButtonId_Y, kButton14},
{kButtonId_L, kButton8},
{kButtonId_R, kButton9},
{kButtonId_ZL, kTriggerXP},
{kButtonId_ZR, kTriggerYP},
{kButtonId_Plus, kButton4},
{kButtonId_Minus, kButton5},
{kButtonId_Up, kButton0},
{kButtonId_Down, kButton1},
{kButtonId_Left, kButton2},
{kButtonId_Right, kButton3},
{kButtonId_StickL, kButton6},
{kButtonId_StickR, kButton7},
{kButtonId_StickL_Up, kAxisYP},
{kButtonId_StickL_Down, kAxisYN},
{kButtonId_StickL_Left, kAxisXN},
{kButtonId_StickL_Right, kAxisXP},
{kButtonId_StickR_Up, kRotationYP},
{kButtonId_StickR_Down, kRotationYN},
{kButtonId_StickR_Left, kRotationXN},
{kButtonId_StickR_Right, kRotationXP},
};
break;
}
}
bool mapping_updated = false;
std::for_each(mapping.cbegin(), mapping.cend(), [this, &controller, &mapping_updated](const auto& m)
{
if (m_mappings.find(m.first) == m_mappings.cend())
{
set_mapping(m.first, controller, m.second);
mapping_updated = true;
}
});
return mapping_updated;
}

View file

@ -0,0 +1,104 @@
#pragma once
#include "input/emulated/EmulatedController.h"
#include "Cafe/OS/libs/vpad/vpad.h"
class VPADController : public EmulatedController
{
public:
enum ButtonId
{
kButtonId_None,
kButtonId_A,
kButtonId_B,
kButtonId_X,
kButtonId_Y,
kButtonId_L,
kButtonId_R,
kButtonId_ZL,
kButtonId_ZR,
kButtonId_Plus,
kButtonId_Minus,
kButtonId_Up,
kButtonId_Down,
kButtonId_Left,
kButtonId_Right,
kButtonId_StickL,
kButtonId_StickR,
kButtonId_StickL_Up,
kButtonId_StickL_Down,
kButtonId_StickL_Left,
kButtonId_StickL_Right,
kButtonId_StickR_Up,
kButtonId_StickR_Down,
kButtonId_StickR_Left,
kButtonId_StickR_Right,
kButtonId_Mic,
kButtonId_Screen,
kButtonId_Home,
kButtonId_Max,
};
using EmulatedController::EmulatedController;
Type type() const override { return VPAD; }
void VPADRead(VPADStatus_t& status, const BtnRepeat& repeat);
void update() override;
uint32 get_emulated_button_flag(uint32 id) const override;
glm::vec2 get_axis() const override;
glm::vec2 get_rotation() const override;
glm::vec2 get_trigger() const override;
bool is_mic_active() { return m_mic_active; }
bool is_screen_active() { return m_screen_active; }
static std::string_view get_button_name(ButtonId id);
void clear_rumble();
bool push_rumble(uint8* pattern, uint8 length);
size_t get_highest_mapping_id() const override { return kButtonId_Max; }
bool is_axis_mapping(uint64 mapping) const override { return mapping >= kButtonId_StickL_Up && mapping <= kButtonId_StickR_Right; }
bool is_start_down() const override { return is_mapping_down(kButtonId_Plus); }
bool is_left_down() const override { return is_mapping_down(kButtonId_Left); }
bool is_right_down() const override { return is_mapping_down(kButtonId_Right); }
bool is_up_down() const override { return is_mapping_down(kButtonId_Up); }
bool is_down_down() const override { return is_mapping_down(kButtonId_Down); }
bool is_a_down() const override { return is_mapping_down(kButtonId_A); }
bool is_b_down() const override { return is_mapping_down(kButtonId_B); }
bool is_home_down() const override { return is_mapping_down(kButtonId_Home); }
bool set_default_mapping(const std::shared_ptr<ControllerBase>& controller) override;
private:
bool m_mic_active = false;
bool m_screen_active = false;
uint32be m_last_holdvalue = 0;
std::chrono::high_resolution_clock::time_point m_last_hold_change{}, m_last_pulse{};
std::mutex m_rumble_mutex;
std::chrono::high_resolution_clock::time_point m_last_rumble_check{};
std::queue<std::vector<bool>> m_rumble_queue;
uint8 m_parser = 0;
void update_touch(VPADStatus_t& status);
void update_motion(VPADStatus_t& status);
glm::ivec2 m_last_touch_position{};
};

View file

@ -0,0 +1,378 @@
#include "input/emulated/WPADController.h"
#include "input/emulated/ClassicController.h"
#include "input/emulated/ProController.h"
#include "input/emulated/WiimoteController.h"
WPADController::WPADController(size_t player_index, WPADDataFormat data_format)
: EmulatedController(player_index), m_data_format(data_format)
{
}
WPADDataFormat WPADController::get_default_data_format() const
{
switch (get_device_type())
{
case kWAPDevCore:
return kDataFormat_CORE_ACC_DPD;
case kWAPDevFreestyle:
return kDataFormat_FREESTYLE_ACC;
case kWAPDevClassic:
return kDataFormat_CLASSIC;
case kWAPDevMPLS:
return kDataFormat_MPLS;
case kWAPDevMPLSFreeStyle:
return kDataFormat_FREESTYLE_ACC_DPD;
case kWAPDevMPLSClassic:
return kDataFormat_CLASSIC_ACC_DPD;
case kWAPDevURCC:
return kDataFormat_URCC;
default:
return kDataFormat_CORE;
}
}
uint32 WPADController::get_emulated_button_flag(WPADDataFormat format, uint32 id) const
{
switch(format)
{
case kDataFormat_CORE:
case kDataFormat_CORE_ACC:
case kDataFormat_CORE_ACC_DPD:
case kDataFormat_CORE_ACC_DPD_FULL:
case kDataFormat_FREESTYLE:
case kDataFormat_FREESTYLE_ACC:
case kDataFormat_FREESTYLE_ACC_DPD:
case kDataFormat_MPLS:
return WiimoteController::s_get_emulated_button_flag(id);
case kDataFormat_CLASSIC:
case kDataFormat_CLASSIC_ACC:
case kDataFormat_CLASSIC_ACC_DPD:
return ClassicController::s_get_emulated_button_flag(id);
case kDataFormat_TRAIN: break;
case kDataFormat_GUITAR: break;
case kDataFormat_BALANCE_CHECKER: break;
case kDataFormat_DRUM: break;
case kDataFormat_TAIKO: break;
case kDataFormat_URCC:
return ProController::s_get_emulated_button_flag(id);
}
return 0;
}
void WPADController::WPADRead(WPADStatus_t* status)
{
controllers_update_states();
uint32 button = 0;
for (uint32 i = 1; i < get_highest_mapping_id(); ++i)
{
if (is_mapping_down(i))
{
const uint32 value = get_emulated_button_flag(m_data_format, i);
button |= value;
}
}
m_homebutton_down |= is_home_down();
// todo fill position api from wiimote
switch (m_data_format)
{
case kDataFormat_CORE:
case kDataFormat_CORE_ACC:
case kDataFormat_CORE_ACC_DPD:
case kDataFormat_CORE_ACC_DPD_FULL:
{
memset(status, 0x00, sizeof(*status));
status->button = button;
break;
}
case kDataFormat_FREESTYLE:
case kDataFormat_FREESTYLE_ACC:
case kDataFormat_FREESTYLE_ACC_DPD:
{
WPADFSStatus_t* ex_status = (WPADFSStatus_t*)status;
memset(ex_status, 0x00, sizeof(*ex_status));
ex_status->button = button;
auto axis = get_axis();
axis *= 127.0f;
ex_status->fsStickX = (sint8)axis.x;
ex_status->fsStickY = (sint8)axis.y;
break;
}
case kDataFormat_CLASSIC:
case kDataFormat_CLASSIC_ACC:
case kDataFormat_CLASSIC_ACC_DPD:
case kDataFormat_GUITAR:
case kDataFormat_DRUM:
case kDataFormat_TAIKO:
{
WPADCLStatus_t* ex_status = (WPADCLStatus_t*)status;
memset(ex_status, 0x00, sizeof(*ex_status));
ex_status->clButton = button;
auto axis = get_axis();
axis *= 2048.0f;
ex_status->clLStickX = (uint16)axis.x;
ex_status->clLStickY = (uint16)axis.y;
auto rotation = get_rotation();
rotation *= 2048.0f;
ex_status->clRStickX = (uint16)rotation.x;
ex_status->clRStickY = (uint16)rotation.y;
break;
}
case kDataFormat_TRAIN:
{
WPADTRStatus_t* ex_status = (WPADTRStatus_t*)status;
// TODO
break;
}
case kDataFormat_BALANCE_CHECKER:
{
WPADBLStatus_t* ex_status = (WPADBLStatus_t*)status;
// TODO
break;
}
case kDataFormat_MPLS:
{
WPADMPStatus_t* ex_status = (WPADMPStatus_t*)status;
ex_status->stat = 1; // attached
// TODO
break;
}
case kDataFormat_URCC:
{
WPADUCStatus_t* ex_status = (WPADUCStatus_t*)status;
memset(ex_status, 0x00, sizeof(*ex_status));
ex_status->ucButton = button;
ex_status->cable = TRUE;
ex_status->charge = TRUE;
auto axis = get_axis();
axis *= 2048.0f;
ex_status->ucLStickX = (uint16)axis.x;
ex_status->ucLStickY = (uint16)axis.y;
auto rotation = get_rotation();
rotation *= 2048.0f;
ex_status->ucRStickX = (uint16)rotation.x;
ex_status->ucRStickY = (uint16)rotation.y;
break;
}
default:
cemu_assert(false);
}
status->dev = get_device_type();
status->err = WPAD_ERR_NONE;
}
void WPADController::KPADRead(KPADStatus_t& status, const BtnRepeat& repeat)
{
uint32be* hold, *release, *trigger;
switch (type())
{
case Pro:
hold = &status.ex_status.uc.hold;
release = &status.ex_status.uc.release;
trigger = &status.ex_status.uc.trig;
break;
case Classic:
hold = &status.ex_status.cl.hold;
release = &status.ex_status.cl.release;
trigger = &status.ex_status.cl.trig;
break;
default:
hold = &status.hold;
release = &status.release;
trigger = &status.trig;
}
controllers_update_states();
for (uint32 i = 1; i < get_highest_mapping_id(); ++i)
{
if (is_mapping_down(i))
{
const uint32 value = get_emulated_button_flag(m_data_format, i);
*hold |= value;
}
}
m_homebutton_down |= is_home_down();
// button repeat
const auto now = std::chrono::steady_clock::now();
if (*hold != m_last_holdvalue)
{
m_last_hold_change = m_last_pulse = now;
}
if (repeat.pulse > 0)
{
if (m_last_hold_change + std::chrono::milliseconds(repeat.delay) >= now)
{
if ((m_last_pulse + std::chrono::milliseconds(repeat.pulse)) < now)
{
m_last_pulse = now;
*hold |= kWPADButtonRepeat;
}
}
}
// axis
const auto axis = get_axis();
const auto rotation = get_rotation();
*release = m_last_holdvalue & ~*hold;
//status.release = m_last_holdvalue & ~*hold;
*trigger = ~m_last_holdvalue & *hold;
//status.trig = ~m_last_holdvalue & *hold;
m_last_holdvalue = *hold;
if (is_mpls_attached())
{
status.mpls.dir.X.x = 1;
status.mpls.dir.X.y = 0;
status.mpls.dir.X.z = 0;
status.mpls.dir.Y.x = 0;
status.mpls.dir.Y.y = 1;
status.mpls.dir.Y.z = 0;
status.mpls.dir.Z.x = 0;
status.mpls.dir.Z.y = 0;
status.mpls.dir.Z.z = 1;
}
if (has_motion())
{
auto motion_sample = get_motion_data();
glm::vec3 acc;
motion_sample.getAccelerometer(&acc[0]);
status.acc.x = acc.x;
status.acc.y = acc.y;
status.acc.z = acc.z;
status.acc_value = motion_sample.getVPADAccMagnitude();
status.acc_speed = motion_sample.getVPADAccAcceleration();
//glm::vec2 acc_vert;
//motion_sample.getVPADAccXY(&acc_vert[0]);
//status.acc_vertical.x = acc_vert.x;
//status.acc_vertical.y = acc_vert.y;
status.accVertical.x = std::min(1.0f, std::abs(acc.x + acc.y));
status.accVertical.y = std::min(std::max(-1.0f, -acc.z), 1.0f);
if (is_mpls_attached())
{
// todo
glm::vec3 gyroChange;
motion_sample.getVPADGyroChange(&gyroChange[0]);
//const auto& gyroChange = motionSample.getVPADGyroChange();
status.mpls.mpls.x = gyroChange.x;
status.mpls.mpls.y = gyroChange.y;
status.mpls.mpls.z = gyroChange.z;
//debug_printf("GyroChange %7.2lf %7.2lf %7.2lf\n", (float)status.gyroChange.x, (float)status.gyroChange.y, (float)status.gyroChange.z);
glm::vec3 gyroOrientation;
motion_sample.getVPADOrientation(&gyroOrientation[0]);
//const auto& gyroOrientation = motionSample.getVPADOrientation();
status.mpls.angle.x = gyroOrientation.x;
status.mpls.angle.y = gyroOrientation.y;
status.mpls.angle.z = gyroOrientation.z;
float attitude[9];
motion_sample.getVPADAttitudeMatrix(attitude);
status.mpls.dir.X.x = attitude[0];
status.mpls.dir.X.y = attitude[1];
status.mpls.dir.X.z = attitude[2];
status.mpls.dir.Y.x = attitude[3];
status.mpls.dir.Y.y = attitude[4];
status.mpls.dir.Y.z = attitude[5];
status.mpls.dir.Z.x = attitude[6];
status.mpls.dir.Z.y = attitude[7];
status.mpls.dir.Z.z = attitude[8];
}
}
if (has_position())
{
status.dpd_valid_fg = 1;
const auto position = get_position();
const auto pos = (position * 2.0f) - 1.0f;
status.pos.x = pos.x;
status.pos.y = pos.y;
const auto delta = position - get_prev_position();
status.vec.x = delta.x;
status.vec.y = delta.y;
status.speed = glm::length(delta);
}
switch (type())
{
case Wiimote:
status.ex_status.fs.stick.x = axis.x;
status.ex_status.fs.stick.y = axis.y;
if(has_second_motion())
{
auto motion_sample = get_second_motion_data();
glm::vec3 acc;
motion_sample.getAccelerometer(&acc[0]);
status.ex_status.fs.acc.x = acc.x;
status.ex_status.fs.acc.y = acc.y;
status.ex_status.fs.acc.z = acc.z;
status.ex_status.fs.accValue = motion_sample.getVPADAccMagnitude();
status.ex_status.fs.accSpeed = motion_sample.getVPADAccAcceleration();
}
break;
case Pro:
status.ex_status.uc.lstick.x = axis.x;
status.ex_status.uc.lstick.y = axis.y;
status.ex_status.uc.rstick.x = rotation.x;
status.ex_status.uc.rstick.y = rotation.y;
status.ex_status.uc.charge = FALSE;
status.ex_status.uc.cable = TRUE;
break;
case Classic:
status.ex_status.cl.lstick.x = axis.x;
status.ex_status.cl.lstick.y = axis.y;
status.ex_status.cl.rstick.x = rotation.x;
status.ex_status.cl.rstick.y = rotation.y;
if (HAS_FLAG((uint32)status.ex_status.cl.hold, kCLButton_ZL))
status.ex_status.cl.ltrigger = 1.0f;
if (HAS_FLAG((uint32)status.ex_status.cl.hold, kCLButton_ZR))
status.ex_status.cl.rtrigger = 1.0f;
break;
default:
cemu_assert(false);
}
}

View file

@ -0,0 +1,147 @@
#pragma once
#include <wx/cmdargs.h>
#include "input/emulated/EmulatedController.h"
#include "Cafe/OS/libs/padscore/padscore.h"
#include "Cafe/OS/libs/vpad/vpad.h"
constexpr uint32 kWPADButtonRepeat = 0x80000000;
enum WPADDeviceType
{
kWAPDevCore = 0,
kWAPDevFreestyle = 1,
kWAPDevClassic = 2,
kWAPDevMPLS = 5,
kWAPDevMPLSFreeStyle = 6,
kWAPDevMPLSClassic = 7,
kWAPDevURCC = 31,
kWAPDevNotFound = 253,
kWAPDevUnknown = 255,
};
// core, balanceboard
enum WPADCoreButtons
{
kWPADButton_Left = 0x1,
kWPADButton_Right = 0x2,
kWPADButton_Down = 0x4,
kWPADButton_Up = 0x8,
kWPADButton_Plus = 0x10,
kWPADButton_2 = 0x100,
kWPADButton_1 = 0x200,
kWPADButton_B = 0x400,
kWPADButton_A = 0x800,
kWPADButton_Minus = 0x1000,
kWPADButton_Home = 0x8000,
};
// Nunchuck aka Freestyle
enum WPADNunchuckButtons
{
kWPADButton_Z = 0x2000,
kWPADButton_C = 0x4000,
};
// Classic Controller
enum WPADClassicButtons
{
kCLButton_Up = 0x1,
kCLButton_Left = 0x2,
kCLButton_ZR = 0x4,
kCLButton_X = 0x8,
kCLButton_A = 0x10,
kCLButton_Y = 0x20,
kCLButton_B = 0x40,
kCLButton_ZL = 0x80,
kCLButton_R = 0x200,
kCLButton_Plus = 0x400,
kCLButton_Home = 0x800,
kCLButton_Minus = 0x1000,
kCLButton_L = 0x2000,
kCLButton_Down = 0x4000,
kCLButton_Right = 0x8000
};
// Pro Controller aka URCC
enum WPADProButtons
{
kProButton_Up = 0x1,
kProButton_Left = 0x2,
kProButton_ZR = 0x4,
kProButton_X = 0x8,
kProButton_A = 0x10,
kProButton_Y = 0x20,
kProButton_B = 0x40,
kProButton_ZL = 0x80,
kProButton_R = 0x200,
kProButton_Plus = 0x400,
kProButton_Home = 0x800,
kProButton_Minus = 0x1000,
kProButton_L = 0x2000,
kProButton_Down = 0x4000,
kProButton_Right = 0x8000,
kProButton_StickR = 0x10000,
kProButton_StickL = 0x20000
};
enum WPADDataFormat {
kDataFormat_CORE = 0,
kDataFormat_CORE_ACC = 1,
kDataFormat_CORE_ACC_DPD = 2,
kDataFormat_FREESTYLE = 3,
kDataFormat_FREESTYLE_ACC = 4,
kDataFormat_FREESTYLE_ACC_DPD = 5,
kDataFormat_CLASSIC = 6,
kDataFormat_CLASSIC_ACC = 7,
kDataFormat_CLASSIC_ACC_DPD = 8,
kDataFormat_CORE_ACC_DPD_FULL = 9, // buttons, motion, pointing
kDataFormat_TRAIN = 10,
kDataFormat_GUITAR = 11,
kDataFormat_BALANCE_CHECKER = 12,
kDataFormat_DRUM = 15,
kDataFormat_MPLS = 16, // buttons, motion, pointing, motion plus
kDataFormat_TAIKO = 17,
kDataFormat_URCC = 22, // buttons, URCC aka pro
};
class WPADController : public EmulatedController
{
using base_type = EmulatedController;
public:
WPADController(size_t player_index, WPADDataFormat data_format);
uint32 get_emulated_button_flag(WPADDataFormat format, uint32 id) const;
virtual WPADDeviceType get_device_type() const = 0;
WPADDataFormat get_data_format() const { return m_data_format; }
void set_data_format(WPADDataFormat data_format) { m_data_format = data_format; }
void WPADRead(WPADStatus_t* status);
void KPADRead(KPADStatus_t& status, const BtnRepeat& repeat);
virtual bool is_mpls_attached() { return false; }
enum class ConnectCallbackStatus
{
None, // do nothing
ReportDisconnect, // call disconnect
ReportConnect, // call connect
};
ConnectCallbackStatus m_status = ConnectCallbackStatus::ReportConnect;
ConnectCallbackStatus m_extension_status = ConnectCallbackStatus::ReportConnect;
WPADDataFormat get_default_data_format() const;
protected:
WPADDataFormat m_data_format;
private:
uint32be m_last_holdvalue = 0;
std::chrono::steady_clock::time_point m_last_hold_change{}, m_last_pulse{};
};

View file

@ -0,0 +1,184 @@
#include "input/emulated/WiimoteController.h"
#include "input/api/Controller.h"
#include "input/api/Wiimote/NativeWiimoteController.h"
WiimoteController::WiimoteController(size_t player_index)
: WPADController(player_index, kDataFormat_CORE_ACC_DPD)
{
}
void WiimoteController::set_device_type(WPADDeviceType device_type)
{
m_device_type = device_type;
m_data_format = get_default_data_format();
}
bool WiimoteController::is_mpls_attached()
{
return m_device_type == kWAPDevMPLS || m_device_type == kWAPDevMPLSClassic || m_device_type == kWAPDevMPLSFreeStyle;
}
uint32 WiimoteController::get_emulated_button_flag(uint32 id) const
{
return s_get_emulated_button_flag(id);
}
bool WiimoteController::set_default_mapping(const std::shared_ptr<ControllerBase>& controller)
{
std::vector<std::pair<uint64, uint64>> mapping;
switch (controller->api())
{
case InputAPI::Wiimote: {
const auto sdl_controller = std::static_pointer_cast<NativeWiimoteController>(controller);
mapping =
{
{kButtonId_A, kWiimoteButton_A},
{kButtonId_B, kWiimoteButton_B},
{kButtonId_1, kWiimoteButton_One},
{kButtonId_2, kWiimoteButton_Two},
{kButtonId_Home, kWiimoteButton_Home},
{kButtonId_Plus, kWiimoteButton_Plus},
{kButtonId_Minus, kWiimoteButton_Minus},
{kButtonId_Up, kWiimoteButton_Up},
{kButtonId_Down, kWiimoteButton_Down},
{kButtonId_Left, kWiimoteButton_Left},
{kButtonId_Right, kWiimoteButton_Right},
{kButtonId_Nunchuck_Z, kWiimoteButton_Z},
{kButtonId_Nunchuck_C, kWiimoteButton_C},
{kButtonId_Nunchuck_Up, kAxisYP},
{kButtonId_Nunchuck_Down, kAxisYN},
{kButtonId_Nunchuck_Left, kAxisXN},
{kButtonId_Nunchuck_Right, kAxisXP},
};
}
}
bool mapping_updated = false;
std::for_each(mapping.cbegin(), mapping.cend(), [this, &controller, &mapping_updated](const auto& m)
{
if (m_mappings.find(m.first) == m_mappings.cend())
{
set_mapping(m.first, controller, m.second);
mapping_updated = true;
}
});
return mapping_updated;
}
glm::vec2 WiimoteController::get_axis() const
{
const auto left = get_axis_value(kButtonId_Nunchuck_Left);
const auto right = get_axis_value(kButtonId_Nunchuck_Right);
const auto up = get_axis_value(kButtonId_Nunchuck_Up);
const auto down = get_axis_value(kButtonId_Nunchuck_Down);
glm::vec2 result;
result.x = (left > right) ? -left : right;
result.y = (up > down) ? up : -down;
return result;
}
glm::vec2 WiimoteController::get_rotation() const
{
return {};
}
glm::vec2 WiimoteController::get_trigger() const
{
return {};
}
void WiimoteController::load(const pugi::xml_node& node)
{
base_type::load(node);
if (const auto value = node.child("device_type"))
m_device_type = ConvertString<WPADDeviceType>(value.child_value());
}
void WiimoteController::save(pugi::xml_node& node)
{
base_type::save(node);
node.append_child("device_type").append_child(pugi::node_pcdata).set_value(fmt::format("{}", (int)m_device_type).c_str());
}
uint32 WiimoteController::s_get_emulated_button_flag(uint32 id)
{
switch (id)
{
case kButtonId_A:
return kWPADButton_A;
case kButtonId_B:
return kWPADButton_B;
case kButtonId_1:
return kWPADButton_1;
case kButtonId_2:
return kWPADButton_2;
case kButtonId_Plus:
return kWPADButton_Plus;
case kButtonId_Minus:
return kWPADButton_Minus;
case kButtonId_Home:
return kWPADButton_Home;
case kButtonId_Up:
return kWPADButton_Up;
case kButtonId_Down:
return kWPADButton_Down;
case kButtonId_Left:
return kWPADButton_Left;
case kButtonId_Right:
return kWPADButton_Right;
case kButtonId_Nunchuck_Z:
return kWPADButton_Z;
case kButtonId_Nunchuck_C:
return kWPADButton_C;
}
return 0;
}
std::string_view WiimoteController::get_button_name(ButtonId id)
{
switch (id)
{
case kButtonId_A: return "A";
case kButtonId_B: return "B";
case kButtonId_1: return "1";
case kButtonId_2: return "2";
case kButtonId_Home: return "home";
case kButtonId_Plus: return "+";
case kButtonId_Minus: return "-";
case kButtonId_Up: return "up";
case kButtonId_Down: return "down";
case kButtonId_Left: return "left";
case kButtonId_Right: return "right";
case kButtonId_Nunchuck_Z: return "Z";
case kButtonId_Nunchuck_C: return "C";
case kButtonId_Nunchuck_Up: return "up";
case kButtonId_Nunchuck_Down: return "down";
case kButtonId_Nunchuck_Left: return "left";
case kButtonId_Nunchuck_Right: return "right";
default:
return "";
}
}

View file

@ -0,0 +1,78 @@
#pragma once
#include "input/emulated/WPADController.h"
#include "gui/input/panels/WiimoteInputPanel.h"
class WiimoteController : public WPADController
{
using base_type = WPADController;
public:
enum ButtonId
{
kButtonId_None,
kButtonId_A,
kButtonId_B,
kButtonId_1,
kButtonId_2,
kButtonId_Nunchuck_Z,
kButtonId_Nunchuck_C,
kButtonId_Plus,
kButtonId_Minus,
kButtonId_Up,
kButtonId_Down,
kButtonId_Left,
kButtonId_Right,
kButtonId_Nunchuck_Up,
kButtonId_Nunchuck_Down,
kButtonId_Nunchuck_Left,
kButtonId_Nunchuck_Right,
kButtonId_Home,
kButtonId_Max,
};
WiimoteController(size_t player_index);
Type type() const override { return Type::Wiimote; }
WPADDeviceType get_device_type() const override { return m_device_type; }
void set_device_type(WPADDeviceType device_type);
bool is_mpls_attached() override;
uint32 get_emulated_button_flag(uint32 id) const override;
size_t get_highest_mapping_id() const override { return kButtonId_Max; }
bool is_axis_mapping(uint64 mapping) const override { return mapping >= kButtonId_Nunchuck_Up && mapping <= kButtonId_Nunchuck_Right; }
bool set_default_mapping(const std::shared_ptr<ControllerBase>& controller) override;
glm::vec2 get_axis() const override;
glm::vec2 get_rotation() const override;
glm::vec2 get_trigger() const override;
void load(const pugi::xml_node& node) override;
void save(pugi::xml_node& node) override;
static uint32 s_get_emulated_button_flag(uint32 id);
static std::string_view get_button_name(ButtonId id);
bool is_start_down() const override { return is_mapping_down(kButtonId_Plus); }
bool is_left_down() const override { return is_mapping_down(kButtonId_Left); }
bool is_right_down() const override { return is_mapping_down(kButtonId_Right); }
bool is_up_down() const override { return is_mapping_down(kButtonId_Up); }
bool is_down_down() const override { return is_mapping_down(kButtonId_Down); }
bool is_a_down() const override { return is_mapping_down(kButtonId_A); }
bool is_b_down() const override { return is_mapping_down(kButtonId_B); }
bool is_home_down() const override { return is_mapping_down(kButtonId_Home); }
private:
WPADDeviceType m_device_type = kWAPDevCore;
};