ControllerInterface/Android: Implement rumble

This commit is contained in:
JosJuice 2022-09-18 19:14:15 +02:00
parent 065481d989
commit 8e33458f48
5 changed files with 231 additions and 0 deletions

View File

@ -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);
}
}
} }

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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);
} }