diff options
| author | 2024-02-16 21:19:17 -0500 | |
|---|---|---|
| committer | 2024-02-17 12:32:33 -0500 | |
| commit | 50ecad547ea7e88301583f17c9f1eea2cc75b0af (patch) | |
| tree | 21e6a6669ea19d05b389b097c0d411654aba4fbf /src/input_common/drivers/android.cpp | |
| parent | hid_core: Prevent crash if we try to iterate through empty color devices list (diff) | |
| download | yuzu-50ecad547ea7e88301583f17c9f1eea2cc75b0af.tar.gz yuzu-50ecad547ea7e88301583f17c9f1eea2cc75b0af.tar.xz yuzu-50ecad547ea7e88301583f17c9f1eea2cc75b0af.zip | |
android: Input mapping
Diffstat (limited to 'src/input_common/drivers/android.cpp')
| -rw-r--r-- | src/input_common/drivers/android.cpp | 324 |
1 files changed, 313 insertions, 11 deletions
diff --git a/src/input_common/drivers/android.cpp b/src/input_common/drivers/android.cpp index b6a03fdc0..e859cc538 100644 --- a/src/input_common/drivers/android.cpp +++ b/src/input_common/drivers/android.cpp | |||
| @@ -1,30 +1,47 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | 2 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 3 | 3 | ||
| 4 | #include <set> | ||
| 5 | #include <common/settings_input.h> | ||
| 6 | #include <jni.h> | ||
| 7 | #include "common/android/android_common.h" | ||
| 8 | #include "common/android/id_cache.h" | ||
| 4 | #include "input_common/drivers/android.h" | 9 | #include "input_common/drivers/android.h" |
| 5 | 10 | ||
| 6 | namespace InputCommon { | 11 | namespace InputCommon { |
| 7 | 12 | ||
| 8 | Android::Android(std::string input_engine_) : InputEngine(std::move(input_engine_)) {} | 13 | Android::Android(std::string input_engine_) : InputEngine(std::move(input_engine_)) {} |
| 9 | 14 | ||
| 10 | void Android::RegisterController(std::size_t controller_number) { | 15 | void Android::RegisterController(jobject j_input_device) { |
| 11 | PreSetController(GetIdentifier(controller_number)); | 16 | auto env = Common::Android::GetEnvForThread(); |
| 17 | const std::string guid = Common::Android::GetJString( | ||
| 18 | env, static_cast<jstring>( | ||
| 19 | env->CallObjectMethod(j_input_device, Common::Android::GetYuzuDeviceGetGUID()))); | ||
| 20 | const s32 port = env->CallIntMethod(j_input_device, Common::Android::GetYuzuDeviceGetPort()); | ||
| 21 | const auto identifier = GetIdentifier(guid, static_cast<size_t>(port)); | ||
| 22 | PreSetController(identifier); | ||
| 23 | |||
| 24 | if (input_devices.find(identifier) != input_devices.end()) { | ||
| 25 | env->DeleteGlobalRef(input_devices[identifier]); | ||
| 26 | } | ||
| 27 | auto new_device = env->NewGlobalRef(j_input_device); | ||
| 28 | input_devices[identifier] = new_device; | ||
| 12 | } | 29 | } |
| 13 | 30 | ||
| 14 | void Android::SetButtonState(std::size_t controller_number, int button_id, bool value) { | 31 | void Android::SetButtonState(std::string guid, size_t port, int button_id, bool value) { |
| 15 | const auto identifier = GetIdentifier(controller_number); | 32 | const auto identifier = GetIdentifier(guid, port); |
| 16 | SetButton(identifier, button_id, value); | 33 | SetButton(identifier, button_id, value); |
| 17 | } | 34 | } |
| 18 | 35 | ||
| 19 | void Android::SetAxisState(std::size_t controller_number, int axis_id, float value) { | 36 | void Android::SetAxisPosition(std::string guid, size_t port, int axis_id, float value) { |
| 20 | const auto identifier = GetIdentifier(controller_number); | 37 | const auto identifier = GetIdentifier(guid, port); |
| 21 | SetAxis(identifier, axis_id, value); | 38 | SetAxis(identifier, axis_id, value); |
| 22 | } | 39 | } |
| 23 | 40 | ||
| 24 | void Android::SetMotionState(std::size_t controller_number, u64 delta_timestamp, float gyro_x, | 41 | void Android::SetMotionState(std::string guid, size_t port, u64 delta_timestamp, float gyro_x, |
| 25 | float gyro_y, float gyro_z, float accel_x, float accel_y, | 42 | float gyro_y, float gyro_z, float accel_x, float accel_y, |
| 26 | float accel_z) { | 43 | float accel_z) { |
| 27 | const auto identifier = GetIdentifier(controller_number); | 44 | const auto identifier = GetIdentifier(guid, port); |
| 28 | const BasicMotion motion_data{ | 45 | const BasicMotion motion_data{ |
| 29 | .gyro_x = gyro_x, | 46 | .gyro_x = gyro_x, |
| 30 | .gyro_y = gyro_y, | 47 | .gyro_y = gyro_y, |
| @@ -37,10 +54,295 @@ void Android::SetMotionState(std::size_t controller_number, u64 delta_timestamp, | |||
| 37 | SetMotion(identifier, 0, motion_data); | 54 | SetMotion(identifier, 0, motion_data); |
| 38 | } | 55 | } |
| 39 | 56 | ||
| 40 | PadIdentifier Android::GetIdentifier(std::size_t controller_number) const { | 57 | Common::Input::DriverResult Android::SetVibration( |
| 58 | [[maybe_unused]] const PadIdentifier& identifier, | ||
| 59 | [[maybe_unused]] const Common::Input::VibrationStatus& vibration) { | ||
| 60 | auto device = input_devices.find(identifier); | ||
| 61 | if (device != input_devices.end()) { | ||
| 62 | Common::Android::RunJNIOnFiber<void>([&](JNIEnv* env) { | ||
| 63 | float average_intensity = | ||
| 64 | static_cast<float>((vibration.high_amplitude + vibration.low_amplitude) / 2.0); | ||
| 65 | env->CallVoidMethod(device->second, Common::Android::GetYuzuDeviceVibrate(), | ||
| 66 | average_intensity); | ||
| 67 | }); | ||
| 68 | return Common::Input::DriverResult::Success; | ||
| 69 | } | ||
| 70 | return Common::Input::DriverResult::NotSupported; | ||
| 71 | } | ||
| 72 | |||
| 73 | bool Android::IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) { | ||
| 74 | auto device = input_devices.find(identifier); | ||
| 75 | if (device != input_devices.end()) { | ||
| 76 | return Common::Android::RunJNIOnFiber<bool>([&](JNIEnv* env) { | ||
| 77 | return static_cast<bool>(env->CallBooleanMethod( | ||
| 78 | device->second, Common::Android::GetYuzuDeviceGetSupportsVibration())); | ||
| 79 | }); | ||
| 80 | } | ||
| 81 | return false; | ||
| 82 | } | ||
| 83 | |||
| 84 | std::vector<Common::ParamPackage> Android::GetInputDevices() const { | ||
| 85 | std::vector<Common::ParamPackage> devices; | ||
| 86 | auto env = Common::Android::GetEnvForThread(); | ||
| 87 | for (const auto& [key, value] : input_devices) { | ||
| 88 | auto name_object = static_cast<jstring>( | ||
| 89 | env->CallObjectMethod(value, Common::Android::GetYuzuDeviceGetName())); | ||
| 90 | const std::string name = | ||
| 91 | fmt::format("{} {}", Common::Android::GetJString(env, name_object), key.port); | ||
| 92 | devices.emplace_back(Common::ParamPackage{ | ||
| 93 | {"engine", GetEngineName()}, | ||
| 94 | {"display", std::move(name)}, | ||
| 95 | {"guid", key.guid.RawString()}, | ||
| 96 | {"port", std::to_string(key.port)}, | ||
| 97 | }); | ||
| 98 | } | ||
| 99 | return devices; | ||
| 100 | } | ||
| 101 | |||
| 102 | std::set<s32> Android::GetDeviceAxes(JNIEnv* env, jobject& j_device) const { | ||
| 103 | auto j_axes = static_cast<jobjectArray>( | ||
| 104 | env->CallObjectMethod(j_device, Common::Android::GetYuzuDeviceGetAxes())); | ||
| 105 | std::set<s32> axes; | ||
| 106 | for (int i = 0; i < env->GetArrayLength(j_axes); ++i) { | ||
| 107 | jobject axis = env->GetObjectArrayElement(j_axes, i); | ||
| 108 | axes.insert(env->GetIntField(axis, Common::Android::GetIntegerValueField())); | ||
| 109 | } | ||
| 110 | return axes; | ||
| 111 | } | ||
| 112 | |||
| 113 | Common::ParamPackage Android::BuildParamPackageForAnalog(PadIdentifier identifier, int axis_x, | ||
| 114 | int axis_y) const { | ||
| 115 | Common::ParamPackage params; | ||
| 116 | params.Set("engine", GetEngineName()); | ||
| 117 | params.Set("port", static_cast<int>(identifier.port)); | ||
| 118 | params.Set("guid", identifier.guid.RawString()); | ||
| 119 | params.Set("axis_x", axis_x); | ||
| 120 | params.Set("axis_y", axis_y); | ||
| 121 | params.Set("offset_x", 0); | ||
| 122 | params.Set("offset_y", 0); | ||
| 123 | params.Set("invert_x", "+"); | ||
| 124 | |||
| 125 | // Invert Y-Axis by default | ||
| 126 | params.Set("invert_y", "-"); | ||
| 127 | return params; | ||
| 128 | } | ||
| 129 | |||
| 130 | Common::ParamPackage Android::BuildAnalogParamPackageForButton(PadIdentifier identifier, s32 axis, | ||
| 131 | bool invert) const { | ||
| 132 | Common::ParamPackage params{}; | ||
| 133 | params.Set("engine", GetEngineName()); | ||
| 134 | params.Set("port", static_cast<int>(identifier.port)); | ||
| 135 | params.Set("guid", identifier.guid.RawString()); | ||
| 136 | params.Set("axis", axis); | ||
| 137 | params.Set("threshold", "0.5"); | ||
| 138 | params.Set("invert", invert ? "-" : "+"); | ||
| 139 | return params; | ||
| 140 | } | ||
| 141 | |||
| 142 | Common::ParamPackage Android::BuildButtonParamPackageForButton(PadIdentifier identifier, | ||
| 143 | s32 button) const { | ||
| 144 | Common::ParamPackage params{}; | ||
| 145 | params.Set("engine", GetEngineName()); | ||
| 146 | params.Set("port", static_cast<int>(identifier.port)); | ||
| 147 | params.Set("guid", identifier.guid.RawString()); | ||
| 148 | params.Set("button", button); | ||
| 149 | return params; | ||
| 150 | } | ||
| 151 | |||
| 152 | bool Android::MatchVID(Common::UUID device, const std::vector<std::string>& vids) const { | ||
| 153 | for (size_t i = 0; i < vids.size(); ++i) { | ||
| 154 | auto fucker = device.RawString(); | ||
| 155 | if (fucker.find(vids[i]) != std::string::npos) { | ||
| 156 | return true; | ||
| 157 | } | ||
| 158 | } | ||
| 159 | return false; | ||
| 160 | } | ||
| 161 | |||
| 162 | AnalogMapping Android::GetAnalogMappingForDevice(const Common::ParamPackage& params) { | ||
| 163 | if (!params.Has("guid") || !params.Has("port")) { | ||
| 164 | return {}; | ||
| 165 | } | ||
| 166 | |||
| 167 | auto identifier = | ||
| 168 | GetIdentifier(params.Get("guid", ""), static_cast<size_t>(params.Get("port", 0))); | ||
| 169 | auto& j_device = input_devices[identifier]; | ||
| 170 | if (j_device == nullptr) { | ||
| 171 | return {}; | ||
| 172 | } | ||
| 173 | |||
| 174 | auto env = Common::Android::GetEnvForThread(); | ||
| 175 | std::set<s32> axes = GetDeviceAxes(env, j_device); | ||
| 176 | if (axes.size() == 0) { | ||
| 177 | return {}; | ||
| 178 | } | ||
| 179 | |||
| 180 | AnalogMapping mapping = {}; | ||
| 181 | if (axes.find(AXIS_X) != axes.end() && axes.find(AXIS_Y) != axes.end()) { | ||
| 182 | mapping.insert_or_assign(Settings::NativeAnalog::LStick, | ||
| 183 | BuildParamPackageForAnalog(identifier, AXIS_X, AXIS_Y)); | ||
| 184 | } | ||
| 185 | |||
| 186 | if (axes.find(AXIS_RX) != axes.end() && axes.find(AXIS_RY) != axes.end()) { | ||
| 187 | mapping.insert_or_assign(Settings::NativeAnalog::RStick, | ||
| 188 | BuildParamPackageForAnalog(identifier, AXIS_RX, AXIS_RY)); | ||
| 189 | } else if (axes.find(AXIS_Z) != axes.end() && axes.find(AXIS_RZ) != axes.end()) { | ||
| 190 | mapping.insert_or_assign(Settings::NativeAnalog::RStick, | ||
| 191 | BuildParamPackageForAnalog(identifier, AXIS_Z, AXIS_RZ)); | ||
| 192 | } | ||
| 193 | return mapping; | ||
| 194 | } | ||
| 195 | |||
| 196 | ButtonMapping Android::GetButtonMappingForDevice(const Common::ParamPackage& params) { | ||
| 197 | if (!params.Has("guid") || !params.Has("port")) { | ||
| 198 | return {}; | ||
| 199 | } | ||
| 200 | |||
| 201 | auto identifier = | ||
| 202 | GetIdentifier(params.Get("guid", ""), static_cast<size_t>(params.Get("port", 0))); | ||
| 203 | auto& j_device = input_devices[identifier]; | ||
| 204 | if (j_device == nullptr) { | ||
| 205 | return {}; | ||
| 206 | } | ||
| 207 | |||
| 208 | auto env = Common::Android::GetEnvForThread(); | ||
| 209 | jintArray j_keys = env->NewIntArray(static_cast<int>(keycode_ids.size())); | ||
| 210 | env->SetIntArrayRegion(j_keys, 0, static_cast<int>(keycode_ids.size()), keycode_ids.data()); | ||
| 211 | auto j_has_keys_object = static_cast<jbooleanArray>( | ||
| 212 | env->CallObjectMethod(j_device, Common::Android::GetYuzuDeviceHasKeys(), j_keys)); | ||
| 213 | jboolean isCopy = false; | ||
| 214 | jboolean* j_has_keys = env->GetBooleanArrayElements(j_has_keys_object, &isCopy); | ||
| 215 | |||
| 216 | std::set<s32> available_keys; | ||
| 217 | for (size_t i = 0; i < keycode_ids.size(); ++i) { | ||
| 218 | if (j_has_keys[i]) { | ||
| 219 | available_keys.insert(keycode_ids[i]); | ||
| 220 | } | ||
| 221 | } | ||
| 222 | |||
| 223 | // Some devices use axes instead of buttons for certain controls so we need all the axes here | ||
| 224 | std::set<s32> axes = GetDeviceAxes(env, j_device); | ||
| 225 | |||
| 226 | ButtonMapping mapping = {}; | ||
| 227 | if (axes.find(AXIS_HAT_X) != axes.end() && axes.find(AXIS_HAT_Y) != axes.end()) { | ||
| 228 | mapping.insert_or_assign(Settings::NativeButton::DUp, | ||
| 229 | BuildAnalogParamPackageForButton(identifier, AXIS_HAT_Y, true)); | ||
| 230 | mapping.insert_or_assign(Settings::NativeButton::DDown, | ||
| 231 | BuildAnalogParamPackageForButton(identifier, AXIS_HAT_Y, false)); | ||
| 232 | mapping.insert_or_assign(Settings::NativeButton::DLeft, | ||
| 233 | BuildAnalogParamPackageForButton(identifier, AXIS_HAT_X, true)); | ||
| 234 | mapping.insert_or_assign(Settings::NativeButton::DRight, | ||
| 235 | BuildAnalogParamPackageForButton(identifier, AXIS_HAT_X, false)); | ||
| 236 | } else if (available_keys.find(KEYCODE_DPAD_UP) != available_keys.end() && | ||
| 237 | available_keys.find(KEYCODE_DPAD_DOWN) != available_keys.end() && | ||
| 238 | available_keys.find(KEYCODE_DPAD_LEFT) != available_keys.end() && | ||
| 239 | available_keys.find(KEYCODE_DPAD_RIGHT) != available_keys.end()) { | ||
| 240 | mapping.insert_or_assign(Settings::NativeButton::DUp, | ||
| 241 | BuildButtonParamPackageForButton(identifier, KEYCODE_DPAD_UP)); | ||
| 242 | mapping.insert_or_assign(Settings::NativeButton::DDown, | ||
| 243 | BuildButtonParamPackageForButton(identifier, KEYCODE_DPAD_DOWN)); | ||
| 244 | mapping.insert_or_assign(Settings::NativeButton::DLeft, | ||
| 245 | BuildButtonParamPackageForButton(identifier, KEYCODE_DPAD_LEFT)); | ||
| 246 | mapping.insert_or_assign(Settings::NativeButton::DRight, | ||
| 247 | BuildButtonParamPackageForButton(identifier, KEYCODE_DPAD_RIGHT)); | ||
| 248 | } | ||
| 249 | |||
| 250 | if (axes.find(AXIS_LTRIGGER) != axes.end()) { | ||
| 251 | mapping.insert_or_assign(Settings::NativeButton::ZL, BuildAnalogParamPackageForButton( | ||
| 252 | identifier, AXIS_LTRIGGER, false)); | ||
| 253 | } else if (available_keys.find(KEYCODE_BUTTON_L2) != available_keys.end()) { | ||
| 254 | mapping.insert_or_assign(Settings::NativeButton::ZL, | ||
| 255 | BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_L2)); | ||
| 256 | } | ||
| 257 | |||
| 258 | if (axes.find(AXIS_RTRIGGER) != axes.end()) { | ||
| 259 | mapping.insert_or_assign(Settings::NativeButton::ZR, BuildAnalogParamPackageForButton( | ||
| 260 | identifier, AXIS_RTRIGGER, false)); | ||
| 261 | } else if (available_keys.find(KEYCODE_BUTTON_R2) != available_keys.end()) { | ||
| 262 | mapping.insert_or_assign(Settings::NativeButton::ZR, | ||
| 263 | BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_R2)); | ||
| 264 | } | ||
| 265 | |||
| 266 | if (available_keys.find(KEYCODE_BUTTON_A) != available_keys.end()) { | ||
| 267 | if (MatchVID(identifier.guid, flipped_ab_vids)) { | ||
| 268 | mapping.insert_or_assign(Settings::NativeButton::B, BuildButtonParamPackageForButton( | ||
| 269 | identifier, KEYCODE_BUTTON_A)); | ||
| 270 | } else { | ||
| 271 | mapping.insert_or_assign(Settings::NativeButton::A, BuildButtonParamPackageForButton( | ||
| 272 | identifier, KEYCODE_BUTTON_A)); | ||
| 273 | } | ||
| 274 | } | ||
| 275 | if (available_keys.find(KEYCODE_BUTTON_B) != available_keys.end()) { | ||
| 276 | if (MatchVID(identifier.guid, flipped_ab_vids)) { | ||
| 277 | mapping.insert_or_assign(Settings::NativeButton::A, BuildButtonParamPackageForButton( | ||
| 278 | identifier, KEYCODE_BUTTON_B)); | ||
| 279 | } else { | ||
| 280 | mapping.insert_or_assign(Settings::NativeButton::B, BuildButtonParamPackageForButton( | ||
| 281 | identifier, KEYCODE_BUTTON_B)); | ||
| 282 | } | ||
| 283 | } | ||
| 284 | if (available_keys.find(KEYCODE_BUTTON_X) != available_keys.end()) { | ||
| 285 | if (MatchVID(identifier.guid, flipped_xy_vids)) { | ||
| 286 | mapping.insert_or_assign(Settings::NativeButton::Y, BuildButtonParamPackageForButton( | ||
| 287 | identifier, KEYCODE_BUTTON_X)); | ||
| 288 | } else { | ||
| 289 | mapping.insert_or_assign(Settings::NativeButton::X, BuildButtonParamPackageForButton( | ||
| 290 | identifier, KEYCODE_BUTTON_X)); | ||
| 291 | } | ||
| 292 | } | ||
| 293 | if (available_keys.find(KEYCODE_BUTTON_Y) != available_keys.end()) { | ||
| 294 | if (MatchVID(identifier.guid, flipped_xy_vids)) { | ||
| 295 | mapping.insert_or_assign(Settings::NativeButton::X, BuildButtonParamPackageForButton( | ||
| 296 | identifier, KEYCODE_BUTTON_Y)); | ||
| 297 | } else { | ||
| 298 | mapping.insert_or_assign(Settings::NativeButton::Y, BuildButtonParamPackageForButton( | ||
| 299 | identifier, KEYCODE_BUTTON_Y)); | ||
| 300 | } | ||
| 301 | } | ||
| 302 | |||
| 303 | if (available_keys.find(KEYCODE_BUTTON_L1) != available_keys.end()) { | ||
| 304 | mapping.insert_or_assign(Settings::NativeButton::L, | ||
| 305 | BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_L1)); | ||
| 306 | } | ||
| 307 | if (available_keys.find(KEYCODE_BUTTON_R1) != available_keys.end()) { | ||
| 308 | mapping.insert_or_assign(Settings::NativeButton::R, | ||
| 309 | BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_R1)); | ||
| 310 | } | ||
| 311 | |||
| 312 | if (available_keys.find(KEYCODE_BUTTON_THUMBL) != available_keys.end()) { | ||
| 313 | mapping.insert_or_assign( | ||
| 314 | Settings::NativeButton::LStick, | ||
| 315 | BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_THUMBL)); | ||
| 316 | } | ||
| 317 | if (available_keys.find(KEYCODE_BUTTON_THUMBR) != available_keys.end()) { | ||
| 318 | mapping.insert_or_assign( | ||
| 319 | Settings::NativeButton::RStick, | ||
| 320 | BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_THUMBR)); | ||
| 321 | } | ||
| 322 | |||
| 323 | if (available_keys.find(KEYCODE_BUTTON_START) != available_keys.end()) { | ||
| 324 | mapping.insert_or_assign( | ||
| 325 | Settings::NativeButton::Plus, | ||
| 326 | BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_START)); | ||
| 327 | } | ||
| 328 | if (available_keys.find(KEYCODE_BUTTON_SELECT) != available_keys.end()) { | ||
| 329 | mapping.insert_or_assign( | ||
| 330 | Settings::NativeButton::Minus, | ||
| 331 | BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_SELECT)); | ||
| 332 | } | ||
| 333 | |||
| 334 | return mapping; | ||
| 335 | } | ||
| 336 | |||
| 337 | Common::Input::ButtonNames Android::GetUIName( | ||
| 338 | [[maybe_unused]] const Common::ParamPackage& params) const { | ||
| 339 | return Common::Input::ButtonNames::Value; | ||
| 340 | } | ||
| 341 | |||
| 342 | PadIdentifier Android::GetIdentifier(const std::string& guid, size_t port) const { | ||
| 41 | return { | 343 | return { |
| 42 | .guid = Common::UUID{}, | 344 | .guid = Common::UUID{guid}, |
| 43 | .port = controller_number, | 345 | .port = port, |
| 44 | .pad = 0, | 346 | .pad = 0, |
| 45 | }; | 347 | }; |
| 46 | } | 348 | } |