Fixed bug 3191 - haptic system on android?

Patch provided by jintiao and Milan Nikolic, thanks!
Sam Lantinga 2017-08-12 08:15:09 -07:00
parent 78c84e7029
commit 0a52db54bd
10 changed files with 521 additions and 3 deletions

View File

@ -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) \

View File

@ -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" /> -->

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,358 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
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: */

View File

@ -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: */

View File

@ -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: */