summaryrefslogtreecommitdiff
path: root/src/input_common/sdl/sdl_impl.cpp
diff options
context:
space:
mode:
authorGravatar german772021-05-15 22:48:37 -0500
committerGravatar german772021-05-22 18:40:53 -0500
commitea4b7226a6af157be13798b2fcc7121f66d769d8 (patch)
treed54ac17e808b12f32b7edcb84a4e800ba85eb1cd /src/input_common/sdl/sdl_impl.cpp
parentMerge pull request #6248 from A-w-x/intelmesa (diff)
downloadyuzu-ea4b7226a6af157be13798b2fcc7121f66d769d8.tar.gz
yuzu-ea4b7226a6af157be13798b2fcc7121f66d769d8.tar.xz
yuzu-ea4b7226a6af157be13798b2fcc7121f66d769d8.zip
input_common: Add dual joycon support
Diffstat (limited to 'src/input_common/sdl/sdl_impl.cpp')
-rw-r--r--src/input_common/sdl/sdl_impl.cpp281
1 files changed, 223 insertions, 58 deletions
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
index 822d0b555..314b0e773 100644
--- a/src/input_common/sdl/sdl_impl.cpp
+++ b/src/input_common/sdl/sdl_impl.cpp
@@ -18,16 +18,6 @@
18#include <utility> 18#include <utility>
19#include <vector> 19#include <vector>
20 20
21// Ignore -Wimplicit-fallthrough due to https://github.com/libsdl-org/SDL/issues/4307
22#ifdef __clang__
23#pragma clang diagnostic push
24#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
25#endif
26#include <SDL.h>
27#ifdef __clang__
28#pragma clang diagnostic pop
29#endif
30
31#include "common/logging/log.h" 21#include "common/logging/log.h"
32#include "common/math_util.h" 22#include "common/math_util.h"
33#include "common/param_package.h" 23#include "common/param_package.h"
@@ -214,6 +204,40 @@ public:
214 sdl_controller.reset(controller); 204 sdl_controller.reset(controller);
215 } 205 }
216 206
207 bool IsJoyconLeft() const {
208 return std::strstr(GetControllerName().c_str(), "Joy-Con Left") != nullptr;
209 }
210
211 bool IsJoyconRight() const {
212 return std::strstr(GetControllerName().c_str(), "Joy-Con Right") != nullptr;
213 }
214
215 std::string GetControllerName() const {
216 if (sdl_controller) {
217 switch (SDL_GameControllerGetType(sdl_controller.get())) {
218 case SDL_CONTROLLER_TYPE_XBOX360:
219 return "XBox 360 Controller";
220 case SDL_CONTROLLER_TYPE_XBOXONE:
221 return "XBox One Controller";
222 default:
223 break;
224 }
225 const auto name = SDL_GameControllerName(sdl_controller.get());
226 if (name) {
227 return name;
228 }
229 }
230
231 if (sdl_joystick) {
232 const auto name = SDL_JoystickName(sdl_joystick.get());
233 if (name) {
234 return name;
235 }
236 }
237
238 return "Unknown";
239 }
240
217private: 241private:
218 struct State { 242 struct State {
219 std::unordered_map<int, bool> buttons; 243 std::unordered_map<int, bool> buttons;
@@ -856,23 +880,42 @@ SDLState::~SDLState() {
856std::vector<Common::ParamPackage> SDLState::GetInputDevices() { 880std::vector<Common::ParamPackage> SDLState::GetInputDevices() {
857 std::scoped_lock lock(joystick_map_mutex); 881 std::scoped_lock lock(joystick_map_mutex);
858 std::vector<Common::ParamPackage> devices; 882 std::vector<Common::ParamPackage> devices;
883 std::unordered_map<int, std::shared_ptr<SDLJoystick>> joycon_pairs;
884 for (const auto& [key, value] : joystick_map) {
885 for (const auto& joystick : value) {
886 if (!joystick->GetSDLJoystick()) {
887 continue;
888 }
889 std::string name =
890 fmt::format("{} {}", joystick->GetControllerName(), joystick->GetPort());
891 devices.emplace_back(Common::ParamPackage{
892 {"class", "sdl"},
893 {"display", std::move(name)},
894 {"guid", joystick->GetGUID()},
895 {"port", std::to_string(joystick->GetPort())},
896 });
897 if (joystick->IsJoyconLeft()) {
898 joycon_pairs.insert_or_assign(joystick->GetPort(), joystick);
899 }
900 }
901 }
902
903 // Add dual controllers
859 for (const auto& [key, value] : joystick_map) { 904 for (const auto& [key, value] : joystick_map) {
860 for (const auto& joystick : value) { 905 for (const auto& joystick : value) {
861 if (auto* const controller = joystick->GetSDLGameController()) { 906 if (joystick->IsJoyconRight()) {
907 if (!joycon_pairs.contains(joystick->GetPort())) {
908 continue;
909 }
910 const auto joystick2 = joycon_pairs.at(joystick->GetPort());
911
862 std::string name = 912 std::string name =
863 fmt::format("{} {}", GetControllerName(controller), joystick->GetPort()); 913 fmt::format("{} {}", "Nintendo Dual Joy-Con", joystick->GetPort());
864 devices.emplace_back(Common::ParamPackage{
865 {"class", "sdl"},
866 {"display", std::move(name)},
867 {"guid", joystick->GetGUID()},
868 {"port", std::to_string(joystick->GetPort())},
869 });
870 } else if (auto* const joy = joystick->GetSDLJoystick()) {
871 std::string name = fmt::format("{} {}", SDL_JoystickName(joy), joystick->GetPort());
872 devices.emplace_back(Common::ParamPackage{ 914 devices.emplace_back(Common::ParamPackage{
873 {"class", "sdl"}, 915 {"class", "sdl"},
874 {"display", std::move(name)}, 916 {"display", std::move(name)},
875 {"guid", joystick->GetGUID()}, 917 {"guid", joystick->GetGUID()},
918 {"guid2", joystick2->GetGUID()},
876 {"port", std::to_string(joystick->GetPort())}, 919 {"port", std::to_string(joystick->GetPort())},
877 }); 920 });
878 } 921 }
@@ -881,17 +924,6 @@ std::vector<Common::ParamPackage> SDLState::GetInputDevices() {
881 return devices; 924 return devices;
882} 925}
883 926
884std::string SDLState::GetControllerName(SDL_GameController* controller) const {
885 switch (SDL_GameControllerGetType(controller)) {
886 case SDL_CONTROLLER_TYPE_XBOX360:
887 return "XBox 360 Controller";
888 case SDL_CONTROLLER_TYPE_XBOXONE:
889 return "XBox One Controller";
890 default:
891 return SDL_GameControllerName(controller);
892 }
893}
894
895namespace { 927namespace {
896Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, s32 axis, 928Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, s32 axis,
897 float value = 0.1f) { 929 float value = 0.1f) {
@@ -1071,24 +1103,48 @@ ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& pa
1071 return {}; 1103 return {};
1072 } 1104 }
1073 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0)); 1105 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
1106
1074 auto* controller = joystick->GetSDLGameController(); 1107 auto* controller = joystick->GetSDLGameController();
1075 if (controller == nullptr) { 1108 if (controller == nullptr) {
1076 return {}; 1109 return {};
1077 } 1110 }
1078 1111
1079 const bool invert =
1080 SDL_GameControllerGetType(controller) != SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO;
1081
1082 // This list is missing ZL/ZR since those are not considered buttons in SDL GameController. 1112 // This list is missing ZL/ZR since those are not considered buttons in SDL GameController.
1083 // We will add those afterwards 1113 // We will add those afterwards
1084 // This list also excludes Screenshot since theres not really a mapping for that 1114 // This list also excludes Screenshot since theres not really a mapping for that
1085 using ButtonBindings = 1115 ButtonBindings switch_to_sdl_button;
1086 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 17>; 1116
1087 const ButtonBindings switch_to_sdl_button{{ 1117 if (SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) {
1088 {Settings::NativeButton::A, invert ? SDL_CONTROLLER_BUTTON_B : SDL_CONTROLLER_BUTTON_A}, 1118 switch_to_sdl_button = GetNintendoButtonBinding(joystick);
1089 {Settings::NativeButton::B, invert ? SDL_CONTROLLER_BUTTON_A : SDL_CONTROLLER_BUTTON_B}, 1119 } else {
1090 {Settings::NativeButton::X, invert ? SDL_CONTROLLER_BUTTON_Y : SDL_CONTROLLER_BUTTON_X}, 1120 switch_to_sdl_button = GetDefaultButtonBinding();
1091 {Settings::NativeButton::Y, invert ? SDL_CONTROLLER_BUTTON_X : SDL_CONTROLLER_BUTTON_Y}, 1121 }
1122
1123 // Add the missing bindings for ZL/ZR
1124 static constexpr ZButtonBindings switch_to_sdl_axis{{
1125 {Settings::NativeButton::ZL, SDL_CONTROLLER_AXIS_TRIGGERLEFT},
1126 {Settings::NativeButton::ZR, SDL_CONTROLLER_AXIS_TRIGGERRIGHT},
1127 }};
1128
1129 // Parameters contain two joysticks return dual
1130 if (params.Has("guid2")) {
1131 const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0));
1132
1133 if (joystick2->GetSDLGameController() != nullptr) {
1134 return GetDualControllerMapping(joystick, joystick2, switch_to_sdl_button,
1135 switch_to_sdl_axis);
1136 }
1137 }
1138
1139 return GetSingleControllerMapping(joystick, switch_to_sdl_button, switch_to_sdl_axis);
1140}
1141
1142ButtonBindings SDLState::GetDefaultButtonBinding() const {
1143 return {
1144 std::pair{Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B},
1145 {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A},
1146 {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y},
1147 {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X},
1092 {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK}, 1148 {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK},
1093 {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK}, 1149 {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK},
1094 {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER}, 1150 {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
@@ -1102,18 +1158,51 @@ ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& pa
1102 {Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER}, 1158 {Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
1103 {Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER}, 1159 {Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
1104 {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE}, 1160 {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
1105 }}; 1161 };
1162}
1106 1163
1107 // Add the missing bindings for ZL/ZR 1164ButtonBindings SDLState::GetNintendoButtonBinding(
1108 using ZBindings = 1165 const std::shared_ptr<SDLJoystick>& joystick) const {
1109 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>; 1166 // Default SL/SR mapping for pro controllers
1110 static constexpr ZBindings switch_to_sdl_axis{{ 1167 auto sl_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER;
1111 {Settings::NativeButton::ZL, SDL_CONTROLLER_AXIS_TRIGGERLEFT}, 1168 auto sr_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER;
1112 {Settings::NativeButton::ZR, SDL_CONTROLLER_AXIS_TRIGGERRIGHT}, 1169
1113 }}; 1170 if (joystick->IsJoyconLeft()) {
1171 sl_button = SDL_CONTROLLER_BUTTON_PADDLE2;
1172 sr_button = SDL_CONTROLLER_BUTTON_PADDLE4;
1173 }
1174 if (joystick->IsJoyconRight()) {
1175 sl_button = SDL_CONTROLLER_BUTTON_PADDLE3;
1176 sr_button = SDL_CONTROLLER_BUTTON_PADDLE1;
1177 }
1114 1178
1179 return {
1180 std::pair{Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_A},
1181 {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_B},
1182 {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_X},
1183 {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_Y},
1184 {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK},
1185 {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK},
1186 {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
1187 {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
1188 {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START},
1189 {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK},
1190 {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT},
1191 {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP},
1192 {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
1193 {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN},
1194 {Settings::NativeButton::SL, sl_button},
1195 {Settings::NativeButton::SR, sr_button},
1196 {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
1197 };
1198}
1199
1200ButtonMapping SDLState::GetSingleControllerMapping(
1201 const std::shared_ptr<SDLJoystick>& joystick, const ButtonBindings& switch_to_sdl_button,
1202 const ZButtonBindings& switch_to_sdl_axis) const {
1115 ButtonMapping mapping; 1203 ButtonMapping mapping;
1116 mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size()); 1204 mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size());
1205 auto* controller = joystick->GetSDLGameController();
1117 1206
1118 for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) { 1207 for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) {
1119 const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button); 1208 const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button);
@@ -1131,11 +1220,68 @@ ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& pa
1131 return mapping; 1220 return mapping;
1132} 1221}
1133 1222
1223ButtonMapping SDLState::GetDualControllerMapping(const std::shared_ptr<SDLJoystick>& joystick,
1224 const std::shared_ptr<SDLJoystick>& joystick2,
1225 const ButtonBindings& switch_to_sdl_button,
1226 const ZButtonBindings& switch_to_sdl_axis) const {
1227 ButtonMapping mapping;
1228 mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size());
1229 auto* controller = joystick->GetSDLGameController();
1230 auto* controller2 = joystick2->GetSDLGameController();
1231
1232 for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) {
1233 if (IsButtonOnLeftSide(switch_button)) {
1234 const auto& binding = SDL_GameControllerGetBindForButton(controller2, sdl_button);
1235 mapping.insert_or_assign(
1236 switch_button,
1237 BuildParamPackageForBinding(joystick2->GetPort(), joystick2->GetGUID(), binding));
1238 continue;
1239 }
1240 const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button);
1241 mapping.insert_or_assign(
1242 switch_button,
1243 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
1244 }
1245 for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) {
1246 if (IsButtonOnLeftSide(switch_button)) {
1247 const auto& binding = SDL_GameControllerGetBindForAxis(controller2, sdl_axis);
1248 mapping.insert_or_assign(
1249 switch_button,
1250 BuildParamPackageForBinding(joystick2->GetPort(), joystick2->GetGUID(), binding));
1251 continue;
1252 }
1253 const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis);
1254 mapping.insert_or_assign(
1255 switch_button,
1256 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
1257 }
1258
1259 return mapping;
1260}
1261
1262bool SDLState::IsButtonOnLeftSide(Settings::NativeButton::Values button) const {
1263 switch (button) {
1264 case Settings::NativeButton::DDown:
1265 case Settings::NativeButton::DLeft:
1266 case Settings::NativeButton::DRight:
1267 case Settings::NativeButton::DUp:
1268 case Settings::NativeButton::L:
1269 case Settings::NativeButton::LStick:
1270 case Settings::NativeButton::Minus:
1271 case Settings::NativeButton::Screenshot:
1272 case Settings::NativeButton::ZL:
1273 return true;
1274 default:
1275 return false;
1276 }
1277}
1278
1134AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& params) { 1279AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
1135 if (!params.Has("guid") || !params.Has("port")) { 1280 if (!params.Has("guid") || !params.Has("port")) {
1136 return {}; 1281 return {};
1137 } 1282 }
1138 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0)); 1283 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
1284 const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0));
1139 auto* controller = joystick->GetSDLGameController(); 1285 auto* controller = joystick->GetSDLGameController();
1140 if (controller == nullptr) { 1286 if (controller == nullptr) {
1141 return {}; 1287 return {};
@@ -1146,10 +1292,17 @@ AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& pa
1146 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX); 1292 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX);
1147 const auto& binding_left_y = 1293 const auto& binding_left_y =
1148 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY); 1294 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
1149 mapping.insert_or_assign(Settings::NativeAnalog::LStick, 1295 if (params.Has("guid2")) {
1150 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), 1296 mapping.insert_or_assign(
1151 binding_left_x.value.axis, 1297 Settings::NativeAnalog::LStick,
1152 binding_left_y.value.axis)); 1298 BuildParamPackageForAnalog(joystick2->GetPort(), joystick2->GetGUID(),
1299 binding_left_x.value.axis, binding_left_y.value.axis));
1300 } else {
1301 mapping.insert_or_assign(
1302 Settings::NativeAnalog::LStick,
1303 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
1304 binding_left_x.value.axis, binding_left_y.value.axis));
1305 }
1153 const auto& binding_right_x = 1306 const auto& binding_right_x =
1154 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX); 1307 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX);
1155 const auto& binding_right_y = 1308 const auto& binding_right_y =
@@ -1166,20 +1319,32 @@ MotionMapping SDLState::GetMotionMappingForDevice(const Common::ParamPackage& pa
1166 return {}; 1319 return {};
1167 } 1320 }
1168 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0)); 1321 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
1322 const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0));
1169 auto* controller = joystick->GetSDLGameController(); 1323 auto* controller = joystick->GetSDLGameController();
1170 if (controller == nullptr) { 1324 if (controller == nullptr) {
1171 return {}; 1325 return {};
1172 } 1326 }
1173 1327
1328 MotionMapping mapping = {};
1174 joystick->EnableMotion(); 1329 joystick->EnableMotion();
1175 1330
1176 if (!joystick->HasGyro() && !joystick->HasAccel()) { 1331 if (joystick->HasGyro() || joystick->HasAccel()) {
1177 return {}; 1332 mapping.insert_or_assign(Settings::NativeMotion::MotionRight,
1333 BuildMotionParam(joystick->GetPort(), joystick->GetGUID()));
1334 }
1335 if (params.Has("guid2")) {
1336 joystick2->EnableMotion();
1337 if (joystick2->HasGyro() || joystick2->HasAccel()) {
1338 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft,
1339 BuildMotionParam(joystick2->GetPort(), joystick2->GetGUID()));
1340 }
1341 } else {
1342 if (joystick->HasGyro() || joystick->HasAccel()) {
1343 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft,
1344 BuildMotionParam(joystick->GetPort(), joystick->GetGUID()));
1345 }
1178 } 1346 }
1179 1347
1180 MotionMapping mapping = {};
1181 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft,
1182 BuildMotionParam(joystick->GetPort(), joystick->GetGUID()));
1183 return mapping; 1348 return mapping;
1184} 1349}
1185namespace Polling { 1350namespace Polling {