mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-23 06:09:50 -06:00
Android: Make input state changes observable
This commit is contained in:
@ -6,6 +6,7 @@ import android.content.Context
|
|||||||
import android.hardware.input.InputManager
|
import android.hardware.input.InputManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
import android.os.VibrationEffect
|
import android.os.VibrationEffect
|
||||||
import android.os.Vibrator
|
import android.os.Vibrator
|
||||||
import android.os.VibratorManager
|
import android.os.VibratorManager
|
||||||
@ -13,8 +14,11 @@ import android.view.InputDevice
|
|||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import androidx.annotation.Keep
|
import androidx.annotation.Keep
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
import org.dolphinemu.dolphinemu.DolphinApplication
|
import org.dolphinemu.dolphinemu.DolphinApplication
|
||||||
import org.dolphinemu.dolphinemu.utils.LooperThread
|
import org.dolphinemu.dolphinemu.utils.LooperThread
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class interfaces with the native ControllerInterface,
|
* This class interfaces with the native ControllerInterface,
|
||||||
@ -24,6 +28,12 @@ object ControllerInterface {
|
|||||||
private var inputDeviceListener: InputDeviceListener? = null
|
private var inputDeviceListener: InputDeviceListener? = null
|
||||||
private lateinit var looperThread: LooperThread
|
private lateinit var looperThread: LooperThread
|
||||||
|
|
||||||
|
private var inputStateUpdatePending = AtomicBoolean(false)
|
||||||
|
private val inputStateVersion = MutableLiveData(0)
|
||||||
|
|
||||||
|
val inputStateChanged: LiveData<Int>
|
||||||
|
get() = inputStateVersion
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
@ -31,7 +41,13 @@ object ControllerInterface {
|
|||||||
* @return true if the emulator core seems to be interested in this event.
|
* @return true if the emulator core seems to be interested in this event.
|
||||||
* false if the event should be passed on to the default dispatchKeyEvent.
|
* false if the event should be passed on to the default dispatchKeyEvent.
|
||||||
*/
|
*/
|
||||||
external fun dispatchKeyEvent(event: KeyEvent): Boolean
|
fun dispatchKeyEvent(event: KeyEvent): Boolean {
|
||||||
|
val result = dispatchKeyEventNative(event)
|
||||||
|
onInputStateChanged()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private external fun dispatchKeyEventNative(event: KeyEvent): Boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Activities which want to pass on inputs to native code
|
* Activities which want to pass on inputs to native code
|
||||||
@ -40,7 +56,13 @@ object ControllerInterface {
|
|||||||
* @return true if the emulator core seems to be interested in this event.
|
* @return true if the emulator core seems to be interested in this event.
|
||||||
* false if the event should be passed on to the default dispatchGenericMotionEvent.
|
* false if the event should be passed on to the default dispatchGenericMotionEvent.
|
||||||
*/
|
*/
|
||||||
external fun dispatchGenericMotionEvent(event: MotionEvent): Boolean
|
fun dispatchGenericMotionEvent(event: MotionEvent): Boolean {
|
||||||
|
val result = dispatchGenericMotionEventNative(event)
|
||||||
|
onInputStateChanged()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private external fun dispatchGenericMotionEventNative(event: MotionEvent): Boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [DolphinSensorEventListener] calls this for each axis of a received SensorEvent.
|
* [DolphinSensorEventListener] calls this for each axis of a received SensorEvent.
|
||||||
@ -48,7 +70,13 @@ object ControllerInterface {
|
|||||||
* @return true if the emulator core seems to be interested in this event.
|
* @return true if the emulator core seems to be interested in this event.
|
||||||
* false if the sensor can be suspended to save battery.
|
* false if the sensor can be suspended to save battery.
|
||||||
*/
|
*/
|
||||||
external fun dispatchSensorEvent(
|
fun dispatchSensorEvent(deviceQualifier: String, axisName: String, value: Float): Boolean {
|
||||||
|
val result = dispatchSensorEventNative(deviceQualifier, axisName, value)
|
||||||
|
onInputStateChanged()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private external fun dispatchSensorEventNative(
|
||||||
deviceQualifier: String,
|
deviceQualifier: String,
|
||||||
axisName: String,
|
axisName: String,
|
||||||
value: Float
|
value: Float
|
||||||
@ -76,6 +104,19 @@ object ControllerInterface {
|
|||||||
|
|
||||||
external fun getDevice(deviceString: String): CoreDevice?
|
external fun getDevice(deviceString: String): CoreDevice?
|
||||||
|
|
||||||
|
private fun onInputStateChanged() {
|
||||||
|
// When a single SensorEvent is dispatched, this method is likely to get called many times.
|
||||||
|
// For the sake of performance, let's batch input state updates so that observers only have
|
||||||
|
// to process one update.
|
||||||
|
if (!inputStateUpdatePending.getAndSet(true)) {
|
||||||
|
Handler(Looper.getMainLooper()).post {
|
||||||
|
if (inputStateUpdatePending.getAndSet(false)) {
|
||||||
|
inputStateVersion.value = inputStateVersion.value?.plus(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
private fun registerInputDeviceListener() {
|
private fun registerInputDeviceListener() {
|
||||||
|
@ -1002,7 +1002,7 @@ void InputBackend::PopulateDevices()
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
JNIEXPORT jboolean JNICALL
|
JNIEXPORT jboolean JNICALL
|
||||||
Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_dispatchKeyEvent(
|
Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_dispatchKeyEventNative(
|
||||||
JNIEnv* env, jclass, jobject key_event)
|
JNIEnv* env, jclass, jobject key_event)
|
||||||
{
|
{
|
||||||
const jint action = env->CallIntMethod(key_event, s_key_event_get_action);
|
const jint action = env->CallIntMethod(key_event, s_key_event_get_action);
|
||||||
@ -1046,7 +1046,7 @@ Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_dispatch
|
|||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT jboolean JNICALL
|
JNIEXPORT jboolean JNICALL
|
||||||
Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_dispatchGenericMotionEvent(
|
Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_dispatchGenericMotionEventNative(
|
||||||
JNIEnv* env, jclass, jobject motion_event)
|
JNIEnv* env, jclass, jobject motion_event)
|
||||||
{
|
{
|
||||||
const jint device_id = env->CallIntMethod(motion_event, s_input_event_get_device_id);
|
const jint device_id = env->CallIntMethod(motion_event, s_input_event_get_device_id);
|
||||||
@ -1090,7 +1090,7 @@ Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_dispatch
|
|||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT jboolean JNICALL
|
JNIEXPORT jboolean JNICALL
|
||||||
Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_dispatchSensorEvent(
|
Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_dispatchSensorEventNative(
|
||||||
JNIEnv* env, jclass, jstring j_device_qualifier, jstring j_axis_name, jfloat value)
|
JNIEnv* env, jclass, jstring j_device_qualifier, jstring j_axis_name, jfloat value)
|
||||||
{
|
{
|
||||||
ciface::Core::DeviceQualifier device_qualifier;
|
ciface::Core::DeviceQualifier device_qualifier;
|
||||||
|
Reference in New Issue
Block a user