Skip to content

Commit

Permalink
Fixed bug 3191 - haptic system on android?
Browse files Browse the repository at this point in the history
Patch provided by jintiao and Milan Nikolic, thanks!
  • Loading branch information
slouken committed Aug 12, 2017
1 parent 78c84e7 commit 0a52db5
Show file tree
Hide file tree
Showing 10 changed files with 521 additions and 3 deletions.
2 changes: 1 addition & 1 deletion Android.mk
Expand Up @@ -28,7 +28,7 @@ LOCAL_SRC_FILES := \
$(wildcard $(LOCAL_PATH)/src/events/*.c) \
$(wildcard $(LOCAL_PATH)/src/file/*.c) \
$(wildcard $(LOCAL_PATH)/src/haptic/*.c) \
$(wildcard $(LOCAL_PATH)/src/haptic/dummy/*.c) \
$(wildcard $(LOCAL_PATH)/src/haptic/android/*.c) \
$(wildcard $(LOCAL_PATH)/src/joystick/*.c) \
$(wildcard $(LOCAL_PATH)/src/joystick/android/*.c) \
$(wildcard $(LOCAL_PATH)/src/loadso/dlopen/*.c) \
Expand Down
2 changes: 2 additions & 0 deletions android-project/AndroidManifest.xml
Expand Up @@ -16,6 +16,8 @@

<!-- Allow writing to external storage -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- Allow access to the vibrator -->
<uses-permission android:name="android.permission.VIBRATE" />

<!-- if you want to capture audio, uncomment this. -->
<!-- <uses-permission android:name="android.permission.RECORD_AUDIO" /> -->
Expand Down
96 changes: 96 additions & 0 deletions android-project/src/org/libsdl/app/SDLActivity.java
Expand Up @@ -61,6 +61,7 @@ public enum NativeState {
protected static View mTextEdit;
protected static ViewGroup mLayout;
protected static SDLJoystickHandler mJoystickHandler;
protected static SDLHapticHandler mHapticHandler;

// This is what SDL runs in. It invokes SDL_main(), eventually
protected static Thread mSDLThread;
Expand Down Expand Up @@ -113,6 +114,7 @@ public static void initialize() {
mTextEdit = null;
mLayout = null;
mJoystickHandler = null;
mHapticHandler = null;
mSDLThread = null;
mAudioTrack = null;
mAudioRecord = null;
Expand Down Expand Up @@ -182,6 +184,7 @@ public void onClick(DialogInterface dialog,int id) {
else {
mJoystickHandler = new SDLJoystickHandler();
}
mHapticHandler = new SDLHapticHandler();

mLayout = new RelativeLayout(this);
mLayout.addView(mSurface);
Expand Down Expand Up @@ -498,6 +501,8 @@ public static native int nativeAddJoystick(int device_id, String name,
int is_accelerometer, int nbuttons,
int naxes, int nhats, int nballs);
public static native int nativeRemoveJoystick(int device_id);
public static native int nativeAddHaptic(int device_id, String name);
public static native int nativeRemoveHaptic(int device_id);
public static native String nativeGetHint(String name);

/**
Expand Down Expand Up @@ -1704,6 +1709,18 @@ protected SDLJoystick getJoystick(int device_id) {
return null;
}

public static void pollHapticDevices() {
if (SDLActivity.mSDLThread != null) {
mHapticHandler.pollHapticDevices();
}
}

public static void hapticRun(int device_id, int length) {
if (SDLActivity.mSDLThread != null) {
mHapticHandler.run(device_id, length);
}
}

@Override
public boolean handleMotionEvent(MotionEvent event) {
if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) {
Expand Down Expand Up @@ -1779,3 +1796,82 @@ public boolean onGenericMotion(View v, MotionEvent event) {
return false;
}
}

class SDLHapticHandler {

class SDLHaptic {
public int device_id;
public String name;
public Vibrator vib;
}

private ArrayList<SDLHaptic> mHaptics;

public SDLHapticHandler() {
mHaptics = new ArrayList<SDLHaptic>();
}

public void run(int device_id, int length) {
SDLHaptic haptic = getHaptic(device_id);
if (haptic != null) {
haptic.vib.vibrate (length);
}
}

public void pollHapticDevices() {
int[] deviceIds = InputDevice.getDeviceIds();
// It helps processing the device ids in reverse order
// For example, in the case of the XBox 360 wireless dongle,
// so the first controller seen by SDL matches what the receiver
// considers to be the first controller

for(int i=deviceIds.length-1; i>-1; i--) {
SDLHaptic haptic = getHaptic(deviceIds[i]);
if (haptic == null) {
InputDevice device = InputDevice.getDevice(deviceIds[i]);
Vibrator vib = device.getVibrator ();
if(vib.hasVibrator ()) {
haptic = new SDLHaptic();
haptic.device_id = deviceIds[i];
haptic.name = device.getName();
haptic.vib = vib;
mHaptics.add(haptic);
SDLActivity.nativeAddHaptic(haptic.device_id, haptic.name);
}
}
}

/* Check removed devices */
ArrayList<Integer> removedDevices = new ArrayList<Integer>();
for(int i=0; i < mHaptics.size(); i++) {
int device_id = mHaptics.get(i).device_id;
int j;
for (j=0; j < deviceIds.length; j++) {
if (device_id == deviceIds[j]) break;
}
if (j == deviceIds.length) {
removedDevices.add(device_id);
}
}

for(int i=0; i < removedDevices.size(); i++) {
int device_id = removedDevices.get(i);
SDLActivity.nativeRemoveHaptic(device_id);
for (int j=0; j < mHaptics.size(); j++) {
if (mHaptics.get(j).device_id == device_id) {
mHaptics.remove(j);
break;
}
}
}
}

protected SDLHaptic getHaptic(int device_id) {
for(int i=0; i < mHaptics.size(); i++) {
if (mHaptics.get(i).device_id == device_id) {
return mHaptics.get(i);
}
}
return null;
}
}
2 changes: 1 addition & 1 deletion include/SDL_config_android.h
Expand Up @@ -118,7 +118,7 @@

/* Enable various input drivers */
#define SDL_JOYSTICK_ANDROID 1
#define SDL_HAPTIC_DUMMY 1
#define SDL_HAPTIC_ANDROID 1

/* Enable various shared object loading systems */
#define SDL_LOADSO_DLOPEN 1
Expand Down
41 changes: 40 additions & 1 deletion src/core/android/SDL_android.c
Expand Up @@ -37,6 +37,7 @@
#include "../../video/android/SDL_androidvideo.h"
#include "../../video/android/SDL_androidwindow.h"
#include "../../joystick/android/SDL_sysjoystick_c.h"
#include "../../haptic/android/SDL_syshaptic_c.h"

#include <android/log.h>
#include <pthread.h>
Expand Down Expand Up @@ -177,6 +178,8 @@ static jmethodID midCaptureReadShortBuffer;
static jmethodID midCaptureReadByteBuffer;
static jmethodID midCaptureClose;
static jmethodID midPollInputDevices;
static jmethodID midPollHapticDevices;
static jmethodID midHapticRun;

/* Accelerometer data storage */
static float fLastAccelerometer[3];
Expand Down Expand Up @@ -237,13 +240,17 @@ JNIEXPORT void JNICALL SDL_Android_Init(JNIEnv* mEnv, jclass cls)
"captureClose", "()V");
midPollInputDevices = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
"pollInputDevices", "()V");
midPollHapticDevices = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
"pollHapticDevices", "()V");
midHapticRun = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
"hapticRun", "(II)V");

bHasNewData = SDL_FALSE;

if (!midGetNativeSurface ||
!midAudioOpen || !midAudioWriteShortBuffer || !midAudioWriteByteBuffer || !midAudioClose ||
!midCaptureOpen || !midCaptureReadShortBuffer || !midCaptureReadByteBuffer || !midCaptureClose ||
!midPollInputDevices) {
!midPollInputDevices || !midPollHapticDevices || !midHapticRun) {
__android_log_print(ANDROID_LOG_WARN, "SDL", "SDL: Couldn't locate Java callbacks, check that they're named and typed correctly");
}
__android_log_print(ANDROID_LOG_INFO, "SDL", "SDL_Android_Init() finished!");
Expand Down Expand Up @@ -323,6 +330,25 @@ JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(nativeRemoveJoystick)(
return Android_RemoveJoystick(device_id);
}

JNIEXPORT jint JNICALL Java_org_libsdl_app_SDLActivity_nativeAddHaptic(
JNIEnv* env, jclass jcls, jint device_id, jstring device_name)
{
int retval;
const char *name = (*env)->GetStringUTFChars(env, device_name, NULL);

retval = Android_AddHaptic(device_id, name);

(*env)->ReleaseStringUTFChars(env, device_name, name);

return retval;
}

JNIEXPORT jint JNICALL Java_org_libsdl_app_SDLActivity_nativeRemoveHaptic(
JNIEnv* env, jclass jcls, jint device_id)
{
return Android_RemoveHaptic(device_id);
}


/* Surface Created */
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)(JNIEnv* env, jclass jcls)
Expand Down Expand Up @@ -1570,6 +1596,19 @@ void Android_JNI_PollInputDevices(void)
(*env)->CallStaticVoidMethod(env, mActivityClass, midPollInputDevices);
}

void Android_JNI_PollHapticDevices(void)
{
JNIEnv *env = Android_JNI_GetEnv();
(*env)->CallStaticVoidMethod(env, mActivityClass, midPollHapticDevices);
}

void Android_JNI_HapticRun(int device_id, int length)
{
JNIEnv *env = Android_JNI_GetEnv();
(*env)->CallStaticVoidMethod(env, mActivityClass, midHapticRun, device_id, length);
}


/* See SDLActivity.java for constants. */
#define COMMAND_SET_KEEP_SCREEN_ON 5

Expand Down
4 changes: 4 additions & 0 deletions src/core/android/SDL_android.h
Expand Up @@ -67,6 +67,10 @@ int Android_JNI_GetPowerInfo(int* plugged, int* charged, int* battery, int* seco
/* Joystick support */
void Android_JNI_PollInputDevices(void);

/* Haptic support */
void Android_JNI_PollHapticDevices(void);
void Android_JNI_HapticRun(int device_id, int length);

/* Video */
void Android_JNI_SuspendScreenSaver(SDL_bool suspend);

Expand Down
1 change: 1 addition & 0 deletions src/haptic/SDL_haptic.c
Expand Up @@ -313,6 +313,7 @@ SDL_HapticOpenFromJoystick(SDL_Joystick * joystick)
SDL_memset(haptic, 0, sizeof(SDL_Haptic));
haptic->rumble_id = -1;
if (SDL_SYS_HapticOpenFromJoystick(haptic, joystick) < 0) {
SDL_SetError("Haptic: SDL_SYS_HapticOpenFromJoystick failed.");
SDL_free(haptic);
return NULL;
}
Expand Down

0 comments on commit 0a52db5

Please sign in to comment.