From 1c26a85e35f94169ddc83e8174d6824aec82d322 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Mon, 18 Apr 2022 16:11:14 +0200 Subject: [PATCH] Android: Add NumericSetting support --- .../model/InputMappingBooleanSetting.java | 48 ++++++ .../model/InputMappingDoubleSetting.java | 49 ++++++ .../model/controlleremu/ControlGroup.java | 4 + .../model/controlleremu/NumericSetting.java | 89 +++++++++++ .../model/view/FloatSliderSetting.java | 7 + .../settings/model/view/SliderSetting.java | 8 + .../ui/SettingsFragmentPresenter.java | 26 ++++ Source/Android/jni/AndroidCommon/IDCache.cpp | 27 ++++ Source/Android/jni/AndroidCommon/IDCache.h | 4 + Source/Android/jni/CMakeLists.txt | 2 + Source/Android/jni/Input/ControlGroup.cpp | 15 ++ Source/Android/jni/Input/NumericSetting.cpp | 139 ++++++++++++++++++ Source/Android/jni/Input/NumericSetting.h | 15 ++ 13 files changed, 433 insertions(+) create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingBooleanSetting.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingDoubleSetting.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/NumericSetting.java create mode 100644 Source/Android/jni/Input/NumericSetting.cpp create mode 100644 Source/Android/jni/Input/NumericSetting.h diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingBooleanSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingBooleanSetting.java new file mode 100644 index 0000000000..7241d6e125 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingBooleanSetting.java @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.model; + +import org.dolphinemu.dolphinemu.features.input.model.controlleremu.NumericSetting; +import org.dolphinemu.dolphinemu.features.settings.model.AbstractBooleanSetting; +import org.dolphinemu.dolphinemu.features.settings.model.Settings; + +public class InputMappingBooleanSetting implements AbstractBooleanSetting +{ + private final NumericSetting mNumericSetting; + + public InputMappingBooleanSetting(NumericSetting numericSetting) + { + mNumericSetting = numericSetting; + } + + @Override + public boolean getBoolean(Settings settings) + { + return mNumericSetting.getBooleanValue(); + } + + @Override + public void setBoolean(Settings settings, boolean newValue) + { + mNumericSetting.setBooleanValue(newValue); + } + + @Override + public boolean isOverridden(Settings settings) + { + return false; + } + + @Override + public boolean isRuntimeEditable() + { + return true; + } + + @Override + public boolean delete(Settings settings) + { + mNumericSetting.setBooleanValue(mNumericSetting.getBooleanDefaultValue()); + return true; + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingDoubleSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingDoubleSetting.java new file mode 100644 index 0000000000..e84b61a045 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingDoubleSetting.java @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.model; + +import org.dolphinemu.dolphinemu.features.input.model.controlleremu.NumericSetting; +import org.dolphinemu.dolphinemu.features.settings.model.AbstractFloatSetting; +import org.dolphinemu.dolphinemu.features.settings.model.Settings; + +// Yes, floats are not the same thing as doubles... They're close enough, though +public class InputMappingDoubleSetting implements AbstractFloatSetting +{ + private final NumericSetting mNumericSetting; + + public InputMappingDoubleSetting(NumericSetting numericSetting) + { + mNumericSetting = numericSetting; + } + + @Override + public float getFloat(Settings settings) + { + return (float) mNumericSetting.getDoubleValue(); + } + + @Override + public void setFloat(Settings settings, float newValue) + { + mNumericSetting.setDoubleValue(newValue); + } + + @Override + public boolean isOverridden(Settings settings) + { + return false; + } + + @Override + public boolean isRuntimeEditable() + { + return true; + } + + @Override + public boolean delete(Settings settings) + { + mNumericSetting.setDoubleValue(mNumericSetting.getDoubleDefaultValue()); + return true; + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup.java index ddfce9a737..19423d5f14 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup.java @@ -26,4 +26,8 @@ public class ControlGroup public native int getControlCount(); public native Control getControl(int i); + + public native int getNumericSettingCount(); + + public native NumericSetting getNumericSetting(int i); } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/NumericSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/NumericSetting.java new file mode 100644 index 0000000000..9baa2c41d7 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/NumericSetting.java @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.model.controlleremu; + +import androidx.annotation.Keep; + +/** + * Represents a C++ ControllerEmu::NumericSetting. + * + * The lifetime of this class is managed by C++ code. Calling methods on it after it's destroyed + * in C++ is undefined behavior! + */ +public class NumericSetting +{ + public static final int TYPE_INT = 0; + public static final int TYPE_DOUBLE = 1; + public static final int TYPE_BOOLEAN = 2; + + @Keep + private final long mPointer; + + @Keep + private NumericSetting(long pointer) + { + mPointer = pointer; + } + + /** + * @return The name used in the UI. + */ + public native String getUiName(); + + /** + * @return A string applied to the number in the UI (unit of measure). + */ + public native String getUiSuffix(); + + /** + * @return Detailed description of the setting. + */ + public native String getUiDescription(); + + /** + * @return TYPE_INT, TYPE_DOUBLE or TYPE_BOOLEAN + */ + public native int getType(); + + public native ControlReference getControlReference(); + + /** + * If the type is TYPE_DOUBLE, gets the current value. Otherwise, undefined behavior! + */ + public native double getDoubleValue(); + + /** + * If the type is TYPE_DOUBLE, sets the current value. Otherwise, undefined behavior! + */ + public native void setDoubleValue(double value); + + /** + * If the type is TYPE_DOUBLE, gets the default value. Otherwise, undefined behavior! + */ + public native double getDoubleDefaultValue(); + + /** + * If the type is TYPE_DOUBLE, returns the minimum valid value. Otherwise, undefined behavior! + */ + public native double getDoubleMin(); + + /** + * If the type is TYPE_DOUBLE, returns the maximum valid value. Otherwise, undefined behavior! + */ + public native double getDoubleMax(); + + /** + * If the type is TYPE_BOOLEAN, gets the current value. Otherwise, undefined behavior! + */ + public native boolean getBooleanValue(); + + /** + * If the type is TYPE_BOOLEAN, sets the current value. Otherwise, undefined behavior! + */ + public native void setBooleanValue(boolean value); + + /** + * If the type is TYPE_BOOLEAN, gets the default value. Otherwise, undefined behavior! + */ + public native boolean getBooleanDefaultValue(); +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/FloatSliderSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/FloatSliderSetting.java index 167c5a471b..5b91517f75 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/FloatSliderSetting.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/FloatSliderSetting.java @@ -19,6 +19,13 @@ public class FloatSliderSetting extends SliderSetting mSetting = setting; } + public FloatSliderSetting(AbstractFloatSetting setting, CharSequence name, + CharSequence description, int min, int max, String units) + { + super(name, description, min, max, units); + mSetting = setting; + } + public int getSelectedValue(Settings settings) { return Math.round(mSetting.getFloat(settings)); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SliderSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SliderSetting.java index 48961839c0..e63fde3bad 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SliderSetting.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SliderSetting.java @@ -23,6 +23,14 @@ public abstract class SliderSetting extends SettingsItem mStepSize = stepSize; } + public SliderSetting(CharSequence name, CharSequence description, int min, int max, String units) + { + super(name, description); + mMin = min; + mMax = max; + mUnits = units; + } + public abstract int getSelectedValue(Settings settings); public int getMin() diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java index 01f4b52b71..775c9b3f8a 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java @@ -14,8 +14,11 @@ import androidx.appcompat.app.AppCompatActivity; import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.activities.UserDataActivity; +import org.dolphinemu.dolphinemu.features.input.model.InputMappingBooleanSetting; +import org.dolphinemu.dolphinemu.features.input.model.InputMappingDoubleSetting; import org.dolphinemu.dolphinemu.features.input.model.controlleremu.ControlGroup; import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController; +import org.dolphinemu.dolphinemu.features.input.model.controlleremu.NumericSetting; import org.dolphinemu.dolphinemu.features.input.model.view.InputMappingControlSetting; import org.dolphinemu.dolphinemu.features.settings.model.AbstractBooleanSetting; import org.dolphinemu.dolphinemu.features.settings.model.AbstractIntSetting; @@ -30,6 +33,7 @@ import org.dolphinemu.dolphinemu.features.settings.model.StringSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.DateTimeChoiceSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.SwitchSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.FilePicker; +import org.dolphinemu.dolphinemu.features.settings.model.view.FloatSliderSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.HeaderSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.HyperLinkHeaderSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.InputStringSetting; @@ -1112,6 +1116,28 @@ public final class SettingsFragmentPresenter { sl.add(new InputMappingControlSetting(group.getControl(j), controller)); } + + int numericSettingCount = group.getNumericSettingCount(); + for (int j = 0; j < numericSettingCount; j++) + { + addNumericSetting(sl, group.getNumericSetting(j)); + } + } + } + + private void addNumericSetting(ArrayList sl, NumericSetting setting) + { + switch (setting.getType()) + { + case NumericSetting.TYPE_DOUBLE: + sl.add(new FloatSliderSetting(new InputMappingDoubleSetting(setting), setting.getUiName(), + "", (int) Math.ceil(setting.getDoubleMin()), + (int) Math.floor(setting.getDoubleMax()), setting.getUiSuffix())); + break; + case NumericSetting.TYPE_BOOLEAN: + sl.add(new SwitchSetting(new InputMappingBooleanSetting(setting), setting.getUiName(), + setting.getUiDescription())); + break; } } diff --git a/Source/Android/jni/AndroidCommon/IDCache.cpp b/Source/Android/jni/AndroidCommon/IDCache.cpp index 54d3f765c9..6e547beaed 100644 --- a/Source/Android/jni/AndroidCommon/IDCache.cpp +++ b/Source/Android/jni/AndroidCommon/IDCache.cpp @@ -87,6 +87,10 @@ static jclass s_control_class; static jfieldID s_control_pointer; static jmethodID s_control_constructor; +static jclass s_numeric_setting_class; +static jfieldID s_numeric_setting_pointer; +static jmethodID s_numeric_setting_constructor; + static jclass s_control_group_class; static jfieldID s_control_group_pointer; static jmethodID s_control_group_constructor; @@ -459,6 +463,21 @@ jmethodID GetEmulatedControllerConstructor() return s_emulated_controller_constructor; } +jclass GetNumericSettingClass() +{ + return s_numeric_setting_class; +} + +jfieldID GetNumericSettingPointer() +{ + return s_numeric_setting_pointer; +} + +jmethodID GetNumericSettingConstructor() +{ + return s_numeric_setting_constructor; +} + } // namespace IDCache extern "C" { @@ -646,6 +665,13 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) s_emulated_controller_constructor = env->GetMethodID(emulated_controller_class, "", "(J)V"); env->DeleteLocalRef(emulated_controller_class); + const jclass numeric_setting_class = + env->FindClass("org/dolphinemu/dolphinemu/features/input/model/controlleremu/NumericSetting"); + s_numeric_setting_class = reinterpret_cast(env->NewGlobalRef(numeric_setting_class)); + s_numeric_setting_pointer = env->GetFieldID(numeric_setting_class, "mPointer", "J"); + s_numeric_setting_constructor = env->GetMethodID(numeric_setting_class, "", "(J)V"); + env->DeleteLocalRef(numeric_setting_class); + return JNI_VERSION; } @@ -677,5 +703,6 @@ JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved) env->DeleteGlobalRef(s_control_group_class); env->DeleteGlobalRef(s_control_reference_class); env->DeleteGlobalRef(s_emulated_controller_class); + env->DeleteGlobalRef(s_numeric_setting_class); } } diff --git a/Source/Android/jni/AndroidCommon/IDCache.h b/Source/Android/jni/AndroidCommon/IDCache.h index bd727c12a9..2a6462c380 100644 --- a/Source/Android/jni/AndroidCommon/IDCache.h +++ b/Source/Android/jni/AndroidCommon/IDCache.h @@ -98,4 +98,8 @@ jclass GetEmulatedControllerClass(); jfieldID GetEmulatedControllerPointer(); jmethodID GetEmulatedControllerConstructor(); +jclass GetNumericSettingClass(); +jfieldID GetNumericSettingPointer(); +jmethodID GetNumericSettingConstructor(); + } // namespace IDCache diff --git a/Source/Android/jni/CMakeLists.txt b/Source/Android/jni/CMakeLists.txt index 575847290e..96dbb9d014 100644 --- a/Source/Android/jni/CMakeLists.txt +++ b/Source/Android/jni/CMakeLists.txt @@ -19,6 +19,8 @@ add_library(main SHARED Input/EmulatedController.cpp Input/InputOverrider.cpp Input/MappingCommon.cpp + Input/NumericSetting.cpp + Input/NumericSetting.h IniFile.cpp MainAndroid.cpp RiivolutionPatches.cpp diff --git a/Source/Android/jni/Input/ControlGroup.cpp b/Source/Android/jni/Input/ControlGroup.cpp index 0e56774e84..01405a0e8b 100644 --- a/Source/Android/jni/Input/ControlGroup.cpp +++ b/Source/Android/jni/Input/ControlGroup.cpp @@ -10,6 +10,7 @@ #include "jni/AndroidCommon/AndroidCommon.h" #include "jni/AndroidCommon/IDCache.h" #include "jni/Input/Control.h" +#include "jni/Input/NumericSetting.h" static ControllerEmu::ControlGroup* GetPointer(JNIEnv* env, jobject obj) { @@ -48,4 +49,18 @@ Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlGroup_g { return ControlToJava(env, GetPointer(env, obj)->controls[i].get()); } + +JNIEXPORT jint JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlGroup_getNumericSettingCount( + JNIEnv* env, jobject obj) +{ + return static_cast(GetPointer(env, obj)->numeric_settings.size()); +} + +JNIEXPORT jobject JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlGroup_getNumericSetting( + JNIEnv* env, jobject obj, jint i) +{ + return NumericSettingToJava(env, GetPointer(env, obj)->numeric_settings[i].get()); +} } diff --git a/Source/Android/jni/Input/NumericSetting.cpp b/Source/Android/jni/Input/NumericSetting.cpp new file mode 100644 index 0000000000..32805d15f8 --- /dev/null +++ b/Source/Android/jni/Input/NumericSetting.cpp @@ -0,0 +1,139 @@ +// Copyright 2022 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "jni/Input/NumericSetting.h" + +#include + +#include + +#include "Common/Assert.h" +#include "Common/MsgHandler.h" +#include "InputCommon/ControlReference/ControlReference.h" +#include "InputCommon/ControllerEmu/Setting/NumericSetting.h" +#include "jni/AndroidCommon/AndroidCommon.h" +#include "jni/AndroidCommon/IDCache.h" +#include "jni/Input/ControlReference.h" + +static const char* NullStringToEmptyString(const char* str) +{ + return str ? str : ""; +} + +static ControllerEmu::NumericSettingBase* GetPointer(JNIEnv* env, jobject obj) +{ + return reinterpret_cast( + env->GetLongField(obj, IDCache::GetNumericSettingPointer())); +} + +template +static ControllerEmu::NumericSetting* GetPointer(JNIEnv* env, jobject obj) +{ + return reinterpret_cast*>( + env->GetLongField(obj, IDCache::GetNumericSettingPointer())); +} + +jobject NumericSettingToJava(JNIEnv* env, ControllerEmu::NumericSettingBase* numeric_setting) +{ + if (!numeric_setting) + return nullptr; + + return env->NewObject(IDCache::GetNumericSettingClass(), IDCache::GetNumericSettingConstructor(), + reinterpret_cast(numeric_setting)); +} + +extern "C" { + +JNIEXPORT jstring JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getUiName( + JNIEnv* env, jobject obj) +{ + return ToJString(env, Common::GetStringT(GetPointer(env, obj)->GetUIName())); +} + +JNIEXPORT jstring JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getUiSuffix( + JNIEnv* env, jobject obj) +{ + const char* str = NullStringToEmptyString(GetPointer(env, obj)->GetUISuffix()); + return ToJString(env, Common::GetStringT(str)); +} + +JNIEXPORT jstring JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getUiDescription( + JNIEnv* env, jobject obj) +{ + const char* str = NullStringToEmptyString(GetPointer(env, obj)->GetUIDescription()); + return ToJString(env, Common::GetStringT(str)); +} + +JNIEXPORT jint JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getType( + JNIEnv* env, jobject obj) +{ + return static_cast(GetPointer(env, obj)->GetType()); +} + +JNIEXPORT jobject JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getControlReference( + JNIEnv* env, jobject obj) +{ + return ControlReferenceToJava(env, &GetPointer(env, obj)->GetInputReference()); +} + +JNIEXPORT jdouble JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getDoubleValue( + JNIEnv* env, jobject obj) +{ + return GetPointer(env, obj)->GetValue(); +} + +JNIEXPORT void JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_setDoubleValue( + JNIEnv* env, jobject obj, jdouble value) +{ + GetPointer(env, obj)->SetValue(value); +} + +JNIEXPORT jdouble JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getDoubleDefaultValue( + JNIEnv* env, jobject obj) +{ + return GetPointer(env, obj)->GetDefaultValue(); +} + +JNIEXPORT jdouble JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getDoubleMin( + JNIEnv* env, jobject obj) +{ + return GetPointer(env, obj)->GetMinValue(); +} + +JNIEXPORT jdouble JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getDoubleMax( + JNIEnv* env, jobject obj) +{ + return GetPointer(env, obj)->GetMaxValue(); +} + +JNIEXPORT jboolean JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getBooleanValue( + JNIEnv* env, jobject obj) +{ + return static_cast(GetPointer(env, obj)->GetValue()); +} + +JNIEXPORT void JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_setBooleanValue( + JNIEnv* env, jobject obj, jboolean value) +{ + GetPointer(env, obj)->SetValue(static_cast(value)); +} + +JNIEXPORT jboolean JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getBooleanDefaultValue( + JNIEnv* env, jobject obj) +{ + return static_cast(GetPointer(env, obj)->GetDefaultValue()); +} +} diff --git a/Source/Android/jni/Input/NumericSetting.h b/Source/Android/jni/Input/NumericSetting.h new file mode 100644 index 0000000000..a7f3a2124a --- /dev/null +++ b/Source/Android/jni/Input/NumericSetting.h @@ -0,0 +1,15 @@ +// Copyright 2022 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +namespace ControllerEmu +{ +class NumericSettingBase; +} + +class ControlReference; + +jobject NumericSettingToJava(JNIEnv* env, ControllerEmu::NumericSettingBase* control);