647 lines
29 KiB
C++
647 lines
29 KiB
C++
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
|
// SPDX-FileCopyrightText: 2024 suyu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#include <common/fs/fs.h>
|
|
#include <common/fs/path_util.h>
|
|
#include <common/settings.h>
|
|
#include <hid_core/hid_types.h>
|
|
#include <jni.h>
|
|
|
|
#include "android_config.h"
|
|
#include "common/android/android_common.h"
|
|
#include "common/android/id_cache.h"
|
|
#include "hid_core/frontend/emulated_controller.h"
|
|
#include "hid_core/hid_core.h"
|
|
#include "input_common/drivers/android.h"
|
|
#include "input_common/drivers/touch_screen.h"
|
|
#include "input_common/drivers/virtual_amiibo.h"
|
|
#include "input_common/drivers/virtual_gamepad.h"
|
|
#include "native.h"
|
|
|
|
std::unordered_map<std::string, std::unique_ptr<AndroidConfig>> map_profiles;
|
|
|
|
bool IsHandheldOnly() {
|
|
const auto npad_style_set =
|
|
EmulationSession::GetInstance().System().HIDCore().GetSupportedStyleTag();
|
|
|
|
if (npad_style_set.fullkey == 1) {
|
|
return false;
|
|
}
|
|
|
|
if (npad_style_set.handheld == 0) {
|
|
return false;
|
|
}
|
|
|
|
return !Settings::IsDockedMode();
|
|
}
|
|
|
|
std::filesystem::path GetNameWithoutExtension(std::filesystem::path filename) {
|
|
return filename.replace_extension();
|
|
}
|
|
|
|
bool IsProfileNameValid(std::string_view profile_name) {
|
|
return profile_name.find_first_of("<>:;\"/\\|,.!?*") == std::string::npos;
|
|
}
|
|
|
|
bool ProfileExistsInFilesystem(std::string_view profile_name) {
|
|
return Common::FS::Exists(Common::FS::GetSuyuPath(Common::FS::SuyuPath::ConfigDir) / "input" /
|
|
fmt::format("{}.ini", profile_name));
|
|
}
|
|
|
|
bool ProfileExistsInMap(const std::string& profile_name) {
|
|
return map_profiles.find(profile_name) != map_profiles.end();
|
|
}
|
|
|
|
bool SaveProfile(const std::string& profile_name, std::size_t player_index) {
|
|
if (!ProfileExistsInMap(profile_name)) {
|
|
return false;
|
|
}
|
|
|
|
Settings::values.players.GetValue()[player_index].profile_name = profile_name;
|
|
map_profiles[profile_name]->SaveAndroidControlPlayerValues(player_index);
|
|
return true;
|
|
}
|
|
|
|
bool LoadProfile(std::string& profile_name, std::size_t player_index) {
|
|
if (!ProfileExistsInMap(profile_name)) {
|
|
return false;
|
|
}
|
|
|
|
if (!ProfileExistsInFilesystem(profile_name)) {
|
|
map_profiles.erase(profile_name);
|
|
return false;
|
|
}
|
|
|
|
LOG_INFO(Config, "Loading input profile `{}`", profile_name);
|
|
|
|
Settings::values.players.GetValue()[player_index].profile_name = profile_name;
|
|
map_profiles[profile_name]->ReadAndroidControlPlayerValues(player_index);
|
|
return true;
|
|
}
|
|
|
|
void ApplyControllerConfig(size_t player_index,
|
|
const std::function<void(Core::HID::EmulatedController*)>& apply) {
|
|
auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
|
|
if (player_index == 0) {
|
|
auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
|
|
auto* player_one = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
|
|
handheld->EnableConfiguration();
|
|
player_one->EnableConfiguration();
|
|
apply(handheld);
|
|
apply(player_one);
|
|
handheld->DisableConfiguration();
|
|
player_one->DisableConfiguration();
|
|
handheld->SaveCurrentConfig();
|
|
player_one->SaveCurrentConfig();
|
|
} else {
|
|
auto* controller = hid_core.GetEmulatedControllerByIndex(player_index);
|
|
controller->EnableConfiguration();
|
|
apply(controller);
|
|
controller->DisableConfiguration();
|
|
controller->SaveCurrentConfig();
|
|
}
|
|
}
|
|
|
|
std::vector<s32> GetSupportedStyles(int player_index) {
|
|
auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
|
|
const auto npad_style_set = hid_core.GetSupportedStyleTag();
|
|
std::vector<s32> supported_indexes;
|
|
if (npad_style_set.fullkey == 1) {
|
|
supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::Fullkey));
|
|
}
|
|
|
|
if (npad_style_set.joycon_dual == 1) {
|
|
supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::JoyconDual));
|
|
}
|
|
|
|
if (npad_style_set.joycon_left == 1) {
|
|
supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::JoyconLeft));
|
|
}
|
|
|
|
if (npad_style_set.joycon_right == 1) {
|
|
supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::JoyconRight));
|
|
}
|
|
|
|
if (player_index == 0 && npad_style_set.handheld == 1) {
|
|
supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::Handheld));
|
|
}
|
|
|
|
if (npad_style_set.gamecube == 1) {
|
|
supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::GameCube));
|
|
}
|
|
|
|
return supported_indexes;
|
|
}
|
|
|
|
void ConnectController(size_t player_index, bool connected) {
|
|
auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
|
|
ApplyControllerConfig(player_index, [&](Core::HID::EmulatedController* controller) {
|
|
auto supported_styles = GetSupportedStyles(player_index);
|
|
auto controller_style = controller->GetNpadStyleIndex(true);
|
|
auto style = std::find(supported_styles.begin(), supported_styles.end(),
|
|
static_cast<int>(controller_style));
|
|
if (style == supported_styles.end() && !supported_styles.empty()) {
|
|
controller->SetNpadStyleIndex(
|
|
static_cast<Core::HID::NpadStyleIndex>(supported_styles[0]));
|
|
}
|
|
});
|
|
|
|
if (player_index == 0) {
|
|
auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
|
|
auto* player_one = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
|
|
handheld->EnableConfiguration();
|
|
player_one->EnableConfiguration();
|
|
if (player_one->GetNpadStyleIndex(true) == Core::HID::NpadStyleIndex::Handheld) {
|
|
if (connected) {
|
|
handheld->Connect();
|
|
} else {
|
|
handheld->Disconnect();
|
|
}
|
|
player_one->Disconnect();
|
|
} else {
|
|
if (connected) {
|
|
player_one->Connect();
|
|
} else {
|
|
player_one->Disconnect();
|
|
}
|
|
handheld->Disconnect();
|
|
}
|
|
handheld->DisableConfiguration();
|
|
player_one->DisableConfiguration();
|
|
handheld->SaveCurrentConfig();
|
|
player_one->SaveCurrentConfig();
|
|
} else {
|
|
auto* controller = hid_core.GetEmulatedControllerByIndex(player_index);
|
|
controller->EnableConfiguration();
|
|
if (connected) {
|
|
controller->Connect();
|
|
} else {
|
|
controller->Disconnect();
|
|
}
|
|
controller->DisableConfiguration();
|
|
controller->SaveCurrentConfig();
|
|
}
|
|
}
|
|
|
|
extern "C" {
|
|
|
|
jboolean Java_org_suyu_suyu_1emu_features_input_NativeInput_isHandheldOnly(JNIEnv* env,
|
|
jobject j_obj) {
|
|
return IsHandheldOnly();
|
|
}
|
|
|
|
void Java_org_suyu_suyu_1emu_features_input_NativeInput_onGamePadButtonEvent(
|
|
JNIEnv* env, jobject j_obj, jstring j_guid, jint j_port, jint j_button_id, jint j_action) {
|
|
EmulationSession::GetInstance().GetInputSubsystem().GetAndroid()->SetButtonState(
|
|
Common::Android::GetJString(env, j_guid), j_port, j_button_id, j_action != 0);
|
|
}
|
|
|
|
void Java_org_suyu_suyu_1emu_features_input_NativeInput_onGamePadAxisEvent(
|
|
JNIEnv* env, jobject j_obj, jstring j_guid, jint j_port, jint j_stick_id, jfloat j_value) {
|
|
EmulationSession::GetInstance().GetInputSubsystem().GetAndroid()->SetAxisPosition(
|
|
Common::Android::GetJString(env, j_guid), j_port, j_stick_id, j_value);
|
|
}
|
|
|
|
void Java_org_suyu_suyu_1emu_features_input_NativeInput_onGamePadMotionEvent(
|
|
JNIEnv* env, jobject j_obj, jstring j_guid, jint j_port, jlong j_delta_timestamp,
|
|
jfloat j_x_gyro, jfloat j_y_gyro, jfloat j_z_gyro, jfloat j_x_accel, jfloat j_y_accel,
|
|
jfloat j_z_accel) {
|
|
EmulationSession::GetInstance().GetInputSubsystem().GetAndroid()->SetMotionState(
|
|
Common::Android::GetJString(env, j_guid), j_port, j_delta_timestamp, j_x_gyro, j_y_gyro,
|
|
j_z_gyro, j_x_accel, j_y_accel, j_z_accel);
|
|
}
|
|
|
|
void Java_org_suyu_suyu_1emu_features_input_NativeInput_onReadNfcTag(JNIEnv* env, jobject j_obj,
|
|
jbyteArray j_data) {
|
|
jboolean isCopy{false};
|
|
std::span<u8> data(reinterpret_cast<u8*>(env->GetByteArrayElements(j_data, &isCopy)),
|
|
static_cast<size_t>(env->GetArrayLength(j_data)));
|
|
|
|
if (EmulationSession::GetInstance().IsRunning()) {
|
|
EmulationSession::GetInstance().GetInputSubsystem().GetVirtualAmiibo()->LoadAmiibo(data);
|
|
}
|
|
}
|
|
|
|
void Java_org_suyu_suyu_1emu_features_input_NativeInput_onRemoveNfcTag(JNIEnv* env, jobject j_obj) {
|
|
if (EmulationSession::GetInstance().IsRunning()) {
|
|
EmulationSession::GetInstance().GetInputSubsystem().GetVirtualAmiibo()->CloseAmiibo();
|
|
}
|
|
}
|
|
|
|
void Java_org_suyu_suyu_1emu_features_input_NativeInput_onTouchPressed(JNIEnv* env, jobject j_obj,
|
|
jint j_id, jfloat j_x_axis,
|
|
jfloat j_y_axis) {
|
|
if (EmulationSession::GetInstance().IsRunning()) {
|
|
EmulationSession::GetInstance().Window().OnTouchPressed(j_id, j_x_axis, j_y_axis);
|
|
}
|
|
}
|
|
|
|
void Java_org_suyu_suyu_1emu_features_input_NativeInput_onTouchMoved(JNIEnv* env, jobject j_obj,
|
|
jint j_id, jfloat j_x_axis,
|
|
jfloat j_y_axis) {
|
|
if (EmulationSession::GetInstance().IsRunning()) {
|
|
EmulationSession::GetInstance().Window().OnTouchMoved(j_id, j_x_axis, j_y_axis);
|
|
}
|
|
}
|
|
|
|
void Java_org_suyu_suyu_1emu_features_input_NativeInput_onTouchReleased(JNIEnv* env, jobject j_obj,
|
|
jint j_id) {
|
|
if (EmulationSession::GetInstance().IsRunning()) {
|
|
EmulationSession::GetInstance().Window().OnTouchReleased(j_id);
|
|
}
|
|
}
|
|
|
|
void Java_org_suyu_suyu_1emu_features_input_NativeInput_onOverlayButtonEventImpl(
|
|
JNIEnv* env, jobject j_obj, jint j_port, jint j_button_id, jint j_action) {
|
|
if (EmulationSession::GetInstance().IsRunning()) {
|
|
EmulationSession::GetInstance().GetInputSubsystem().GetVirtualGamepad()->SetButtonState(
|
|
j_port, j_button_id, j_action == 1);
|
|
}
|
|
}
|
|
|
|
void Java_org_suyu_suyu_1emu_features_input_NativeInput_onOverlayJoystickEventImpl(
|
|
JNIEnv* env, jobject j_obj, jint j_port, jint j_stick_id, jfloat j_x_axis, jfloat j_y_axis) {
|
|
if (EmulationSession::GetInstance().IsRunning()) {
|
|
EmulationSession::GetInstance().GetInputSubsystem().GetVirtualGamepad()->SetStickPosition(
|
|
j_port, j_stick_id, j_x_axis, j_y_axis);
|
|
}
|
|
}
|
|
|
|
void Java_org_suyu_suyu_1emu_features_input_NativeInput_onDeviceMotionEvent(
|
|
JNIEnv* env, jobject j_obj, jint j_port, jlong j_delta_timestamp, jfloat j_x_gyro,
|
|
jfloat j_y_gyro, jfloat j_z_gyro, jfloat j_x_accel, jfloat j_y_accel, jfloat j_z_accel) {
|
|
if (EmulationSession::GetInstance().IsRunning()) {
|
|
EmulationSession::GetInstance().GetInputSubsystem().GetVirtualGamepad()->SetMotionState(
|
|
j_port, j_delta_timestamp, j_x_gyro, j_y_gyro, j_z_gyro, j_x_accel, j_y_accel,
|
|
j_z_accel);
|
|
}
|
|
}
|
|
|
|
void Java_org_suyu_suyu_1emu_features_input_NativeInput_reloadInputDevices(JNIEnv* env,
|
|
jobject j_obj) {
|
|
EmulationSession::GetInstance().System().HIDCore().ReloadInputDevices();
|
|
}
|
|
|
|
void Java_org_suyu_suyu_1emu_features_input_NativeInput_registerController(JNIEnv* env,
|
|
jobject j_obj,
|
|
jobject j_device) {
|
|
EmulationSession::GetInstance().GetInputSubsystem().GetAndroid()->RegisterController(j_device);
|
|
}
|
|
|
|
jobjectArray Java_org_suyu_suyu_1emu_features_input_NativeInput_getInputDevices(JNIEnv* env,
|
|
jobject j_obj) {
|
|
auto devices = EmulationSession::GetInstance().GetInputSubsystem().GetInputDevices();
|
|
jobjectArray jdevices = env->NewObjectArray(devices.size(), Common::Android::GetStringClass(),
|
|
Common::Android::ToJString(env, ""));
|
|
for (size_t i = 0; i < devices.size(); ++i) {
|
|
env->SetObjectArrayElement(jdevices, i,
|
|
Common::Android::ToJString(env, devices[i].Serialize()));
|
|
}
|
|
return jdevices;
|
|
}
|
|
|
|
void Java_org_suyu_suyu_1emu_features_input_NativeInput_loadInputProfiles(JNIEnv* env,
|
|
jobject j_obj) {
|
|
map_profiles.clear();
|
|
const auto input_profile_loc =
|
|
Common::FS::GetSuyuPath(Common::FS::SuyuPath::ConfigDir) / "input";
|
|
|
|
if (Common::FS::IsDir(input_profile_loc)) {
|
|
Common::FS::IterateDirEntries(
|
|
input_profile_loc,
|
|
[&](const std::filesystem::path& full_path) {
|
|
const auto filename = full_path.filename();
|
|
const auto name_without_ext =
|
|
Common::FS::PathToUTF8String(GetNameWithoutExtension(filename));
|
|
|
|
if (filename.extension() == ".ini" && IsProfileNameValid(name_without_ext)) {
|
|
map_profiles.insert_or_assign(
|
|
name_without_ext, std::make_unique<AndroidConfig>(
|
|
name_without_ext, Config::ConfigType::InputProfile));
|
|
}
|
|
|
|
return true;
|
|
},
|
|
Common::FS::DirEntryFilter::File);
|
|
}
|
|
}
|
|
|
|
jobjectArray Java_org_suyu_suyu_1emu_features_input_NativeInput_getInputProfileNames(
|
|
JNIEnv* env, jobject j_obj) {
|
|
std::vector<std::string> profile_names;
|
|
profile_names.reserve(map_profiles.size());
|
|
|
|
auto it = map_profiles.cbegin();
|
|
while (it != map_profiles.cend()) {
|
|
const auto& [profile_name, config] = *it;
|
|
if (!ProfileExistsInFilesystem(profile_name)) {
|
|
it = map_profiles.erase(it);
|
|
continue;
|
|
}
|
|
|
|
profile_names.push_back(profile_name);
|
|
++it;
|
|
}
|
|
|
|
std::stable_sort(profile_names.begin(), profile_names.end());
|
|
|
|
jobjectArray j_profile_names =
|
|
env->NewObjectArray(profile_names.size(), Common::Android::GetStringClass(),
|
|
Common::Android::ToJString(env, ""));
|
|
for (size_t i = 0; i < profile_names.size(); ++i) {
|
|
env->SetObjectArrayElement(j_profile_names, i,
|
|
Common::Android::ToJString(env, profile_names[i]));
|
|
}
|
|
|
|
return j_profile_names;
|
|
}
|
|
|
|
jboolean Java_org_suyu_suyu_1emu_features_input_NativeInput_isProfileNameValid(JNIEnv* env,
|
|
jobject j_obj,
|
|
jstring j_name) {
|
|
return Common::Android::GetJString(env, j_name).find_first_of("<>:;\"/\\|,.!?*") ==
|
|
std::string::npos;
|
|
}
|
|
|
|
jboolean Java_org_suyu_suyu_1emu_features_input_NativeInput_createProfile(JNIEnv* env,
|
|
jobject j_obj,
|
|
jstring j_name,
|
|
jint j_player_index) {
|
|
auto profile_name = Common::Android::GetJString(env, j_name);
|
|
if (ProfileExistsInMap(profile_name)) {
|
|
return false;
|
|
}
|
|
|
|
map_profiles.insert_or_assign(
|
|
profile_name,
|
|
std::make_unique<AndroidConfig>(profile_name, Config::ConfigType::InputProfile));
|
|
|
|
return SaveProfile(profile_name, j_player_index);
|
|
}
|
|
|
|
jboolean Java_org_suyu_suyu_1emu_features_input_NativeInput_deleteProfile(JNIEnv* env,
|
|
jobject j_obj,
|
|
jstring j_name,
|
|
jint j_player_index) {
|
|
auto profile_name = Common::Android::GetJString(env, j_name);
|
|
if (!ProfileExistsInMap(profile_name)) {
|
|
return false;
|
|
}
|
|
|
|
if (!ProfileExistsInFilesystem(profile_name) ||
|
|
Common::FS::RemoveFile(map_profiles[profile_name]->GetConfigFilePath())) {
|
|
map_profiles.erase(profile_name);
|
|
}
|
|
|
|
Settings::values.players.GetValue()[j_player_index].profile_name = "";
|
|
return !ProfileExistsInMap(profile_name) && !ProfileExistsInFilesystem(profile_name);
|
|
}
|
|
|
|
jboolean Java_org_suyu_suyu_1emu_features_input_NativeInput_loadProfile(JNIEnv* env, jobject j_obj,
|
|
jstring j_name,
|
|
jint j_player_index) {
|
|
auto profile_name = Common::Android::GetJString(env, j_name);
|
|
return LoadProfile(profile_name, j_player_index);
|
|
}
|
|
|
|
jboolean Java_org_suyu_suyu_1emu_features_input_NativeInput_saveProfile(JNIEnv* env, jobject j_obj,
|
|
jstring j_name,
|
|
jint j_player_index) {
|
|
auto profile_name = Common::Android::GetJString(env, j_name);
|
|
return SaveProfile(profile_name, j_player_index);
|
|
}
|
|
|
|
void Java_org_suyu_suyu_1emu_features_input_NativeInput_loadPerGameConfiguration(
|
|
JNIEnv* env, jobject j_obj, jint j_player_index, jint j_selected_index,
|
|
jstring j_selected_profile_name) {
|
|
static constexpr size_t HANDHELD_INDEX = 8;
|
|
|
|
auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
|
|
Settings::values.players.SetGlobal(false);
|
|
|
|
auto profile_name = Common::Android::GetJString(env, j_selected_profile_name);
|
|
auto* emulated_controller = hid_core.GetEmulatedControllerByIndex(j_player_index);
|
|
|
|
if (j_selected_index == 0) {
|
|
Settings::values.players.GetValue()[j_player_index].profile_name = "";
|
|
if (j_player_index == 0) {
|
|
Settings::values.players.GetValue()[HANDHELD_INDEX] = {};
|
|
}
|
|
Settings::values.players.SetGlobal(true);
|
|
emulated_controller->ReloadFromSettings();
|
|
return;
|
|
}
|
|
if (profile_name.empty()) {
|
|
return;
|
|
}
|
|
auto& player = Settings::values.players.GetValue()[j_player_index];
|
|
auto& global_player = Settings::values.players.GetValue(true)[j_player_index];
|
|
player.profile_name = profile_name;
|
|
global_player.profile_name = profile_name;
|
|
// Read from the profile into the custom player settings
|
|
LoadProfile(profile_name, j_player_index);
|
|
// Make sure the controller is connected
|
|
player.connected = true;
|
|
|
|
emulated_controller->ReloadFromSettings();
|
|
|
|
if (j_player_index > 0) {
|
|
return;
|
|
}
|
|
// Handle Handheld cases
|
|
auto& handheld_player = Settings::values.players.GetValue()[HANDHELD_INDEX];
|
|
auto* handheld_controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
|
|
if (player.controller_type == Settings::ControllerType::Handheld) {
|
|
handheld_player = player;
|
|
} else {
|
|
handheld_player = {};
|
|
}
|
|
handheld_controller->ReloadFromSettings();
|
|
}
|
|
|
|
void Java_org_suyu_suyu_1emu_features_input_NativeInput_beginMapping(JNIEnv* env, jobject j_obj,
|
|
jint jtype) {
|
|
EmulationSession::GetInstance().GetInputSubsystem().BeginMapping(
|
|
static_cast<InputCommon::Polling::InputType>(jtype));
|
|
}
|
|
|
|
jstring Java_org_suyu_suyu_1emu_features_input_NativeInput_getNextInput(JNIEnv* env,
|
|
jobject j_obj) {
|
|
return Common::Android::ToJString(
|
|
env, EmulationSession::GetInstance().GetInputSubsystem().GetNextInput().Serialize());
|
|
}
|
|
|
|
void Java_org_suyu_suyu_1emu_features_input_NativeInput_stopMapping(JNIEnv* env, jobject j_obj) {
|
|
EmulationSession::GetInstance().GetInputSubsystem().StopMapping();
|
|
}
|
|
|
|
void Java_org_suyu_suyu_1emu_features_input_NativeInput_updateMappingsWithDefaultImpl(
|
|
JNIEnv* env, jobject j_obj, jint j_player_index, jstring j_device_params,
|
|
jstring j_display_name) {
|
|
auto& input_subsystem = EmulationSession::GetInstance().GetInputSubsystem();
|
|
|
|
// Clear all previous mappings
|
|
for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
|
|
ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
|
|
controller->SetButtonParam(button_id, {});
|
|
});
|
|
}
|
|
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
|
|
ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
|
|
controller->SetStickParam(analog_id, {});
|
|
});
|
|
}
|
|
|
|
// Apply new mappings
|
|
auto device = Common::ParamPackage(Common::Android::GetJString(env, j_device_params));
|
|
auto button_mappings = input_subsystem.GetButtonMappingForDevice(device);
|
|
auto analog_mappings = input_subsystem.GetAnalogMappingForDevice(device);
|
|
auto display_name = Common::Android::GetJString(env, j_display_name);
|
|
for (const auto& button_mapping : button_mappings) {
|
|
const std::size_t index = button_mapping.first;
|
|
auto named_mapping = button_mapping.second;
|
|
named_mapping.Set("display", display_name);
|
|
ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
|
|
controller->SetButtonParam(index, named_mapping);
|
|
});
|
|
}
|
|
for (const auto& analog_mapping : analog_mappings) {
|
|
const std::size_t index = analog_mapping.first;
|
|
auto named_mapping = analog_mapping.second;
|
|
named_mapping.Set("display", display_name);
|
|
ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
|
|
controller->SetStickParam(index, named_mapping);
|
|
});
|
|
}
|
|
}
|
|
|
|
jstring Java_org_suyu_suyu_1emu_features_input_NativeInput_getButtonParamImpl(JNIEnv* env,
|
|
jobject j_obj,
|
|
jint j_player_index,
|
|
jint j_button) {
|
|
return Common::Android::ToJString(env, EmulationSession::GetInstance()
|
|
.System()
|
|
.HIDCore()
|
|
.GetEmulatedControllerByIndex(j_player_index)
|
|
->GetButtonParam(j_button)
|
|
.Serialize());
|
|
}
|
|
|
|
void Java_org_suyu_suyu_1emu_features_input_NativeInput_setButtonParamImpl(
|
|
JNIEnv* env, jobject j_obj, jint j_player_index, jint j_button_id, jstring j_param) {
|
|
ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
|
|
controller->SetButtonParam(j_button_id,
|
|
Common::ParamPackage(Common::Android::GetJString(env, j_param)));
|
|
});
|
|
}
|
|
|
|
jstring Java_org_suyu_suyu_1emu_features_input_NativeInput_getStickParamImpl(JNIEnv* env,
|
|
jobject j_obj,
|
|
jint j_player_index,
|
|
jint j_stick) {
|
|
return Common::Android::ToJString(env, EmulationSession::GetInstance()
|
|
.System()
|
|
.HIDCore()
|
|
.GetEmulatedControllerByIndex(j_player_index)
|
|
->GetStickParam(j_stick)
|
|
.Serialize());
|
|
}
|
|
|
|
void Java_org_suyu_suyu_1emu_features_input_NativeInput_setStickParamImpl(
|
|
JNIEnv* env, jobject j_obj, jint j_player_index, jint j_stick_id, jstring j_param) {
|
|
ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
|
|
controller->SetStickParam(j_stick_id,
|
|
Common::ParamPackage(Common::Android::GetJString(env, j_param)));
|
|
});
|
|
}
|
|
|
|
jint Java_org_suyu_suyu_1emu_features_input_NativeInput_getButtonNameImpl(JNIEnv* env,
|
|
jobject j_obj,
|
|
jstring j_param) {
|
|
return static_cast<jint>(EmulationSession::GetInstance().GetInputSubsystem().GetButtonName(
|
|
Common::ParamPackage(Common::Android::GetJString(env, j_param))));
|
|
}
|
|
|
|
jintArray Java_org_suyu_suyu_1emu_features_input_NativeInput_getSupportedStyleTagsImpl(
|
|
JNIEnv* env, jobject j_obj, jint j_player_index) {
|
|
auto supported_styles = GetSupportedStyles(j_player_index);
|
|
jintArray j_supported_indexes = env->NewIntArray(supported_styles.size());
|
|
env->SetIntArrayRegion(j_supported_indexes, 0, supported_styles.size(),
|
|
supported_styles.data());
|
|
return j_supported_indexes;
|
|
}
|
|
|
|
jint Java_org_suyu_suyu_1emu_features_input_NativeInput_getStyleIndexImpl(JNIEnv* env,
|
|
jobject j_obj,
|
|
jint j_player_index) {
|
|
return static_cast<s32>(EmulationSession::GetInstance()
|
|
.System()
|
|
.HIDCore()
|
|
.GetEmulatedControllerByIndex(j_player_index)
|
|
->GetNpadStyleIndex(true));
|
|
}
|
|
|
|
void Java_org_suyu_suyu_1emu_features_input_NativeInput_setStyleIndexImpl(JNIEnv* env,
|
|
jobject j_obj,
|
|
jint j_player_index,
|
|
jint j_style_index) {
|
|
auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
|
|
auto type = static_cast<Core::HID::NpadStyleIndex>(j_style_index);
|
|
ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
|
|
controller->SetNpadStyleIndex(type);
|
|
});
|
|
if (j_player_index == 0) {
|
|
auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
|
|
auto* player_one = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
|
|
ConnectController(j_player_index,
|
|
player_one->IsConnected(true) || handheld->IsConnected(true));
|
|
}
|
|
}
|
|
|
|
jboolean Java_org_suyu_suyu_1emu_features_input_NativeInput_isControllerImpl(JNIEnv* env,
|
|
jobject j_obj,
|
|
jstring jparams) {
|
|
return static_cast<jint>(EmulationSession::GetInstance().GetInputSubsystem().IsController(
|
|
Common::ParamPackage(Common::Android::GetJString(env, jparams))));
|
|
}
|
|
|
|
jboolean Java_org_suyu_suyu_1emu_features_input_NativeInput_getIsConnected(JNIEnv* env,
|
|
jobject j_obj,
|
|
jint j_player_index) {
|
|
auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
|
|
auto* controller = hid_core.GetEmulatedControllerByIndex(static_cast<size_t>(j_player_index));
|
|
if (j_player_index == 0 &&
|
|
controller->GetNpadStyleIndex(true) == Core::HID::NpadStyleIndex::Handheld) {
|
|
return hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld)->IsConnected(true);
|
|
}
|
|
return controller->IsConnected(true);
|
|
}
|
|
|
|
void Java_org_suyu_suyu_1emu_features_input_NativeInput_connectControllersImpl(
|
|
JNIEnv* env, jobject j_obj, jbooleanArray j_connected) {
|
|
jboolean isCopy = false;
|
|
auto j_connected_array_size = env->GetArrayLength(j_connected);
|
|
jboolean* j_connected_array = env->GetBooleanArrayElements(j_connected, &isCopy);
|
|
for (int i = 0; i < j_connected_array_size; ++i) {
|
|
ConnectController(i, j_connected_array[i]);
|
|
}
|
|
}
|
|
|
|
void Java_org_suyu_suyu_1emu_features_input_NativeInput_resetControllerMappings(
|
|
JNIEnv* env, jobject j_obj, jint j_player_index) {
|
|
// Clear all previous mappings
|
|
for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
|
|
ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
|
|
controller->SetButtonParam(button_id, {});
|
|
});
|
|
}
|
|
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
|
|
ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
|
|
controller->SetStickParam(analog_id, {});
|
|
});
|
|
}
|
|
}
|
|
|
|
} // extern "C"
|