android: simple rumble support

This commit is contained in:
Flyinghead 2019-02-23 00:17:59 +01:00
parent a39503dd56
commit c37dbae4e4
5 changed files with 109 additions and 45 deletions

View File

@ -93,6 +93,7 @@ protected:
InputMapping *input_mapper;
std::map<u32, int> axis_min_values;
std::map<u32, unsigned int> axis_ranges;
bool _rumble_enabled = true;
private:
int get_axis_min_value(u32 axis);
@ -105,8 +106,6 @@ private:
input_detected_cb _input_detected;
bool _remappable;
bool _rumble_enabled = true;
static std::vector<std::shared_ptr<GamepadDevice>> _gamepads;
static std::mutex _gamepads_mutex;
};

View File

@ -2,6 +2,7 @@ package com.reicast.emulator;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatDelegate;
@ -10,6 +11,8 @@ import android.util.Log;
import com.reicast.emulator.emu.JNIdc;
public class Emulator extends Application {
private static Context context;
public static final String pref_dynarecopt = "dynarec_opt";
public static final String pref_unstable = "unstable_opt";
public static final String pref_dynsafemode = "dyn_safemode";
@ -262,6 +265,16 @@ public class Emulator extends Application {
return false;
}
@Override
public void onCreate() {
super.onCreate();
Emulator.context = getApplicationContext();
}
public static Context getAppContext() {
return Emulator.context;
}
static {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}

View File

@ -2,9 +2,11 @@ package com.reicast.emulator.periph;
import android.content.Context;
import android.hardware.input.InputManager;
import android.util.Log;
import android.os.Vibrator;
import android.view.InputDevice;
import com.reicast.emulator.Emulator;
public final class InputDeviceManager implements InputManager.InputDeviceListener {
public static final int VIRTUAL_GAMEPAD_ID = 0x12345678;
@ -13,6 +15,11 @@ public final class InputDeviceManager implements InputManager.InputDeviceListene
private InputManager inputManager;
private int maple_port = 0;
public InputDeviceManager()
{
init();
}
public void startListening(Context applicationContext)
{
maple_port = 0;
@ -55,10 +62,34 @@ public final class InputDeviceManager implements InputManager.InputDeviceListene
public void onInputDeviceChanged(int i) {
}
// Called from native code
private boolean Rumble(int i, float power, float inclination, int duration_ms) {
Vibrator vibrator;
if (i == VIRTUAL_GAMEPAD_ID) {
vibrator = (Vibrator)Emulator.getAppContext().getSystemService(Context.VIBRATOR_SERVICE);
}
else {
InputDevice device = InputDevice.getDevice(i);
if (device == null)
return false;
vibrator = device.getVibrator();
if (!vibrator.hasVibrator())
return false;
}
// TODO API >= 26 (Android 8.0)
if (power == 0)
vibrator.cancel();
else
vibrator.vibrate(duration_ms);
return true;
}
public static InputDeviceManager getInstance() {
return INSTANCE;
}
public native void init();
public native void virtualGamepadEvent(int kcode, int joyx, int joyy, int lt, int rt);
public native boolean joystickButtonEvent(int id, int button, boolean pressed);
public native boolean joystickAxisEvent(int id, int button, int value);

View File

@ -22,6 +22,50 @@
#include "reios/reios.h"
#include "imgread/common.h"
#include "rend/gui.h"
JavaVM* g_jvm;
// Convenience class to get the java environment for the current thread.
// Also attach the threads, and detach it on destruction, if needed. This is probably not very efficient
// but shouldn't be needed except for error reporting.
class JVMAttacher {
public:
JVMAttacher() : env(NULL), detach_thread(false) {
if (g_jvm == NULL) {
log_error("g_jvm == NULL");
return;
}
int rc = g_jvm->GetEnv((void **)&env, JNI_VERSION_1_6);
if (rc == JNI_EDETACHED) {
if (g_jvm->AttachCurrentThread(&env, NULL) != 0) {
log_error("AttachCurrentThread failed");
return;
}
detach_thread = true;
}
else if (rc == JNI_EVERSION) {
log_error("JNI version error");
return;
}
}
~JVMAttacher()
{
if (detach_thread)
g_jvm->DetachCurrentThread();
}
void log_error(const char *reason)
{
LOGE("JVMAttacher cannot attach to JVM: %s", reason);
}
bool failed() { return env == NULL; }
JNIEnv *env;
bool detach_thread = false;
};
#include "android_gamepad.h"
#define SETTINGS_ACCESSORS(jsetting, csetting, type) \
@ -101,6 +145,7 @@ JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_screenDpi(JNIEnv *env
JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_guiOpenSettings(JNIEnv *env,jobject obj) __attribute__((visibility("default")));
JNIEXPORT jboolean JNICALL Java_com_reicast_emulator_emu_JNIdc_guiIsOpen(JNIEnv *env,jobject obj) __attribute__((visibility("default")));
JNIEXPORT void JNICALL Java_com_reicast_emulator_periph_InputDeviceManager_init(JNIEnv *env, jobject obj) __attribute__((visibility("default")));
JNIEXPORT void JNICALL Java_com_reicast_emulator_periph_InputDeviceManager_joystickAdded(JNIEnv *env, jobject obj, jint id, jstring name, jint maple_port) __attribute__((visibility("default")));
JNIEXPORT void JNICALL Java_com_reicast_emulator_periph_InputDeviceManager_joystickRemoved(JNIEnv *env, jobject obj, jint id) __attribute__((visibility("default")));
JNIEXPORT void JNICALL Java_com_reicast_emulator_periph_InputDeviceManager_virtualGamepadEvent(JNIEnv *env, jobject obj, jint kcode, jint joyx, jint joyy, jint lt, jint rt) __attribute__((visibility("default")));
@ -151,7 +196,6 @@ extern u32 mo_buttons;
extern bool print_stats;
//stuff for saving prefs
JavaVM* g_jvm;
jobject g_emulator;
jmethodID saveSettingsMid;
static ANativeWindow *g_window = 0;
@ -341,47 +385,6 @@ jobject vmulcd = NULL;
jbyteArray jpix = NULL;
jmethodID updatevmuscreen;
// Convenience class to get the java environment for the current thread.
// Also attach the threads, and detach it on destruction, if needed. This is probably not very efficient
// but shouldn't be needed except for error reporting.
class JVMAttacher {
public:
JVMAttacher() : env(NULL), detach_thread(false) {
if (g_jvm == NULL) {
log_error("g_jvm == NULL");
return;
}
int rc = g_jvm->GetEnv((void **)&env, JNI_VERSION_1_6);
if (rc == JNI_EDETACHED) {
if (g_jvm->AttachCurrentThread(&env, NULL) != 0) {
log_error("AttachCurrentThread failed");
return;
}
detach_thread = true;
}
else if (rc == JNI_EVERSION) {
log_error("JNI version error");
return;
}
}
~JVMAttacher()
{
if (detach_thread)
g_jvm->DetachCurrentThread();
}
void log_error(const char *reason)
{
LOGE("JVMAttacher cannot attach to JVM: %s", reason);
}
bool failed() { return env == NULL; }
JNIEnv *env;
bool detach_thread = false;
};
JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_query(JNIEnv *env,jobject obj,jobject emu_thread)
{
jmethodID reiosInfoMid=env->GetMethodID(env->GetObjectClass(emu_thread),"reiosInfo","(Ljava/lang/String;Ljava/lang/String;)V");
@ -709,6 +712,12 @@ void os_DebugBreak()
for(;;) ;
}
JNIEXPORT void JNICALL Java_com_reicast_emulator_periph_InputDeviceManager_init(JNIEnv *env, jobject obj)
{
input_device_manager = env->NewGlobalRef(obj);
input_device_manager_rumble = env->GetMethodID(env->GetObjectClass(obj), "Rumble", "(IFFI)Z");
}
JNIEXPORT void JNICALL Java_com_reicast_emulator_periph_InputDeviceManager_joystickAdded(JNIEnv *env, jobject obj, jint id, jstring name, jint maple_port)
{
const char* joyname = env->GetStringUTFChars(name,0);

View File

@ -19,6 +19,9 @@
#include "input/gamepad_device.h"
static jobject input_device_manager;
static jmethodID input_device_manager_rumble;
enum {
AXIS_X = 0,
AXIS_Y = 1,
@ -138,6 +141,15 @@ public:
previous_kcode = kcode;
}
void rumble(float power, float inclination, u32 duration_ms) override
{
JVMAttacher jvm_attacher;
if (jvm_attacher.failed())
return;
jboolean has_vibrator = jvm_attacher.env->CallBooleanMethod(input_device_manager, input_device_manager_rumble, android_id, power, inclination, duration_ms);
_rumble_enabled = has_vibrator;
}
static const int VIRTUAL_GAMEPAD_ID = 0x12345678; // must match the Java definition
protected: