mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-07-04 14:01:17 +12:00
717 lines
19 KiB
C++
717 lines
19 KiB
C++
#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;
|
|
}
|
|
|
|
if (GetKeyState(VK_F1) & 1 && !(GetKeyState(VK_TAB) & 0x8000))
|
|
{
|
|
if (i == kButtonId_ZR && (GetKeyState(MK_LBUTTON) & 0x8000))
|
|
{
|
|
status.hold |= VPAD_ZR;
|
|
}
|
|
else if (i == kButtonId_R && (GetKeyState(MK_RBUTTON) & 0x8000))
|
|
{
|
|
status.hold |= VPAD_R;
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|
|
if (!GetKeyState(VK_F1) & 1 || (GetKeyState(VK_TAB) & 0x8000))
|
|
{
|
|
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;
|
|
}
|
|
|
|
float oldPosX, oldPosY = 0;
|
|
float wx, wy = 0;
|
|
|
|
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();
|
|
const float sensitivity = 1;
|
|
|
|
if (GetKeyState(VK_F1) & 1 && !(GetKeyState(VK_TAB) & 0x8000))
|
|
{
|
|
//auto mouse = input_manager.get_mouse_position(false);
|
|
|
|
//Vector2<float> mousePos(mouse.x, mouse.y);
|
|
|
|
|
|
POINT mousePos;
|
|
|
|
GetCursorPos(&mousePos);
|
|
|
|
|
|
float width = GetSystemMetrics(SM_CXSCREEN) / 2;
|
|
float height = GetSystemMetrics(SM_CYSCREEN) / 2;
|
|
|
|
|
|
wx += (mousePos.x) - width;
|
|
wy += (mousePos.y) - height;
|
|
|
|
static glm::vec3 m_lastGyroRotation{}, m_startGyroRotation{};
|
|
static bool m_startGyroRotationSet{};
|
|
|
|
float rotX = (wy * 0.05) * 1; // up/down best
|
|
float rotY = (wx * -0.1) * 1; // left/right
|
|
float rotZ = 0; //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};
|
|
|
|
SetCursorPos(width, height);
|
|
}
|
|
}
|
|
|
|
|
|
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;
|
|
}
|