diff --git a/Android.mk b/Android.mk
index c1a4956f9..0c203c5ba 100755
--- a/Android.mk
+++ b/Android.mk
@@ -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) \
diff --git a/android-project/AndroidManifest.xml b/android-project/AndroidManifest.xml
index d3897f241..3845127e9 100644
--- a/android-project/AndroidManifest.xml
+++ b/android-project/AndroidManifest.xml
@@ -16,6 +16,8 @@
+
+
diff --git a/android-project/src/org/libsdl/app/SDLActivity.java b/android-project/src/org/libsdl/app/SDLActivity.java
index abee46dc8..10c34543e 100644
--- a/android-project/src/org/libsdl/app/SDLActivity.java
+++ b/android-project/src/org/libsdl/app/SDLActivity.java
@@ -61,6 +61,7 @@ public class SDLActivity extends Activity {
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;
@@ -113,6 +114,7 @@ public class SDLActivity extends Activity {
mTextEdit = null;
mLayout = null;
mJoystickHandler = null;
+ mHapticHandler = null;
mSDLThread = null;
mAudioTrack = null;
mAudioRecord = null;
@@ -182,6 +184,7 @@ public class SDLActivity extends Activity {
else {
mJoystickHandler = new SDLJoystickHandler();
}
+ mHapticHandler = new SDLHapticHandler();
mLayout = new RelativeLayout(this);
mLayout.addView(mSurface);
@@ -498,6 +501,8 @@ public class SDLActivity extends Activity {
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);
/**
@@ -1704,6 +1709,18 @@ class SDLJoystickHandler_API12 extends SDLJoystickHandler {
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) {
@@ -1779,3 +1796,82 @@ class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener {
return false;
}
}
+
+class SDLHapticHandler {
+
+ class SDLHaptic {
+ public int device_id;
+ public String name;
+ public Vibrator vib;
+ }
+
+ private ArrayList mHaptics;
+
+ public SDLHapticHandler() {
+ mHaptics = new ArrayList();
+ }
+
+ 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 removedDevices = new ArrayList();
+ 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;
+ }
+}
diff --git a/include/SDL_config_android.h b/include/SDL_config_android.h
index 32fa96cb8..0bdff66c6 100644
--- a/include/SDL_config_android.h
+++ b/include/SDL_config_android.h
@@ -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
diff --git a/src/core/android/SDL_android.c b/src/core/android/SDL_android.c
index 227b48c7f..dbff4073a 100644
--- a/src/core/android/SDL_android.c
+++ b/src/core/android/SDL_android.c
@@ -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
#include
@@ -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];
@@ -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!");
@@ -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)
@@ -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
diff --git a/src/core/android/SDL_android.h b/src/core/android/SDL_android.h
index 1e4ebf3dc..799b29a0d 100644
--- a/src/core/android/SDL_android.h
+++ b/src/core/android/SDL_android.h
@@ -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);
diff --git a/src/haptic/SDL_haptic.c b/src/haptic/SDL_haptic.c
index 318a7fb73..d66a2df48 100644
--- a/src/haptic/SDL_haptic.c
+++ b/src/haptic/SDL_haptic.c
@@ -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;
}
diff --git a/src/haptic/android/SDL_syshaptic.c b/src/haptic/android/SDL_syshaptic.c
new file mode 100644
index 000000000..ce2226f32
--- /dev/null
+++ b/src/haptic/android/SDL_syshaptic.c
@@ -0,0 +1,358 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2014 Sam Lantinga
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#ifdef SDL_HAPTIC_ANDROID
+
+#include "SDL_assert.h"
+#include "SDL_timer.h"
+#include "SDL_syshaptic_c.h"
+#include "../SDL_syshaptic.h"
+#include "SDL_haptic.h"
+#include "../../core/android/SDL_android.h"
+#include "SDL_joystick.h"
+#include "../../joystick/SDL_sysjoystick.h" /* For the real SDL_Joystick */
+#include "../../joystick/android/SDL_sysjoystick_c.h" /* For joystick hwdata */
+
+
+typedef struct SDL_hapticlist_item
+{
+ int device_id;
+ char *name;
+ SDL_Haptic *haptic;
+ struct SDL_hapticlist_item *next;
+} SDL_hapticlist_item;
+
+static SDL_hapticlist_item *SDL_hapticlist = NULL;
+static SDL_hapticlist_item *SDL_hapticlist_tail = NULL;
+static int numhaptics = 0;
+
+
+int
+SDL_SYS_HapticInit(void)
+{
+ /* Support for device connect/disconnect is API >= 16 only,
+ * so we poll every three seconds
+ * Ref: http://developer.android.com/reference/android/hardware/input/InputManager.InputDeviceListener.html
+ */
+ static Uint32 timeout = 0;
+ if (SDL_TICKS_PASSED(SDL_GetTicks(), timeout)) {
+ timeout = SDL_GetTicks() + 3000;
+ Android_JNI_PollHapticDevices();
+ }
+ return (numhaptics);
+}
+
+int
+SDL_SYS_NumHaptics(void)
+{
+ return (numhaptics);
+}
+
+static SDL_hapticlist_item *
+HapticByOrder(int index)
+{
+ SDL_hapticlist_item *item = SDL_hapticlist;
+ if ((index < 0) || (index >= numhaptics)) {
+ return NULL;
+ }
+ while (index > 0) {
+ SDL_assert(item != NULL);
+ --index;
+ item = item->next;
+ }
+ return item;
+}
+
+static SDL_hapticlist_item *
+HapticByDevId (int device_id)
+{
+ SDL_hapticlist_item *item;
+ for (item = SDL_hapticlist; item != NULL; item = item->next) {
+ if (device_id == item->device_id) {
+ SDL_Log("=+=+=+=+=+= HapticByDevId id [%d]", device_id);
+ return item;
+ }
+ }
+ return NULL;
+}
+
+const char *
+SDL_SYS_HapticName(int index)
+{
+ SDL_hapticlist_item *item = HapticByOrder(index);
+ if (item == NULL ) {
+ SDL_SetError("No such device");
+ return NULL;
+ }
+ return item->name;
+}
+
+
+static SDL_hapticlist_item *
+OpenHaptic(SDL_Haptic *haptic, SDL_hapticlist_item *item)
+{
+ if (item == NULL ) {
+ SDL_SetError("No such device");
+ return NULL;
+ }
+ if (item->haptic != NULL) {
+ SDL_SetError("Haptic already opened");
+ return NULL;
+ }
+
+ haptic->hwdata = (struct haptic_hwdata *)item;
+ item->haptic = haptic;
+
+ haptic->supported = SDL_HAPTIC_LEFTRIGHT;
+ haptic->neffects = 1;
+ haptic->nplaying = haptic->neffects;
+ haptic->effects = (struct haptic_effect *)SDL_malloc (sizeof (struct haptic_effect) * haptic->neffects);
+ if (haptic->effects == NULL) {
+ SDL_OutOfMemory();
+ return NULL;
+ }
+ SDL_memset(haptic->effects, 0, sizeof (struct haptic_effect) * haptic->neffects);
+ return item;
+}
+
+static SDL_hapticlist_item *
+OpenHapticByOrder(SDL_Haptic *haptic, int index)
+{
+ return OpenHaptic (haptic, HapticByOrder(index));
+}
+
+static SDL_hapticlist_item *
+OpenHapticByDevId(SDL_Haptic *haptic, int device_id)
+{
+ return OpenHaptic (haptic, HapticByDevId(device_id));
+}
+
+int
+SDL_SYS_HapticOpen(SDL_Haptic *haptic)
+{
+ return (OpenHapticByOrder(haptic, haptic->index) == NULL ? -1 : 0);
+}
+
+
+int
+SDL_SYS_HapticMouse(void)
+{
+ return 0;
+}
+
+
+int
+SDL_SYS_JoystickIsHaptic(SDL_Joystick *joystick)
+{
+ SDL_hapticlist_item *item;
+ item = HapticByDevId(((joystick_hwdata *)joystick->hwdata)->device_id);
+ int ret = (item != NULL ? 1 : 0);
+ return ret;
+}
+
+
+int
+SDL_SYS_HapticOpenFromJoystick(SDL_Haptic *haptic, SDL_Joystick *joystick)
+{
+ return (OpenHapticByDevId(haptic, ((joystick_hwdata *)joystick->hwdata)->device_id) == NULL ? -1 : 0);
+}
+
+
+int
+SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
+{
+ return (((SDL_hapticlist_item *)haptic->hwdata)->device_id == ((joystick_hwdata *)joystick->hwdata)->device_id ? 1 : 0);
+}
+
+
+void
+SDL_SYS_HapticClose(SDL_Haptic * haptic)
+{
+ ((SDL_hapticlist_item *)haptic->hwdata)->haptic = NULL;
+ haptic->hwdata = NULL;
+ return;
+}
+
+
+void
+SDL_SYS_HapticQuit(void)
+{
+ SDL_hapticlist_item *item = NULL;
+ SDL_hapticlist_item *next = NULL;
+
+ for (item = SDL_hapticlist; item; item = next) {
+ next = item->next;
+ SDL_free(item);
+ }
+
+ SDL_hapticlist = SDL_hapticlist_tail = NULL;
+ numhaptics = 0;
+ return;
+}
+
+
+int
+SDL_SYS_HapticNewEffect(SDL_Haptic * haptic,
+ struct haptic_effect *effect, SDL_HapticEffect * base)
+{
+ return 0;
+}
+
+
+int
+SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic,
+ struct haptic_effect *effect,
+ SDL_HapticEffect * data)
+{
+ return 0;
+}
+
+
+int
+SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
+ Uint32 iterations)
+{
+ Android_JNI_HapticRun (((SDL_hapticlist_item *)haptic->hwdata)->device_id, effect->effect.leftright.length);
+ return 0;
+}
+
+
+int
+SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
+{
+ return 0;
+}
+
+
+void
+SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
+{
+ return;
+}
+
+
+int
+SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic,
+ struct haptic_effect *effect)
+{
+ return 0;
+}
+
+
+int
+SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain)
+{
+ return 0;
+}
+
+
+int
+SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
+{
+ return 0;
+}
+
+int
+SDL_SYS_HapticPause(SDL_Haptic * haptic)
+{
+ return 0;
+}
+
+int
+SDL_SYS_HapticUnpause(SDL_Haptic * haptic)
+{
+ return 0;
+}
+
+int
+SDL_SYS_HapticStopAll(SDL_Haptic * haptic)
+{
+ return 0;
+}
+
+
+
+int
+Android_AddHaptic(int device_id, const char *name)
+{
+ SDL_hapticlist_item *item;
+ item = (SDL_hapticlist_item *) SDL_calloc(1, sizeof (SDL_hapticlist_item));
+ if (item == NULL) {
+ return -1;
+ }
+
+ item->device_id = device_id;
+ item->name = SDL_strdup (name);
+ if (item->name == NULL) {
+ SDL_free (item);
+ return -1;
+ }
+
+ if (SDL_hapticlist_tail == NULL) {
+ SDL_hapticlist = SDL_hapticlist_tail = item;
+ } else {
+ SDL_hapticlist_tail->next = item;
+ SDL_hapticlist_tail = item;
+ }
+
+ ++numhaptics;
+ return numhaptics;
+}
+
+int
+Android_RemoveHaptic(int device_id)
+{
+ SDL_hapticlist_item *item;
+ SDL_hapticlist_item *prev = NULL;
+
+ for (item = SDL_hapticlist; item != NULL; item = item->next) {
+ /* found it, remove it. */
+ if (device_id == item->device_id) {
+ const int retval = item->haptic ? item->haptic->index : -1;
+
+ if (prev != NULL) {
+ prev->next = item->next;
+ } else {
+ SDL_assert(SDL_hapticlist == item);
+ SDL_hapticlist = item->next;
+ }
+ if (item == SDL_hapticlist_tail) {
+ SDL_hapticlist_tail = prev;
+ }
+
+ /* Need to decrement the haptic count */
+ --numhaptics;
+ /* !!! TODO: Send a haptic remove event? */
+
+ SDL_free(item->name);
+ SDL_free(item);
+ return retval;
+ }
+ prev = item;
+ }
+ return -1;
+}
+
+
+#endif /* SDL_HAPTIC_ANDROID */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/haptic/android/SDL_syshaptic_c.h b/src/haptic/android/SDL_syshaptic_c.h
new file mode 100644
index 000000000..08634d223
--- /dev/null
+++ b/src/haptic/android/SDL_syshaptic_c.h
@@ -0,0 +1,12 @@
+#include "SDL_config.h"
+
+#ifdef SDL_HAPTIC_ANDROID
+
+
+extern int Android_AddHaptic(int device_id, const char *name);
+extern int Android_RemoveHaptic(int device_id);
+
+
+#endif /* SDL_HAPTIC_ANDROID */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/joystick/android/SDL_sysjoystick_c.h b/src/joystick/android/SDL_sysjoystick_c.h
index 6387ceb3a..140eda1fc 100644
--- a/src/joystick/android/SDL_sysjoystick_c.h
+++ b/src/joystick/android/SDL_sysjoystick_c.h
@@ -22,6 +22,10 @@
#include "../../SDL_internal.h"
#ifdef SDL_JOYSTICK_ANDROID
+
+#ifndef _SDL_sysjoystick_c_h
+#define _SDL_sysjoystick_c_h
+
#include "../SDL_sysjoystick.h"
extern int Android_OnPadDown(int device_id, int keycode);
@@ -47,6 +51,8 @@ typedef struct SDL_joylist_item
typedef SDL_joylist_item joystick_hwdata;
+#endif /* _SDL_sysjoystick_c_h */
+
#endif /* SDL_JOYSTICK_ANDROID */
/* vi: set ts=4 sw=4 expandtab: */