diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/ConfigChangedCallback.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/ConfigChangedCallback.kt new file mode 100644 index 0000000000..4415efa9e8 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/ConfigChangedCallback.kt @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.model + +import androidx.annotation.Keep + +/** + * Calls the passed-in Runnable when Dolphin's config changes. + * + * Please note: The Runnable may be called from any thread. + */ +class ConfigChangedCallback(runnable: Runnable) { + @Keep + private var pointer: Long = initialize(runnable) + + /** + * Stops the callback from being called in the future. + */ + fun unregister() { + if (pointer != 0L) { + deinitialize(pointer) + pointer = 0L + } + } + + companion object { + @JvmStatic + private external fun initialize(runnable: Runnable): Long + + @JvmStatic + private external fun deinitialize(pointer: Long) + } +} diff --git a/Source/Android/jni/AndroidCommon/IDCache.cpp b/Source/Android/jni/AndroidCommon/IDCache.cpp index c3bd435af6..a19423d67c 100644 --- a/Source/Android/jni/AndroidCommon/IDCache.cpp +++ b/Source/Android/jni/AndroidCommon/IDCache.cpp @@ -109,6 +109,8 @@ static jclass s_core_device_control_class; static jfieldID s_core_device_control_pointer; static jmethodID s_core_device_control_constructor; +static jmethodID s_runnable_run; + namespace IDCache { JNIEnv* GetEnvForThread() @@ -504,6 +506,11 @@ jmethodID GetCoreDeviceControlConstructor() return s_core_device_control_constructor; } +jmethodID GetRunnableRun() +{ + return s_runnable_run; +} + } // namespace IDCache extern "C" { @@ -709,6 +716,10 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) "(Lorg/dolphinemu/dolphinemu/features/input/model/CoreDevice;J)V"); env->DeleteLocalRef(core_device_control_class); + const jclass runnable_class = env->FindClass("java/lang/Runnable"); + s_runnable_run = env->GetMethodID(runnable_class, "run", "()V"); + env->DeleteLocalRef(runnable_class); + return JNI_VERSION; } diff --git a/Source/Android/jni/AndroidCommon/IDCache.h b/Source/Android/jni/AndroidCommon/IDCache.h index eef5a8a085..604b68e38e 100644 --- a/Source/Android/jni/AndroidCommon/IDCache.h +++ b/Source/Android/jni/AndroidCommon/IDCache.h @@ -108,4 +108,6 @@ jclass GetCoreDeviceControlClass(); jfieldID GetCoreDeviceControlPointer(); jmethodID GetCoreDeviceControlConstructor(); +jmethodID GetRunnableRun(); + } // namespace IDCache diff --git a/Source/Android/jni/CMakeLists.txt b/Source/Android/jni/CMakeLists.txt index bb18440b02..ab2acc7063 100644 --- a/Source/Android/jni/CMakeLists.txt +++ b/Source/Android/jni/CMakeLists.txt @@ -5,6 +5,7 @@ add_library(main SHARED Cheats/GraphicsMod.cpp Cheats/GraphicsModGroup.cpp Cheats/PatchCheat.cpp + Config/ConfigChangedCallback.cpp Config/NativeConfig.cpp Config/PostProcessing.cpp GameList/GameFile.cpp diff --git a/Source/Android/jni/Config/ConfigChangedCallback.cpp b/Source/Android/jni/Config/ConfigChangedCallback.cpp new file mode 100644 index 0000000000..2ec8b06541 --- /dev/null +++ b/Source/Android/jni/Config/ConfigChangedCallback.cpp @@ -0,0 +1,44 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include + +#include "Common/Config/Config.h" +#include "jni/AndroidCommon/AndroidCommon.h" +#include "jni/AndroidCommon/IDCache.h" + +struct ConfigChangedCallbackContext +{ + jobject runnable; + Config::ConfigChangedCallbackID callback_id; +}; + +extern "C" { + +JNIEXPORT jlong JNICALL +Java_org_dolphinemu_dolphinemu_features_settings_model_ConfigChangedCallback_initialize( + JNIEnv* env, jclass, jobject runnable) +{ + auto* context = new ConfigChangedCallbackContext; + + jobject runnable_global = env->NewGlobalRef(runnable); + context->runnable = runnable_global; + context->callback_id = Config::AddConfigChangedCallback([runnable_global] { + IDCache::GetEnvForThread()->CallVoidMethod(runnable_global, IDCache::GetRunnableRun()); + }); + + return reinterpret_cast(context); +} + +JNIEXPORT void JNICALL +Java_org_dolphinemu_dolphinemu_features_settings_model_ConfigChangedCallback_deinitialize( + JNIEnv* env, jclass, jlong pointer) +{ + auto* context = reinterpret_cast(pointer); + + Config::RemoveConfigChangedCallback(context->callback_id); + env->DeleteGlobalRef(context->runnable); + + delete context; +} +}