mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-12-26 16:37:05 +00:00
Add all the files
This commit is contained in:
parent
e3db07a16a
commit
d60742f52b
1445 changed files with 430238 additions and 0 deletions
258
src/input/emulated/ClassicController.cpp
Normal file
258
src/input/emulated/ClassicController.cpp
Normal 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;
|
||||
}
|
||||
72
src/input/emulated/ClassicController.h
Normal file
72
src/input/emulated/ClassicController.h
Normal 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;
|
||||
};
|
||||
341
src/input/emulated/EmulatedController.cpp
Normal file
341
src/input/emulated/EmulatedController.cpp
Normal 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);
|
||||
}
|
||||
140
src/input/emulated/EmulatedController.h
Normal file
140
src/input/emulated/EmulatedController.h
Normal 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)));
|
||||
}
|
||||
};
|
||||
272
src/input/emulated/ProController.cpp
Normal file
272
src/input/emulated/ProController.cpp
Normal 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;
|
||||
}
|
||||
74
src/input/emulated/ProController.h
Normal file
74
src/input/emulated/ProController.h
Normal 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;
|
||||
};
|
||||
687
src/input/emulated/VPADController.cpp
Normal file
687
src/input/emulated/VPADController.cpp
Normal 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;
|
||||
}
|
||||
104
src/input/emulated/VPADController.h
Normal file
104
src/input/emulated/VPADController.h
Normal 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{};
|
||||
};
|
||||
378
src/input/emulated/WPADController.cpp
Normal file
378
src/input/emulated/WPADController.cpp
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
147
src/input/emulated/WPADController.h
Normal file
147
src/input/emulated/WPADController.h
Normal 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{};
|
||||
|
||||
|
||||
};
|
||||
184
src/input/emulated/WiimoteController.cpp
Normal file
184
src/input/emulated/WiimoteController.cpp
Normal 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 "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
78
src/input/emulated/WiimoteController.h
Normal file
78
src/input/emulated/WiimoteController.h
Normal 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;
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue