ControllerInterface/Android: Implement rumble
This commit is contained in:
parent
065481d989
commit
8e33458f48
|
@ -4,11 +4,18 @@ package org.dolphinemu.dolphinemu.features.input.model;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.hardware.input.InputManager;
|
import android.hardware.input.InputManager;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.os.VibrationEffect;
|
||||||
|
import android.os.Vibrator;
|
||||||
|
import android.os.VibratorManager;
|
||||||
|
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.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.dolphinemu.dolphinemu.DolphinApplication;
|
import org.dolphinemu.dolphinemu.DolphinApplication;
|
||||||
import org.dolphinemu.dolphinemu.utils.LooperThread;
|
import org.dolphinemu.dolphinemu.utils.LooperThread;
|
||||||
|
@ -119,4 +126,48 @@ public final class ControllerInterface
|
||||||
mInputDeviceListener = null;
|
mInputDeviceListener = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Keep @NonNull
|
||||||
|
private static DolphinVibratorManager getVibratorManager(InputDevice device)
|
||||||
|
{
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
|
||||||
|
{
|
||||||
|
return new DolphinVibratorManagerPassthrough(device.getVibratorManager());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new DolphinVibratorManagerCompat(device.getVibrator());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Keep @NonNull
|
||||||
|
private static DolphinVibratorManager getSystemVibratorManager()
|
||||||
|
{
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
|
||||||
|
{
|
||||||
|
VibratorManager vibratorManager = (VibratorManager)
|
||||||
|
DolphinApplication.getAppContext().getSystemService(Context.VIBRATOR_MANAGER_SERVICE);
|
||||||
|
|
||||||
|
if (vibratorManager != null)
|
||||||
|
return new DolphinVibratorManagerPassthrough(vibratorManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vibrator vibrator = (Vibrator)
|
||||||
|
DolphinApplication.getAppContext().getSystemService(Context.VIBRATOR_SERVICE);
|
||||||
|
|
||||||
|
return new DolphinVibratorManagerCompat(vibrator);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
private static void vibrate(@NonNull Vibrator vibrator)
|
||||||
|
{
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||||
|
{
|
||||||
|
vibrator.vibrate(VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vibrator.vibrate(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
package org.dolphinemu.dolphinemu.features.input.model;
|
||||||
|
|
||||||
|
import android.os.Vibrator;
|
||||||
|
|
||||||
|
import androidx.annotation.Keep;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A wrapper around {@link android.os.VibratorManager}, for backwards compatibility.
|
||||||
|
*/
|
||||||
|
public interface DolphinVibratorManager
|
||||||
|
{
|
||||||
|
@Keep @NonNull
|
||||||
|
Vibrator getVibrator(int vibratorId);
|
||||||
|
|
||||||
|
@Keep @NonNull
|
||||||
|
int[] getVibratorIds();
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
package org.dolphinemu.dolphinemu.features.input.model;
|
||||||
|
|
||||||
|
import android.os.Vibrator;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
public final class DolphinVibratorManagerCompat implements DolphinVibratorManager
|
||||||
|
{
|
||||||
|
private final Vibrator mVibrator;
|
||||||
|
private final int[] mIds;
|
||||||
|
|
||||||
|
public DolphinVibratorManagerCompat(@Nullable Vibrator vibrator)
|
||||||
|
{
|
||||||
|
mVibrator = vibrator;
|
||||||
|
mIds = vibrator != null && vibrator.hasVibrator() ? new int[]{0} : new int[]{};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override @NonNull
|
||||||
|
public Vibrator getVibrator(int vibratorId)
|
||||||
|
{
|
||||||
|
if (vibratorId > mIds.length)
|
||||||
|
throw new IndexOutOfBoundsException();
|
||||||
|
|
||||||
|
return mVibrator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override @NonNull
|
||||||
|
public int[] getVibratorIds()
|
||||||
|
{
|
||||||
|
return mIds;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
package org.dolphinemu.dolphinemu.features.input.model;
|
||||||
|
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Vibrator;
|
||||||
|
import android.os.VibratorManager;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.S)
|
||||||
|
public final class DolphinVibratorManagerPassthrough implements DolphinVibratorManager
|
||||||
|
{
|
||||||
|
private final VibratorManager mVibratorManager;
|
||||||
|
|
||||||
|
public DolphinVibratorManagerPassthrough(@NonNull VibratorManager vibratorManager)
|
||||||
|
{
|
||||||
|
mVibratorManager = vibratorManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override @NonNull
|
||||||
|
public Vibrator getVibrator(int vibratorId)
|
||||||
|
{
|
||||||
|
return mVibratorManager.getVibrator(vibratorId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override @NonNull
|
||||||
|
public int[] getVibratorIds()
|
||||||
|
{
|
||||||
|
return mVibratorManager.getVibratorIds();
|
||||||
|
}
|
||||||
|
}
|
|
@ -62,6 +62,9 @@ jmethodID s_motion_event_get_source;
|
||||||
jclass s_controller_interface_class;
|
jclass s_controller_interface_class;
|
||||||
jmethodID s_controller_interface_register_input_device_listener;
|
jmethodID s_controller_interface_register_input_device_listener;
|
||||||
jmethodID s_controller_interface_unregister_input_device_listener;
|
jmethodID s_controller_interface_unregister_input_device_listener;
|
||||||
|
jmethodID s_controller_interface_get_vibrator_manager;
|
||||||
|
jmethodID s_controller_interface_get_system_vibrator_manager;
|
||||||
|
jmethodID s_controller_interface_vibrate;
|
||||||
|
|
||||||
jclass s_sensor_event_listener_class;
|
jclass s_sensor_event_listener_class;
|
||||||
jmethodID s_sensor_event_listener_constructor;
|
jmethodID s_sensor_event_listener_constructor;
|
||||||
|
@ -71,6 +74,10 @@ jmethodID s_sensor_event_listener_request_unsuspend_sensor;
|
||||||
jmethodID s_sensor_event_listener_get_axis_names;
|
jmethodID s_sensor_event_listener_get_axis_names;
|
||||||
jmethodID s_sensor_event_listener_get_negative_axes;
|
jmethodID s_sensor_event_listener_get_negative_axes;
|
||||||
|
|
||||||
|
jclass s_dolphin_vibrator_manager_class;
|
||||||
|
jmethodID s_dolphin_vibrator_manager_get_vibrator;
|
||||||
|
jmethodID s_dolphin_vibrator_manager_get_vibrator_ids;
|
||||||
|
|
||||||
jintArray s_keycodes_array;
|
jintArray s_keycodes_array;
|
||||||
|
|
||||||
using Clock = std::chrono::steady_clock;
|
using Clock = std::chrono::steady_clock;
|
||||||
|
@ -534,6 +541,35 @@ private:
|
||||||
std::atomic<bool> m_is_suspended = true;
|
std::atomic<bool> m_is_suspended = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class AndroidMotor : public Core::Device::Output
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AndroidMotor(JNIEnv* env, jobject vibrator, jint id)
|
||||||
|
: m_vibrator(env->NewGlobalRef(vibrator)), m_id(id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~AndroidMotor() { IDCache::GetEnvForThread()->DeleteGlobalRef(m_vibrator); }
|
||||||
|
|
||||||
|
std::string GetName() const override { return "Motor " + std::to_string(m_id); }
|
||||||
|
|
||||||
|
void SetState(ControlState state) override
|
||||||
|
{
|
||||||
|
ControlState old_state = m_state.exchange(state, std::memory_order_relaxed);
|
||||||
|
|
||||||
|
if (old_state < 0.5 && state >= 0.5)
|
||||||
|
{
|
||||||
|
IDCache::GetEnvForThread()->CallStaticVoidMethod(s_controller_interface_class,
|
||||||
|
s_controller_interface_vibrate, m_vibrator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const jobject m_vibrator;
|
||||||
|
const jint m_id;
|
||||||
|
std::atomic<ControlState> m_state = 0;
|
||||||
|
};
|
||||||
|
|
||||||
class AndroidDevice final : public Core::Device
|
class AndroidDevice final : public Core::Device
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -551,6 +587,7 @@ public:
|
||||||
|
|
||||||
AddKeys(env, input_device);
|
AddKeys(env, input_device);
|
||||||
AddAxes(env, input_device);
|
AddAxes(env, input_device);
|
||||||
|
AddMotors(env, input_device);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Constructor for the device added by Dolphin to contain sensor inputs
|
// Constructor for the device added by Dolphin to contain sensor inputs
|
||||||
|
@ -558,6 +595,7 @@ public:
|
||||||
: m_sensor_event_listener(AddSensors(env, nullptr)), 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))
|
||||||
{
|
{
|
||||||
|
AddSystemMotors(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
~AndroidDevice()
|
~AndroidDevice()
|
||||||
|
@ -687,6 +725,41 @@ private:
|
||||||
return sensor_event_listener;
|
return sensor_event_listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AddMotors(JNIEnv* env, jobject input_device)
|
||||||
|
{
|
||||||
|
jobject vibrator_manager = env->CallStaticObjectMethod(
|
||||||
|
s_controller_interface_class, s_controller_interface_get_vibrator_manager, input_device);
|
||||||
|
AddMotorsFromManager(env, vibrator_manager);
|
||||||
|
env->DeleteLocalRef(vibrator_manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddSystemMotors(JNIEnv* env)
|
||||||
|
{
|
||||||
|
jobject vibrator_manager = env->CallStaticObjectMethod(
|
||||||
|
s_controller_interface_class, s_controller_interface_get_system_vibrator_manager);
|
||||||
|
AddMotorsFromManager(env, vibrator_manager);
|
||||||
|
env->DeleteLocalRef(vibrator_manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddMotorsFromManager(JNIEnv* env, jobject vibrator_manager)
|
||||||
|
{
|
||||||
|
jintArray j_vibrator_ids = reinterpret_cast<jintArray>(
|
||||||
|
env->CallObjectMethod(vibrator_manager, s_dolphin_vibrator_manager_get_vibrator_ids));
|
||||||
|
jint* vibrator_ids = env->GetIntArrayElements(j_vibrator_ids, nullptr);
|
||||||
|
|
||||||
|
jint size = env->GetArrayLength(j_vibrator_ids);
|
||||||
|
for (jint i = 0; i < size; ++i)
|
||||||
|
{
|
||||||
|
jobject vibrator =
|
||||||
|
env->CallObjectMethod(vibrator_manager, s_dolphin_vibrator_manager_get_vibrator, i);
|
||||||
|
AddOutput(new AndroidMotor(env, vibrator, i));
|
||||||
|
env->DeleteLocalRef(vibrator);
|
||||||
|
}
|
||||||
|
|
||||||
|
env->ReleaseIntArrayElements(j_vibrator_ids, vibrator_ids, 0);
|
||||||
|
env->DeleteLocalRef(j_vibrator_ids);
|
||||||
|
}
|
||||||
|
|
||||||
const jobject m_sensor_event_listener;
|
const jobject m_sensor_event_listener;
|
||||||
const int m_source;
|
const int m_source;
|
||||||
const int m_controller_number;
|
const int m_controller_number;
|
||||||
|
@ -764,6 +837,15 @@ void Init()
|
||||||
env->GetStaticMethodID(s_controller_interface_class, "registerInputDeviceListener", "()V");
|
env->GetStaticMethodID(s_controller_interface_class, "registerInputDeviceListener", "()V");
|
||||||
s_controller_interface_unregister_input_device_listener =
|
s_controller_interface_unregister_input_device_listener =
|
||||||
env->GetStaticMethodID(s_controller_interface_class, "unregisterInputDeviceListener", "()V");
|
env->GetStaticMethodID(s_controller_interface_class, "unregisterInputDeviceListener", "()V");
|
||||||
|
s_controller_interface_get_vibrator_manager =
|
||||||
|
env->GetStaticMethodID(s_controller_interface_class, "getVibratorManager",
|
||||||
|
"(Landroid/view/InputDevice;)Lorg/dolphinemu/dolphinemu/features/"
|
||||||
|
"input/model/DolphinVibratorManager;");
|
||||||
|
s_controller_interface_get_system_vibrator_manager = env->GetStaticMethodID(
|
||||||
|
s_controller_interface_class, "getSystemVibratorManager",
|
||||||
|
"()Lorg/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManager;");
|
||||||
|
s_controller_interface_vibrate =
|
||||||
|
env->GetStaticMethodID(s_controller_interface_class, "vibrate", "(Landroid/os/Vibrator;)V");
|
||||||
|
|
||||||
const jclass sensor_event_listener_class =
|
const jclass sensor_event_listener_class =
|
||||||
env->FindClass("org/dolphinemu/dolphinemu/features/input/model/DolphinSensorEventListener");
|
env->FindClass("org/dolphinemu/dolphinemu/features/input/model/DolphinSensorEventListener");
|
||||||
|
@ -782,6 +864,15 @@ void Init()
|
||||||
s_sensor_event_listener_get_negative_axes =
|
s_sensor_event_listener_get_negative_axes =
|
||||||
env->GetMethodID(s_sensor_event_listener_class, "getNegativeAxes", "()[Z");
|
env->GetMethodID(s_sensor_event_listener_class, "getNegativeAxes", "()[Z");
|
||||||
|
|
||||||
|
const jclass dolphin_vibrator_manager_class =
|
||||||
|
env->FindClass("org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManager");
|
||||||
|
s_dolphin_vibrator_manager_class =
|
||||||
|
reinterpret_cast<jclass>(env->NewGlobalRef(dolphin_vibrator_manager_class));
|
||||||
|
s_dolphin_vibrator_manager_get_vibrator =
|
||||||
|
env->GetMethodID(s_dolphin_vibrator_manager_class, "getVibrator", "(I)Landroid/os/Vibrator;");
|
||||||
|
s_dolphin_vibrator_manager_get_vibrator_ids =
|
||||||
|
env->GetMethodID(s_dolphin_vibrator_manager_class, "getVibratorIds", "()[I");
|
||||||
|
|
||||||
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);
|
||||||
|
@ -804,6 +895,7 @@ void Shutdown()
|
||||||
env->DeleteGlobalRef(s_motion_event_class);
|
env->DeleteGlobalRef(s_motion_event_class);
|
||||||
env->DeleteGlobalRef(s_controller_interface_class);
|
env->DeleteGlobalRef(s_controller_interface_class);
|
||||||
env->DeleteGlobalRef(s_sensor_event_listener_class);
|
env->DeleteGlobalRef(s_sensor_event_listener_class);
|
||||||
|
env->DeleteGlobalRef(s_dolphin_vibrator_manager_class);
|
||||||
env->DeleteGlobalRef(s_keycodes_array);
|
env->DeleteGlobalRef(s_keycodes_array);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue