ControllerInterface/Android: Implement hotplug
This commit is contained in:
parent
d6af294a23
commit
104ea09892
|
@ -2,15 +2,50 @@
|
||||||
|
|
||||||
package org.dolphinemu.dolphinemu.features.input.model;
|
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.KeyEvent;
|
||||||
import android.view.MotionEvent;
|
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,
|
* This class interfaces with the native ControllerInterface,
|
||||||
* which is where the emulator core gets inputs from.
|
* which is where the emulator core gets inputs from.
|
||||||
*/
|
*/
|
||||||
public final class ControllerInterface
|
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
|
* Activities which want to pass on inputs to native code
|
||||||
* should call this in their own dispatchKeyEvent method.
|
* 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.
|
* false if the event should be passed on to the default dispatchGenericMotionEvent.
|
||||||
*/
|
*/
|
||||||
public static native boolean dispatchGenericMotionEvent(MotionEvent event);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -58,6 +58,10 @@ jclass s_motion_event_class;
|
||||||
jmethodID s_motion_event_get_axis_value;
|
jmethodID s_motion_event_get_axis_value;
|
||||||
jmethodID s_motion_event_get_source;
|
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;
|
jintArray s_keycodes_array;
|
||||||
|
|
||||||
using Clock = std::chrono::steady_clock;
|
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_axis_value = env->GetMethodID(s_motion_event_class, "getAxisValue", "(I)F");
|
||||||
s_motion_event_get_source = env->GetMethodID(s_motion_event_class, "getSource", "()I");
|
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);
|
jintArray keycodes_array = CreateKeyCodesArray(env);
|
||||||
s_keycodes_array = reinterpret_cast<jintArray>(env->NewGlobalRef(keycodes_array));
|
s_keycodes_array = reinterpret_cast<jintArray>(env->NewGlobalRef(keycodes_array));
|
||||||
env->DeleteLocalRef(keycodes_array);
|
env->DeleteLocalRef(keycodes_array);
|
||||||
|
|
||||||
|
env->CallStaticVoidMethod(s_controller_interface_class,
|
||||||
|
s_controller_interface_register_input_device_listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shutdown()
|
void Shutdown()
|
||||||
{
|
{
|
||||||
JNIEnv* env = IDCache::GetEnvForThread();
|
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_input_device_class);
|
||||||
env->DeleteGlobalRef(s_motion_range_class);
|
env->DeleteGlobalRef(s_motion_range_class);
|
||||||
env->DeleteGlobalRef(s_input_event_class);
|
env->DeleteGlobalRef(s_input_event_class);
|
||||||
env->DeleteGlobalRef(s_key_event_class);
|
env->DeleteGlobalRef(s_key_event_class);
|
||||||
env->DeleteGlobalRef(s_motion_event_class);
|
env->DeleteGlobalRef(s_motion_event_class);
|
||||||
|
env->DeleteGlobalRef(s_controller_interface_class);
|
||||||
env->DeleteGlobalRef(s_keycodes_array);
|
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;
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue