summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosJuice <josjuice@gmail.com>2022-03-06 20:27:25 +0100
committerJosJuice <josjuice@gmail.com>2023-03-03 22:28:23 +0100
commit104ea098921846e01eb58ebfa00822b14da46cda (patch)
tree11f82b21f94f17731fefcfce6ac27ac4bfbf0d7f
parentd6af294a2370b1827508cdf1819ad7cedbb39f7b (diff)
ControllerInterface/Android: Implement hotplug
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java72
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/LooperThread.java58
-rw-r--r--Source/Core/InputCommon/ControllerInterface/Android/Android.cpp27
3 files changed, 157 insertions, 0 deletions
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java
index 80d21eeb57..8a7bc8177f 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java
@@ -2,15 +2,50 @@
package org.dolphinemu.dolphinemu.features.input.model;
+import android.content.Context;
+import android.hardware.input.InputManager;
+import android.os.Handler;
import android.view.KeyEvent;
import android.view.MotionEvent;
+import androidx.annotation.Keep;
+
+import org.dolphinemu.dolphinemu.DolphinApplication;
+import org.dolphinemu.dolphinemu.utils.LooperThread;
+
/**
* This class interfaces with the native ControllerInterface,
* which is where the emulator core gets inputs from.
*/
public final class ControllerInterface
{
+ private static final class InputDeviceListener implements InputManager.InputDeviceListener
+ {
+ @Override
+ public void onInputDeviceAdded(int deviceId)
+ {
+ // Simple implementation for now. We could do something fancier if we wanted to.
+ refreshDevices();
+ }
+
+ @Override
+ public void onInputDeviceRemoved(int deviceId)
+ {
+ // Simple implementation for now. We could do something fancier if we wanted to.
+ refreshDevices();
+ }
+
+ @Override
+ public void onInputDeviceChanged(int deviceId)
+ {
+ // Simple implementation for now. We could do something fancier if we wanted to.
+ refreshDevices();
+ }
+ }
+
+ private static InputDeviceListener mInputDeviceListener;
+ private static LooperThread mLooperThread;
+
/**
* Activities which want to pass on inputs to native code
* should call this in their own dispatchKeyEvent method.
@@ -28,4 +63,41 @@ public final class ControllerInterface
* false if the event should be passed on to the default dispatchGenericMotionEvent.
*/
public static native boolean dispatchGenericMotionEvent(MotionEvent event);
+
+ /**
+ * Rescans for input devices.
+ */
+ public static native void refreshDevices();
+
+ @Keep
+ private static void registerInputDeviceListener()
+ {
+ if (mLooperThread == null)
+ {
+ mLooperThread = new LooperThread("Hotplug thread");
+ mLooperThread.start();
+ }
+
+ if (mInputDeviceListener == null)
+ {
+ InputManager im = (InputManager)
+ DolphinApplication.getAppContext().getSystemService(Context.INPUT_SERVICE);
+
+ mInputDeviceListener = new InputDeviceListener();
+ im.registerInputDeviceListener(mInputDeviceListener, new Handler(mLooperThread.getLooper()));
+ }
+ }
+
+ @Keep
+ private static void unregisterInputDeviceListener()
+ {
+ if (mInputDeviceListener != null)
+ {
+ InputManager im = (InputManager)
+ DolphinApplication.getAppContext().getSystemService(Context.INPUT_SERVICE);
+
+ im.unregisterInputDeviceListener(mInputDeviceListener);
+ mInputDeviceListener = null;
+ }
+ }
}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/LooperThread.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/LooperThread.java
new file mode 100644
index 0000000000..8a67ec7e89
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/LooperThread.java
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.utils;
+
+import android.os.Looper;
+
+public class LooperThread extends Thread
+{
+ private Looper mLooper;
+
+ public LooperThread()
+ {
+ super();
+ }
+
+ public LooperThread(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void run()
+ {
+ Looper.prepare();
+
+ synchronized (this)
+ {
+ mLooper = Looper.myLooper();
+ notifyAll();
+ }
+
+ Looper.loop();
+ }
+
+ public Looper getLooper()
+ {
+ if (!isAlive())
+ {
+ throw new IllegalStateException();
+ }
+
+ synchronized (this)
+ {
+ while (mLooper == null)
+ {
+ try
+ {
+ wait();
+ }
+ catch (InterruptedException ignored)
+ {
+ }
+ }
+ }
+
+ return mLooper;
+ }
+}
diff --git a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp
index 5a16ef56d0..e5baa4e43e 100644
--- a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp
+++ b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp
@@ -58,6 +58,10 @@ jclass s_motion_event_class;
jmethodID s_motion_event_get_axis_value;
jmethodID s_motion_event_get_source;
+jclass s_controller_interface_class;
+jmethodID s_controller_interface_register_input_device_listener;
+jmethodID s_controller_interface_unregister_input_device_listener;
+
jintArray s_keycodes_array;
using Clock = std::chrono::steady_clock;
@@ -635,20 +639,36 @@ void Init()
s_motion_event_get_axis_value = env->GetMethodID(s_motion_event_class, "getAxisValue", "(I)F");
s_motion_event_get_source = env->GetMethodID(s_motion_event_class, "getSource", "()I");
+ const jclass controller_interface_class =
+ env->FindClass("org/dolphinemu/dolphinemu/features/input/model/ControllerInterface");
+ s_controller_interface_class =
+ reinterpret_cast<jclass>(env->NewGlobalRef(controller_interface_class));
+ s_controller_interface_register_input_device_listener =
+ env->GetStaticMethodID(s_controller_interface_class, "registerInputDeviceListener", "()V");
+ s_controller_interface_unregister_input_device_listener =
+ env->GetStaticMethodID(s_controller_interface_class, "unregisterInputDeviceListener", "()V");
+
jintArray keycodes_array = CreateKeyCodesArray(env);
s_keycodes_array = reinterpret_cast<jintArray>(env->NewGlobalRef(keycodes_array));
env->DeleteLocalRef(keycodes_array);
+
+ env->CallStaticVoidMethod(s_controller_interface_class,
+ s_controller_interface_register_input_device_listener);
}
void Shutdown()
{
JNIEnv* env = IDCache::GetEnvForThread();
+ env->CallStaticVoidMethod(s_controller_interface_class,
+ s_controller_interface_unregister_input_device_listener);
+
env->DeleteGlobalRef(s_input_device_class);
env->DeleteGlobalRef(s_motion_range_class);
env->DeleteGlobalRef(s_input_event_class);
env->DeleteGlobalRef(s_key_event_class);
env->DeleteGlobalRef(s_motion_event_class);
+ env->DeleteGlobalRef(s_controller_interface_class);
env->DeleteGlobalRef(s_keycodes_array);
}
@@ -785,4 +805,11 @@ Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_dispatch
return last_polled >= Clock::now() - ACTIVE_INPUT_TIMEOUT;
}
+
+JNIEXPORT void JNICALL
+Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_refreshDevices(JNIEnv* env,
+ jclass)
+{
+ g_controller_interface.RefreshDevices();
+}
}