ControllerInterface/Android: Implement sensor input for InputDevices
This functionality was added in Android 12 to let apps get motion data for gamepads.
This commit is contained in:
parent
5e51b56d72
commit
36acb17700
|
@ -67,7 +67,8 @@ public final class ControllerInterface
|
||||||
/**
|
/**
|
||||||
* {@link DolphinSensorEventListener} calls this for each axis of a received SensorEvent.
|
* {@link DolphinSensorEventListener} calls this for each axis of a received SensorEvent.
|
||||||
*/
|
*/
|
||||||
public static native void dispatchSensorEvent(String axisName, float value);
|
public static native void dispatchSensorEvent(String deviceQualifier, String axisName,
|
||||||
|
float value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables delivering sensor events to native code.
|
* Enables delivering sensor events to native code.
|
||||||
|
|
|
@ -6,6 +6,7 @@ import android.hardware.SensorEvent;
|
||||||
import android.hardware.SensorEventListener;
|
import android.hardware.SensorEventListener;
|
||||||
import android.hardware.SensorManager;
|
import android.hardware.SensorManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.view.InputDevice;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
|
|
||||||
import androidx.annotation.Keep;
|
import androidx.annotation.Keep;
|
||||||
|
@ -55,6 +56,10 @@ public class DolphinSensorEventListener implements SensorEventListener
|
||||||
|
|
||||||
private final HashMap<Sensor, SensorDetails> mSensorDetails = new HashMap<>();
|
private final HashMap<Sensor, SensorDetails> mSensorDetails = new HashMap<>();
|
||||||
|
|
||||||
|
private final boolean mRotateCoordinatesForScreenOrientation;
|
||||||
|
|
||||||
|
private String mDeviceQualifier = "";
|
||||||
|
|
||||||
private SensorEventRequester mRequester = null;
|
private SensorEventRequester mRequester = null;
|
||||||
|
|
||||||
// The fastest sampling rate Android lets us use without declaring the HIGH_SAMPLING_RATE_SENSORS
|
// The fastest sampling rate Android lets us use without declaring the HIGH_SAMPLING_RATE_SENSORS
|
||||||
|
@ -66,10 +71,28 @@ public class DolphinSensorEventListener implements SensorEventListener
|
||||||
{
|
{
|
||||||
mSensorManager = (SensorManager)
|
mSensorManager = (SensorManager)
|
||||||
DolphinApplication.getAppContext().getSystemService(Context.SENSOR_SERVICE);
|
DolphinApplication.getAppContext().getSystemService(Context.SENSOR_SERVICE);
|
||||||
|
mRotateCoordinatesForScreenOrientation = true;
|
||||||
|
|
||||||
addSensors();
|
addSensors();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
public DolphinSensorEventListener(InputDevice inputDevice)
|
||||||
|
{
|
||||||
|
mRotateCoordinatesForScreenOrientation = false;
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= 31)
|
||||||
|
{
|
||||||
|
mSensorManager = inputDevice.getSensorManager();
|
||||||
|
|
||||||
|
addSensors();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mSensorManager = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void addSensors()
|
private void addSensors()
|
||||||
{
|
{
|
||||||
tryAddSensor(Sensor.TYPE_ACCELEROMETER, new String[]{"Accel Right", "Accel Left",
|
tryAddSensor(Sensor.TYPE_ACCELEROMETER, new String[]{"Accel Right", "Accel Left",
|
||||||
|
@ -201,7 +224,8 @@ public class DolphinSensorEventListener implements SensorEventListener
|
||||||
axisSetDetails[detailsAxisSetIndex].firstAxisOfSet == eventAxisIndex)
|
axisSetDetails[detailsAxisSetIndex].firstAxisOfSet == eventAxisIndex)
|
||||||
{
|
{
|
||||||
int rotation = Surface.ROTATION_0;
|
int rotation = Surface.ROTATION_0;
|
||||||
if (axisSetDetails[detailsAxisSetIndex].axisSetType == AXIS_SET_TYPE_DEVICE_COORDINATES)
|
if (mRotateCoordinatesForScreenOrientation &&
|
||||||
|
axisSetDetails[detailsAxisSetIndex].axisSetType == AXIS_SET_TYPE_DEVICE_COORDINATES)
|
||||||
{
|
{
|
||||||
rotation = mRequester.getDisplay().getRotation();
|
rotation = mRequester.getDisplay().getRotation();
|
||||||
}
|
}
|
||||||
|
@ -230,12 +254,17 @@ public class DolphinSensorEventListener implements SensorEventListener
|
||||||
|
|
||||||
float z = values[eventAxisIndex + 2];
|
float z = values[eventAxisIndex + 2];
|
||||||
|
|
||||||
ControllerInterface.dispatchSensorEvent(axisNames[detailsAxisIndex], x);
|
ControllerInterface.dispatchSensorEvent(mDeviceQualifier, axisNames[detailsAxisIndex], x);
|
||||||
ControllerInterface.dispatchSensorEvent(axisNames[detailsAxisIndex + 1], x);
|
ControllerInterface.dispatchSensorEvent(mDeviceQualifier, axisNames[detailsAxisIndex + 1],
|
||||||
ControllerInterface.dispatchSensorEvent(axisNames[detailsAxisIndex + 2], y);
|
x);
|
||||||
ControllerInterface.dispatchSensorEvent(axisNames[detailsAxisIndex + 3], y);
|
ControllerInterface.dispatchSensorEvent(mDeviceQualifier, axisNames[detailsAxisIndex + 2],
|
||||||
ControllerInterface.dispatchSensorEvent(axisNames[detailsAxisIndex + 4], z);
|
y);
|
||||||
ControllerInterface.dispatchSensorEvent(axisNames[detailsAxisIndex + 5], z);
|
ControllerInterface.dispatchSensorEvent(mDeviceQualifier, axisNames[detailsAxisIndex + 3],
|
||||||
|
y);
|
||||||
|
ControllerInterface.dispatchSensorEvent(mDeviceQualifier, axisNames[detailsAxisIndex + 4],
|
||||||
|
z);
|
||||||
|
ControllerInterface.dispatchSensorEvent(mDeviceQualifier, axisNames[detailsAxisIndex + 5],
|
||||||
|
z);
|
||||||
|
|
||||||
eventAxisIndex += 3;
|
eventAxisIndex += 3;
|
||||||
detailsAxisIndex += 6;
|
detailsAxisIndex += 6;
|
||||||
|
@ -243,7 +272,7 @@ public class DolphinSensorEventListener implements SensorEventListener
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ControllerInterface.dispatchSensorEvent(axisNames[detailsAxisIndex],
|
ControllerInterface.dispatchSensorEvent(mDeviceQualifier, axisNames[detailsAxisIndex],
|
||||||
values[eventAxisIndex]);
|
values[eventAxisIndex]);
|
||||||
|
|
||||||
eventAxisIndex++;
|
eventAxisIndex++;
|
||||||
|
@ -258,6 +287,16 @@ public class DolphinSensorEventListener implements SensorEventListener
|
||||||
// We don't care about this
|
// We don't care about this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The device qualifier set here will be passed on to native code,
|
||||||
|
* for the purpose of letting native code identify which device this object belongs to.
|
||||||
|
*/
|
||||||
|
@Keep
|
||||||
|
public void setDeviceQualifier(String deviceQualifier)
|
||||||
|
{
|
||||||
|
mDeviceQualifier = deviceQualifier;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables delivering sensor events to native code.
|
* Enables delivering sensor events to native code.
|
||||||
*
|
*
|
||||||
|
@ -275,9 +314,12 @@ public class DolphinSensorEventListener implements SensorEventListener
|
||||||
|
|
||||||
mRequester = requester;
|
mRequester = requester;
|
||||||
|
|
||||||
for (Sensor sensor : mSensorDetails.keySet())
|
if (mSensorManager != null)
|
||||||
{
|
{
|
||||||
mSensorManager.registerListener(this, sensor, SAMPLING_PERIOD_US);
|
for (Sensor sensor : mSensorDetails.keySet())
|
||||||
|
{
|
||||||
|
mSensorManager.registerListener(this, sensor, SAMPLING_PERIOD_US);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,7 +333,10 @@ public class DolphinSensorEventListener implements SensorEventListener
|
||||||
{
|
{
|
||||||
mRequester = null;
|
mRequester = null;
|
||||||
|
|
||||||
mSensorManager.unregisterListener(this);
|
if (mSensorManager != null)
|
||||||
|
{
|
||||||
|
mSensorManager.unregisterListener(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
|
|
|
@ -65,6 +65,8 @@ jmethodID s_controller_interface_unregister_input_device_listener;
|
||||||
|
|
||||||
jclass s_sensor_event_listener_class;
|
jclass s_sensor_event_listener_class;
|
||||||
jmethodID s_sensor_event_listener_constructor;
|
jmethodID s_sensor_event_listener_constructor;
|
||||||
|
jmethodID s_sensor_event_listener_constructor_input_device;
|
||||||
|
jmethodID s_sensor_event_listener_set_device_qualifier;
|
||||||
jmethodID s_sensor_event_listener_enable_sensor_events;
|
jmethodID s_sensor_event_listener_enable_sensor_events;
|
||||||
jmethodID s_sensor_event_listener_disable_sensor_events;
|
jmethodID s_sensor_event_listener_disable_sensor_events;
|
||||||
jmethodID s_sensor_event_listener_get_axis_names;
|
jmethodID s_sensor_event_listener_get_axis_names;
|
||||||
|
@ -76,7 +78,6 @@ using Clock = std::chrono::steady_clock;
|
||||||
constexpr Clock::duration ACTIVE_INPUT_TIMEOUT = std::chrono::milliseconds(1000);
|
constexpr Clock::duration ACTIVE_INPUT_TIMEOUT = std::chrono::milliseconds(1000);
|
||||||
|
|
||||||
std::unordered_map<jint, ciface::Core::DeviceQualifier> s_device_id_to_device_qualifier;
|
std::unordered_map<jint, ciface::Core::DeviceQualifier> s_device_id_to_device_qualifier;
|
||||||
ciface::Core::DeviceQualifier s_sensor_device_qualifier;
|
|
||||||
|
|
||||||
constexpr int MAX_KEYCODE = AKEYCODE_PROFILE_SWITCH; // Up to date as of SDK 31
|
constexpr int MAX_KEYCODE = AKEYCODE_PROFILE_SWITCH; // Up to date as of SDK 31
|
||||||
|
|
||||||
|
@ -502,7 +503,7 @@ class AndroidDevice final : public Core::Device
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AndroidDevice(JNIEnv* env, jobject input_device)
|
AndroidDevice(JNIEnv* env, jobject input_device)
|
||||||
: m_sensor_event_listener(nullptr),
|
: m_sensor_event_listener(AddSensors(env, input_device)),
|
||||||
m_source(env->CallIntMethod(input_device, s_input_device_get_sources)),
|
m_source(env->CallIntMethod(input_device, s_input_device_get_sources)),
|
||||||
m_controller_number(env->CallIntMethod(input_device, s_input_device_get_controller_number))
|
m_controller_number(env->CallIntMethod(input_device, s_input_device_get_controller_number))
|
||||||
{
|
{
|
||||||
|
@ -519,7 +520,7 @@ public:
|
||||||
|
|
||||||
// Constructor for the device added by Dolphin to contain sensor inputs
|
// Constructor for the device added by Dolphin to contain sensor inputs
|
||||||
AndroidDevice(JNIEnv* env, std::string name)
|
AndroidDevice(JNIEnv* env, std::string name)
|
||||||
: m_sensor_event_listener(AddSensors(env)), m_source(AINPUT_SOURCE_SENSOR),
|
: m_sensor_event_listener(AddSensors(env, nullptr)), m_source(AINPUT_SOURCE_SENSOR),
|
||||||
m_controller_number(0), m_name(std::move(name))
|
m_controller_number(0), m_name(std::move(name))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -608,10 +609,20 @@ private:
|
||||||
env->DeleteLocalRef(motion_ranges_list);
|
env->DeleteLocalRef(motion_ranges_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
jobject AddSensors(JNIEnv* env)
|
jobject AddSensors(JNIEnv* env, jobject input_device)
|
||||||
{
|
{
|
||||||
jobject sensor_event_listener =
|
jobject sensor_event_listener;
|
||||||
env->NewObject(s_sensor_event_listener_class, s_sensor_event_listener_constructor);
|
if (input_device)
|
||||||
|
{
|
||||||
|
sensor_event_listener =
|
||||||
|
env->NewObject(s_sensor_event_listener_class,
|
||||||
|
s_sensor_event_listener_constructor_input_device, input_device);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sensor_event_listener =
|
||||||
|
env->NewObject(s_sensor_event_listener_class, s_sensor_event_listener_constructor);
|
||||||
|
}
|
||||||
|
|
||||||
jobjectArray j_axis_names = reinterpret_cast<jobjectArray>(
|
jobjectArray j_axis_names = reinterpret_cast<jobjectArray>(
|
||||||
env->CallObjectMethod(sensor_event_listener, s_sensor_event_listener_get_axis_names));
|
env->CallObjectMethod(sensor_event_listener, s_sensor_event_listener_get_axis_names));
|
||||||
|
@ -720,6 +731,10 @@ void Init()
|
||||||
reinterpret_cast<jclass>(env->NewGlobalRef(sensor_event_listener_class));
|
reinterpret_cast<jclass>(env->NewGlobalRef(sensor_event_listener_class));
|
||||||
s_sensor_event_listener_constructor =
|
s_sensor_event_listener_constructor =
|
||||||
env->GetMethodID(s_sensor_event_listener_class, "<init>", "()V");
|
env->GetMethodID(s_sensor_event_listener_class, "<init>", "()V");
|
||||||
|
s_sensor_event_listener_constructor_input_device =
|
||||||
|
env->GetMethodID(s_sensor_event_listener_class, "<init>", "(Landroid/view/InputDevice;)V");
|
||||||
|
s_sensor_event_listener_set_device_qualifier = env->GetMethodID(
|
||||||
|
s_sensor_event_listener_class, "setDeviceQualifier", "(Ljava/lang/String;)V");
|
||||||
s_sensor_event_listener_enable_sensor_events =
|
s_sensor_event_listener_enable_sensor_events =
|
||||||
env->GetMethodID(s_sensor_event_listener_class, "enableSensorEvents",
|
env->GetMethodID(s_sensor_event_listener_class, "enableSensorEvents",
|
||||||
"(Lorg/dolphinemu/dolphinemu/features/input/model/SensorEventRequester;)V");
|
"(Lorg/dolphinemu/dolphinemu/features/input/model/SensorEventRequester;)V");
|
||||||
|
@ -775,6 +790,11 @@ static void AddDevice(JNIEnv* env, int device_id)
|
||||||
INFO_LOG_FMT(CONTROLLERINTERFACE, "Added device ID {} as {}", device_id,
|
INFO_LOG_FMT(CONTROLLERINTERFACE, "Added device ID {} as {}", device_id,
|
||||||
device->GetQualifiedName());
|
device->GetQualifiedName());
|
||||||
s_device_id_to_device_qualifier.emplace(device_id, qualifier);
|
s_device_id_to_device_qualifier.emplace(device_id, qualifier);
|
||||||
|
|
||||||
|
jstring j_qualifier = ToJString(env, qualifier.ToString());
|
||||||
|
env->CallVoidMethod(device->GetSensorEventListener(),
|
||||||
|
s_sensor_event_listener_set_device_qualifier, j_qualifier);
|
||||||
|
env->DeleteLocalRef(j_qualifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void AddSensorDevice(JNIEnv* env)
|
static void AddSensorDevice(JNIEnv* env)
|
||||||
|
@ -793,7 +813,11 @@ static void AddSensorDevice(JNIEnv* env)
|
||||||
qualifier.FromDevice(device.get());
|
qualifier.FromDevice(device.get());
|
||||||
|
|
||||||
INFO_LOG_FMT(CONTROLLERINTERFACE, "Added sensor device as {}", device->GetQualifiedName());
|
INFO_LOG_FMT(CONTROLLERINTERFACE, "Added sensor device as {}", device->GetQualifiedName());
|
||||||
s_sensor_device_qualifier = qualifier;
|
|
||||||
|
jstring j_qualifier = ToJString(env, qualifier.ToString());
|
||||||
|
env->CallVoidMethod(device->GetSensorEventListener(),
|
||||||
|
s_sensor_event_listener_set_device_qualifier, j_qualifier);
|
||||||
|
env->DeleteLocalRef(j_qualifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PopulateDevices()
|
void PopulateDevices()
|
||||||
|
@ -912,10 +936,12 @@ Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_dispatch
|
||||||
|
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_dispatchSensorEvent(
|
Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_dispatchSensorEvent(
|
||||||
JNIEnv* env, jclass, jstring j_axis_name, jfloat value)
|
JNIEnv* env, jclass, jstring j_device_qualifier, jstring j_axis_name, jfloat value)
|
||||||
{
|
{
|
||||||
|
ciface::Core::DeviceQualifier device_qualifier;
|
||||||
|
device_qualifier.FromString(GetJString(env, j_device_qualifier));
|
||||||
const std::shared_ptr<ciface::Core::Device> device =
|
const std::shared_ptr<ciface::Core::Device> device =
|
||||||
g_controller_interface.FindDevice(s_sensor_device_qualifier);
|
g_controller_interface.FindDevice(device_qualifier);
|
||||||
if (!device)
|
if (!device)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -936,28 +962,24 @@ JNIEXPORT void JNICALL
|
||||||
Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_enableSensorEvents(
|
Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_enableSensorEvents(
|
||||||
JNIEnv* env, jclass, jobject j_sensor_event_requester)
|
JNIEnv* env, jclass, jobject j_sensor_event_requester)
|
||||||
{
|
{
|
||||||
const std::shared_ptr<ciface::Core::Device> device =
|
for (std::shared_ptr<ciface::Core::Device>& device : g_controller_interface.GetAllDevices())
|
||||||
g_controller_interface.FindDevice(s_sensor_device_qualifier);
|
{
|
||||||
if (!device)
|
env->CallVoidMethod(
|
||||||
return;
|
static_cast<ciface::Android::AndroidDevice*>(device.get())->GetSensorEventListener(),
|
||||||
|
s_sensor_event_listener_enable_sensor_events, j_sensor_event_requester);
|
||||||
env->CallVoidMethod(
|
}
|
||||||
static_cast<ciface::Android::AndroidDevice*>(device.get())->GetSensorEventListener(),
|
|
||||||
s_sensor_event_listener_enable_sensor_events, j_sensor_event_requester);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_disableSensorEvents(
|
Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_disableSensorEvents(
|
||||||
JNIEnv* env, jclass)
|
JNIEnv* env, jclass)
|
||||||
{
|
{
|
||||||
const std::shared_ptr<ciface::Core::Device> device =
|
for (std::shared_ptr<ciface::Core::Device>& device : g_controller_interface.GetAllDevices())
|
||||||
g_controller_interface.FindDevice(s_sensor_device_qualifier);
|
{
|
||||||
if (!device)
|
env->CallVoidMethod(
|
||||||
return;
|
static_cast<ciface::Android::AndroidDevice*>(device.get())->GetSensorEventListener(),
|
||||||
|
s_sensor_event_listener_disable_sensor_events);
|
||||||
env->CallVoidMethod(
|
}
|
||||||
static_cast<ciface::Android::AndroidDevice*>(device.get())->GetSensorEventListener(),
|
|
||||||
s_sensor_event_listener_disable_sensor_events);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
|
|
|
@ -242,6 +242,18 @@ std::shared_ptr<Device> DeviceContainer::FindDevice(const DeviceQualifier& devq)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<Device>> DeviceContainer::GetAllDevices() const
|
||||||
|
{
|
||||||
|
std::lock_guard lk(m_devices_mutex);
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<Device>> devices;
|
||||||
|
|
||||||
|
for (const auto& d : m_devices)
|
||||||
|
devices.emplace_back(d);
|
||||||
|
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::string> DeviceContainer::GetAllDeviceStrings() const
|
std::vector<std::string> DeviceContainer::GetAllDeviceStrings() const
|
||||||
{
|
{
|
||||||
std::lock_guard lk(m_devices_mutex);
|
std::lock_guard lk(m_devices_mutex);
|
||||||
|
|
|
@ -226,6 +226,7 @@ public:
|
||||||
Device::Input* FindInput(std::string_view name, const Device* def_dev) const;
|
Device::Input* FindInput(std::string_view name, const Device* def_dev) const;
|
||||||
Device::Output* FindOutput(std::string_view name, const Device* def_dev) const;
|
Device::Output* FindOutput(std::string_view name, const Device* def_dev) const;
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<Device>> GetAllDevices() const;
|
||||||
std::vector<std::string> GetAllDeviceStrings() const;
|
std::vector<std::string> GetAllDeviceStrings() const;
|
||||||
bool HasDefaultDevice() const;
|
bool HasDefaultDevice() const;
|
||||||
std::string GetDefaultDeviceString() const;
|
std::string GetDefaultDeviceString() const;
|
||||||
|
|
Loading…
Reference in New Issue