From bfcd28c1e6291edf36371d25b5a159b8ed506fae Mon Sep 17 00:00:00 2001 From: Gabriel Jacobo Date: Tue, 10 Dec 2013 16:24:11 -0300 Subject: [PATCH] [Android] Hotplugging support for joysticks --- WhatsNew.txt | 1 + .../src/org/libsdl/app/SDLActivity.java | 173 ++++--- src/core/android/SDL_android.c | 97 ++-- src/core/android/SDL_android.h | 5 +- src/joystick/android/SDL_sysjoystick.c | 423 ++++++++++++------ ...{SDL_sysjoystick.h => SDL_sysjoystick_c.h} | 29 +- 6 files changed, 434 insertions(+), 294 deletions(-) rename src/joystick/android/{SDL_sysjoystick.h => SDL_sysjoystick_c.h} (52%) diff --git a/WhatsNew.txt b/WhatsNew.txt index 79873831794b2..e7cec5b609518 100644 --- a/WhatsNew.txt +++ b/WhatsNew.txt @@ -16,6 +16,7 @@ Android: * Joystick support (minimum SDK version required to build SDL is now 12, the required runtime version remains at 10, but on such devices joystick support won't be available). +* Hotplugging support for joysticks Linux: * Fixed fullscreen and focused behavior when receiving NotifyGrab events diff --git a/android-project/src/org/libsdl/app/SDLActivity.java b/android-project/src/org/libsdl/app/SDLActivity.java index 045130efb35dd..914e19ca8396f 100644 --- a/android-project/src/org/libsdl/app/SDLActivity.java +++ b/android-project/src/org/libsdl/app/SDLActivity.java @@ -256,9 +256,9 @@ boolean sendCommand(int command, Object data) { public static native void nativePause(); public static native void nativeResume(); public static native void onNativeResize(int x, int y, int format); - public static native int onNativePadDown(int padId, int keycode); - public static native int onNativePadUp(int padId, int keycode); - public static native void onNativeJoy(int joyId, int axis, + public static native int onNativePadDown(int device_id, int keycode); + public static native int onNativePadUp(int device_id, int keycode); + public static native void onNativeJoy(int device_id, int axis, float value); public static native void onNativeKeyDown(int keycode); public static native void onNativeKeyUp(int keycode); @@ -270,6 +270,10 @@ public static native void onNativeTouch(int touchDevId, int pointerFingerId, public static native void onNativeSurfaceChanged(); public static native void onNativeSurfaceDestroyed(); public static native void nativeFlipBuffers(); + 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 void flipBuffers() { SDLActivity.nativeFlipBuffers(); @@ -460,29 +464,16 @@ public static int[] inputGetInputDeviceIds(int sources) { } // Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance - public static int getNumJoysticks() { - return mJoystickHandler.getNumJoysticks(); - } - - public static String getJoystickName(int joy) { - return mJoystickHandler.getJoystickName(joy); - } - - public static int getJoystickAxes(int joy) { - return mJoystickHandler.getJoystickAxes(joy); - } - public static boolean handleJoystickMotionEvent(MotionEvent event) { return mJoystickHandler.handleMotionEvent(event); } - /** - * @param devId the device id to get opened joystick id for. - * @return joystick id for device id or -1 if there is none. - */ - public static int getJoyId(int devId) { - return mJoystickHandler.getJoyId(devId); + public static void pollInputDevices() { + if (SDLActivity.mSDLThread != null) { + mJoystickHandler.pollInputDevices(); + } } + } /** @@ -660,16 +651,13 @@ public boolean onKey(View v, int keyCode, KeyEvent event) { if ( (event.getSource() & 0x00000401) != 0 || /* API 12: SOURCE_GAMEPAD */ (event.getSource() & InputDevice.SOURCE_DPAD) != 0 ) { - int id = SDLActivity.getJoyId( event.getDeviceId() ); - if (id != -1) { - if (event.getAction() == KeyEvent.ACTION_DOWN) { - if (SDLActivity.onNativePadDown(id, keyCode) == 0) { - return true; - } - } else if (event.getAction() == KeyEvent.ACTION_UP) { - if (SDLActivity.onNativePadUp(id, keyCode) == 0) { - return true; - } + if (event.getAction() == KeyEvent.ACTION_DOWN) { + if (SDLActivity.onNativePadDown(event.getDeviceId(), keyCode) == 0) { + return true; + } + } else if (event.getAction() == KeyEvent.ACTION_UP) { + if (SDLActivity.onNativePadUp(event.getDeviceId(), keyCode) == 0) { + return true; } } } @@ -916,36 +904,20 @@ public boolean deleteSurroundingText(int beforeLength, int afterLength) { /* A null joystick handler for API level < 12 devices (the accelerometer is handled separately) */ class SDLJoystickHandler { - public int getNumJoysticks() { - return 0; - } - - public String getJoystickName(int joy) { - return ""; - } - - public int getJoystickAxes(int joy) { - return 0; - } - - /** - * @param devId the device id to get opened joystick id for. - * @return joystick id for device id or -1 if there is none. - */ - public int getJoyId(int devId) { - return -1; - } public boolean handleMotionEvent(MotionEvent event) { return false; } + + public void pollInputDevices() { + } } /* Actual joystick functionality available for API >= 12 devices */ class SDLJoystickHandler_API12 extends SDLJoystickHandler { class SDLJoystick { - public int id; + public int device_id; public String name; public ArrayList axes; } @@ -953,54 +925,72 @@ class SDLJoystick { private ArrayList mJoysticks; public SDLJoystickHandler_API12() { - /* FIXME: Move the joystick initialization code to its own function and support hotplugging of devices */ mJoysticks = new ArrayList(); - + } + + @Override + public void pollInputDevices() { int[] deviceIds = InputDevice.getDeviceIds(); - for(int i=0; i(); - - for (InputDevice.MotionRange range : joystickDevice.getMotionRanges()) { - if ( (range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) { - joystick.axes.add(range); - } + // 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--) { + SDLJoystick joystick = getJoystick(deviceIds[i]); + if (joystick == null) { + joystick = new SDLJoystick(); + InputDevice joystickDevice = InputDevice.getDevice(deviceIds[i]); + if( (joystickDevice.getSources() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) { + joystick.device_id = deviceIds[i]; + joystick.name = joystickDevice.getName(); + joystick.axes = new ArrayList(); + + for (InputDevice.MotionRange range : joystickDevice.getMotionRanges()) { + if ( (range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) { + joystick.axes.add(range); + } + } + + mJoysticks.add(joystick); + SDLActivity.nativeAddJoystick(joystick.device_id, joystick.name, 0, -1, joystick.axes.size(), 0, 0); } - - mJoysticks.add(joystick); } } + + /* Check removed devices */ + ArrayList removedDevices = new ArrayList(); + for(int i=0; i < mJoysticks.size(); i++) { + int device_id = mJoysticks.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.nativeRemoveJoystick(device_id); + for (int j=0; j < mJoysticks.size(); j++) { + if (mJoysticks.get(j).device_id == device_id) { + mJoysticks.remove(j); + break; + } + } + } } - @Override - public int getNumJoysticks() { - return mJoysticks.size(); - } - - @Override - public String getJoystickName(int joy) { - return mJoysticks.get(joy).name; - } - - @Override - public int getJoystickAxes(int joy) { - return mJoysticks.get(joy).axes.size(); - } - - @Override - public int getJoyId(int devId) { + protected SDLJoystick getJoystick(int device_id) { for(int i=0; i < mJoysticks.size(); i++) { - if (mJoysticks.get(i).id == devId) { - return i; + if (mJoysticks.get(i).device_id == device_id) { + return mJoysticks.get(i); } } - return -1; + return null; } @Override @@ -1010,14 +1000,13 @@ public boolean handleMotionEvent(MotionEvent event) { int action = event.getActionMasked(); switch(action) { case MotionEvent.ACTION_MOVE: - int id = getJoyId( event.getDeviceId() ); - if ( id != -1 ) { - SDLJoystick joystick = mJoysticks.get(id); + SDLJoystick joystick = getJoystick(event.getDeviceId()); + if ( joystick != null ) { for (int i = 0; i < joystick.axes.size(); i++) { InputDevice.MotionRange range = joystick.axes.get(i); /* Normalize the value to -1...1 */ float value = ( event.getAxisValue( range.getAxis(), actionPointerIndex) - range.getMin() ) / range.getRange() * 2.0f - 1.0f; - SDLActivity.onNativeJoy(id, i, value ); + SDLActivity.onNativeJoy(joystick.device_id, i, value ); } } break; diff --git a/src/core/android/SDL_android.c b/src/core/android/SDL_android.c index a18cb4f7dd9cf..0482efd08a49c 100644 --- a/src/core/android/SDL_android.c +++ b/src/core/android/SDL_android.c @@ -34,7 +34,7 @@ #include "../../video/android/SDL_androidtouch.h" #include "../../video/android/SDL_androidvideo.h" #include "../../video/android/SDL_androidwindow.h" -#include "../../joystick/android/SDL_sysjoystick.h" +#include "../../joystick/android/SDL_sysjoystick_c.h" #include #include @@ -75,6 +75,7 @@ static jmethodID midAudioInit; static jmethodID midAudioWriteShortBuffer; static jmethodID midAudioWriteByteBuffer; static jmethodID midAudioQuit; +static jmethodID midPollInputDevices; /* Accelerometer data storage */ static float fLastAccelerometer[3]; @@ -127,11 +128,13 @@ void SDL_Android_Init(JNIEnv* mEnv, jclass cls) "audioWriteByteBuffer", "([B)V"); midAudioQuit = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "audioQuit", "()V"); + midPollInputDevices = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, + "pollInputDevices", "()V"); bHasNewData = false; if(!midGetNativeSurface || !midFlipBuffers || !midAudioInit || - !midAudioWriteShortBuffer || !midAudioWriteByteBuffer || !midAudioQuit) { + !midAudioWriteShortBuffer || !midAudioWriteByteBuffer || !midAudioQuit || !midPollInputDevices) { __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!"); @@ -148,25 +151,47 @@ void Java_org_libsdl_app_SDLActivity_onNativeResize( // Paddown int Java_org_libsdl_app_SDLActivity_onNativePadDown( JNIEnv* env, jclass jcls, - jint padId, jint keycode) + jint device_id, jint keycode) { - return Android_OnPadDown(padId, keycode); + return Android_OnPadDown(device_id, keycode); } // Padup int Java_org_libsdl_app_SDLActivity_onNativePadUp( JNIEnv* env, jclass jcls, - jint padId, jint keycode) + jint device_id, jint keycode) { - return Android_OnPadUp(padId, keycode); + return Android_OnPadUp(device_id, keycode); } /* Joy */ void Java_org_libsdl_app_SDLActivity_onNativeJoy( JNIEnv* env, jclass jcls, - jint joyId, jint axis, jfloat value) + jint device_id, jint axis, jfloat value) { - Android_OnJoy(joyId, axis, value); + Android_OnJoy(device_id, axis, value); +} + + +int Java_org_libsdl_app_SDLActivity_nativeAddJoystick( + JNIEnv* env, jclass jcls, + jint device_id, jstring device_name, jint is_accelerometer, + jint nbuttons, jint naxes, jint nhats, jint nballs) +{ + int retval; + const char *name = (*env)->GetStringUTFChars(env, device_name, NULL); + + retval = Android_AddJoystick(device_id, name, (SDL_bool) is_accelerometer, nbuttons, naxes, nhats, nballs); + + (*env)->ReleaseStringUTFChars(env, device_name, name); + + return retval; +} + +int Java_org_libsdl_app_SDLActivity_nativeRemoveJoystick( + JNIEnv* env, jclass jcls, jint device_id) +{ + return Android_RemoveJoystick(device_id); } @@ -1247,62 +1272,12 @@ int Android_JNI_GetTouchDeviceIds(int **ids) { return number; } -/* return the total number of plugged in joysticks */ -int Android_JNI_GetNumJoysticks() -{ - JNIEnv* env = Android_JNI_GetEnv(); - if (!env) { - return -1; - } - - jmethodID mid = (*env)->GetStaticMethodID(env, mActivityClass, "getNumJoysticks", "()I"); - if (!mid) { - return -1; - } - - return (int)(*env)->CallStaticIntMethod(env, mActivityClass, mid); -} - -/* Return the name of joystick number "i" */ -char* Android_JNI_GetJoystickName(int i) -{ - JNIEnv* env = Android_JNI_GetEnv(); - if (!env) { - return SDL_strdup(""); - } - - jmethodID mid = (*env)->GetStaticMethodID(env, mActivityClass, "getJoystickName", "(I)Ljava/lang/String;"); - if (!mid) { - return SDL_strdup(""); - } - jstring string = (jstring)((*env)->CallStaticObjectMethod(env, mActivityClass, mid, i)); - const char* utf = (*env)->GetStringUTFChars(env, string, 0); - if (!utf) { - return SDL_strdup(""); - } - - char* text = SDL_strdup(utf); - (*env)->ReleaseStringUTFChars(env, string, utf); - return text; -} - -/* return the number of axes in the given joystick */ -int Android_JNI_GetJoystickAxes(int joy) +void Android_JNI_PollInputDevices() { - JNIEnv* env = Android_JNI_GetEnv(); - if (!env) { - return -1; - } - - jmethodID mid = (*env)->GetStaticMethodID(env, mActivityClass, "getJoystickAxes", "(I)I"); - if (!mid) { - return -1; - } - - return (int)(*env)->CallIntMethod(env, mActivityClass, mid, joy); + JNIEnv *env = Android_JNI_GetEnv(); + (*env)->CallStaticVoidMethod(env, mActivityClass, midPollInputDevices); } - /* sends message to be handled on the UI event dispatch thread */ int Android_JNI_SendMessage(int command, int param) { diff --git a/src/core/android/SDL_android.h b/src/core/android/SDL_android.h index 805518a8f61c4..2219069b955dc 100644 --- a/src/core/android/SDL_android.h +++ b/src/core/android/SDL_android.h @@ -66,9 +66,8 @@ SDL_bool Android_JNI_HasClipboardText(); int Android_JNI_GetPowerInfo(int* plugged, int* charged, int* battery, int* seconds, int* percent); /* Joystick support */ -int Android_JNI_GetNumJoysticks(); -char* Android_JNI_GetJoystickName(int i); -int Android_JNI_GetJoystickAxes(int joy); +void Android_JNI_PollInputDevices(); + /* Touch support */ int Android_JNI_GetTouchDeviceIds(int **ids); diff --git a/src/joystick/android/SDL_sysjoystick.c b/src/joystick/android/SDL_sysjoystick.c index 55db0158342ad..7e3c595066ee0 100644 --- a/src/joystick/android/SDL_sysjoystick.c +++ b/src/joystick/android/SDL_sysjoystick.c @@ -23,15 +23,18 @@ #ifdef SDL_JOYSTICK_ANDROID -/* This is the system specific header for the SDL joystick API */ #include /* For the definition of NULL */ - #include "SDL_error.h" #include "SDL_events.h" + +#if !SDL_EVENTS_DISABLED +#include "../../events/SDL_events_c.h" +#endif + #include "SDL_joystick.h" #include "SDL_hints.h" #include "SDL_assert.h" -#include "../SDL_sysjoystick.h" +#include "SDL_sysjoystick_c.h" #include "../SDL_joystick_c.h" #include "../../core/android/SDL_android.h" @@ -57,14 +60,17 @@ #define AKEYCODE_BUTTON_16 203 #endif -#define ANDROID_ACCELEROMETER_INDEX (SYS_numjoysticks - 1) #define ANDROID_ACCELEROMETER_NAME "Android Accelerometer" +#define ANDROID_ACCELEROMETER_DEVICE_ID INT_MIN #define ANDROID_MAX_NBUTTONS 36 -static SDL_Joystick **SYS_Joysticks; -static char **SYS_JoystickNames; -static int SYS_numjoysticks; -static SDL_bool SYS_accelAsJoy; +static SDL_joylist_item * JoystickByDeviceId(int device_id); + +static SDL_joylist_item *SDL_joylist = NULL; +static SDL_joylist_item *SDL_joylist_tail = NULL; +static int numjoysticks = 0; +static int instance_counter = 0; + /* Function to convert Android keyCodes into SDL ones. * This code manipulation is done to get a sequential list of codes. @@ -139,78 +145,265 @@ keycode_to_SDL(int keycode) } -/* Function to scan the system for joysticks. - * This function should set SDL_numjoysticks to the number of available - * joysticks. Joystick 0 should be the system default joystick. - * It should return 0, or -1 on an unrecoverable fatal error. - */ int -SDL_SYS_JoystickInit(void) +Android_OnPadDown(int device_id, int keycode) { - int i = 0; - const char *env; + SDL_joylist_item *item; + int button = keycode_to_SDL(keycode); + if (button >= 0) { + item = JoystickByDeviceId(device_id); + if (item && item->joystick) { + SDL_PrivateJoystickButton(item->joystick, button , SDL_PRESSED); + } + return 0; + } - env = SDL_GetHint(SDL_HINT_ACCEL_AS_JOY); - if (env && !SDL_atoi(env)) - SYS_accelAsJoy = SDL_FALSE; - else - SYS_accelAsJoy = SDL_TRUE; /* Default behavior */ + return -1; +} + +int +Android_OnPadUp(int device_id, int keycode) +{ + SDL_joylist_item *item; + int button = keycode_to_SDL(keycode); + if (button >= 0) { + item = JoystickByDeviceId(device_id); + if (item && item->joystick) { + SDL_PrivateJoystickButton(item->joystick, button, SDL_RELEASED); + } + return 0; + } - SYS_numjoysticks = Android_JNI_GetNumJoysticks(); - if (SYS_accelAsJoy) { - SYS_numjoysticks++; + return -1; +} + +int +Android_OnJoy(int device_id, int axis, float value) +{ + /* Android gives joy info normalized as [-1.0, 1.0] or [0.0, 1.0] */ + SDL_joylist_item *item = JoystickByDeviceId(device_id); + if (item && item->joystick) { + SDL_PrivateJoystickAxis(item->joystick, axis, (Sint16) (32767.*value) ); } - SYS_Joysticks = (SDL_Joystick **)SDL_calloc(1, SYS_numjoysticks*sizeof(SDL_Joystick *)); - if (SYS_Joysticks == NULL) - { - return SDL_OutOfMemory(); + + return 0; +} + + +int +Android_AddJoystick(int device_id, const char *name, SDL_bool is_accelerometer, int nbuttons, int naxes, int nhats, int nballs) +{ + SDL_JoystickGUID guid; + SDL_joylist_item *item; +#if !SDL_EVENTS_DISABLED + SDL_Event event; +#endif + + if(JoystickByDeviceId(device_id) != NULL || name == NULL) { + return -1; } - SYS_JoystickNames = (char **)SDL_calloc(1, SYS_numjoysticks*sizeof(char *)); - if (SYS_JoystickNames == NULL) - { - SDL_free(SYS_Joysticks); - SYS_Joysticks = NULL; - return SDL_OutOfMemory(); + + /* the GUID is just the first 16 chars of the name for now */ + SDL_zero( guid ); + SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) ); + + item = (SDL_joylist_item *) SDL_malloc(sizeof (SDL_joylist_item)); + if (item == NULL) { + return -1; + } + + SDL_zerop(item); + item->guid = guid; + item->device_id = device_id; + item->name = SDL_strdup(name); + if ( item->name == NULL ) { + SDL_free(item); + return -1; } - for (i = 0; i < SYS_numjoysticks; i++) - { - if ( SYS_accelAsJoy && i == ANDROID_ACCELEROMETER_INDEX ) { - SYS_JoystickNames[i] = ANDROID_ACCELEROMETER_NAME; - } else { - SYS_JoystickNames[i] = Android_JNI_GetJoystickName(i); + item->is_accelerometer = is_accelerometer; + if (nbuttons > -1) { + item->nbuttons = nbuttons; + } + else { + item->nbuttons = ANDROID_MAX_NBUTTONS; + } + item->naxes = naxes; + item->nhats = nhats; + item->nballs = nballs; + item->device_instance = instance_counter++; + if (SDL_joylist_tail == NULL) { + SDL_joylist = SDL_joylist_tail = item; + } else { + SDL_joylist_tail->next = item; + SDL_joylist_tail = item; + } + + /* Need to increment the joystick count before we post the event */ + ++numjoysticks; + +#if !SDL_EVENTS_DISABLED + event.type = SDL_JOYDEVICEADDED; + + if (SDL_GetEventState(event.type) == SDL_ENABLE) { + event.jdevice.which = (numjoysticks - 1); + if ( (SDL_EventOK == NULL) || + (*SDL_EventOK) (SDL_EventOKParam, &event) ) { + SDL_PushEvent(&event); + } + } +#endif /* !SDL_EVENTS_DISABLED */ + + SDL_Log("Added joystick %s with device_id %d", name, device_id); + + return numjoysticks; +} + +int +Android_RemoveJoystick(int device_id) +{ + SDL_joylist_item *item = SDL_joylist; + SDL_joylist_item *prev = NULL; +#if !SDL_EVENTS_DISABLED + SDL_Event event; +#endif + + /* Don't call JoystickByDeviceId here or there'll be an infinite loop! */ + while (item != NULL) { + if (item->device_id == device_id) { + break; } + prev = item; + item = item->next; + } + + if (item == NULL) { + return -1; + } + + const int retval = item->device_instance; + if (prev != NULL) { + prev->next = item->next; + } else { + SDL_assert(SDL_joylist == item); + SDL_joylist = item->next; + } + if (item == SDL_joylist_tail) { + SDL_joylist_tail = prev; + } + + /* Need to decrement the joystick count before we post the event */ + --numjoysticks; + +#if !SDL_EVENTS_DISABLED + event.type = SDL_JOYDEVICEREMOVED; + + if (SDL_GetEventState(event.type) == SDL_ENABLE) { + event.jdevice.which = item->device_instance; + if ( (SDL_EventOK == NULL) || + (*SDL_EventOK) (SDL_EventOKParam, &event) ) { + SDL_PushEvent(&event); + } + } +#endif /* !SDL_EVENTS_DISABLED */ + + SDL_Log("Removed joystick with device_id %d", device_id); + + SDL_free(item->name); + SDL_free(item); + return retval; +} + + +int +SDL_SYS_JoystickInit(void) +{ + const char *env; + SDL_SYS_JoystickDetect(); + + env = SDL_GetHint(SDL_HINT_ACCEL_AS_JOY); + if (!env || SDL_atoi(env)) { + /* Default behavior, accelerometer as joystick */ + Android_AddJoystick(ANDROID_ACCELEROMETER_DEVICE_ID, ANDROID_ACCELEROMETER_NAME, SDL_TRUE, 0, 3, 0, 0); } - return (SYS_numjoysticks); + return (numjoysticks); + } int SDL_SYS_NumJoysticks() { - return SYS_numjoysticks; + return numjoysticks; } void SDL_SYS_JoystickDetect() { + /* Support for device connect/disconnect is API >= 16 only, + * so we have to poll ever few seconds. + * Ref: http://developer.android.com/reference/android/hardware/input/InputManager.InputDeviceListener.html + */ + Android_JNI_PollInputDevices(); } -/* TODO: Hotplugging support */ SDL_bool SDL_SYS_JoystickNeedsPolling() { - return SDL_FALSE; + return SDL_TRUE; +} + +static SDL_joylist_item * +JoystickByDevIndex(int device_index) +{ + SDL_joylist_item *item = SDL_joylist; + + if ((device_index < 0) || (device_index >= numjoysticks)) { + return NULL; + } + + while (device_index > 0) { + SDL_assert(item != NULL); + device_index--; + item = item->next; + } + + return item; +} + +static SDL_joylist_item * +JoystickByDeviceId(int device_id) +{ + SDL_joylist_item *item = SDL_joylist; + + while (item != NULL) { + if (item->device_id == device_id) { + return item; + } + item = item->next; + } + + /* Joystick not found, try adding it */ + SDL_SYS_JoystickDetect(); + + while (item != NULL) { + if (item->device_id == device_id) { + return item; + } + item = item->next; + } + + return NULL; } /* Function to get the device-dependent name of a joystick */ const char * SDL_SYS_JoystickNameForDeviceIndex(int device_index) { - return SYS_JoystickNames[device_index]; + return JoystickByDevIndex(device_index)->name; } /* Function to perform the mapping from device index to the instance id for this index */ SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index) { - return device_index; + return JoystickByDevIndex(device_index)->device_instance; } /* Function to open a joystick for use. @@ -221,49 +414,54 @@ SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index) int SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index) { - if (device_index < SYS_numjoysticks) { - joystick->nhats = 0; - joystick->nballs = 0; - if (SYS_accelAsJoy && device_index == ANDROID_ACCELEROMETER_INDEX) { - joystick->nbuttons = 0; - joystick->naxes = 3; - } else { - /* FIXME: Get the real number of buttons in the device? */ - joystick->nbuttons = ANDROID_MAX_NBUTTONS; - joystick->naxes = Android_JNI_GetJoystickAxes(device_index); - } - - SYS_Joysticks[device_index] = joystick; - return 0; - } else { - return SDL_SetError("No joystick available with that index"); + SDL_joylist_item *item = JoystickByDevIndex(device_index); + char *fname = NULL; + + if (item == NULL ) { + return SDL_SetError("No such device"); } + + if (item->joystick != NULL) { + return SDL_SetError("Joystick already opened"); + } + + joystick->instance_id = item->device_instance; + joystick->hwdata = (struct joystick_hwdata *) item; + item->joystick = joystick; + joystick->nhats = item->nhats; + joystick->nballs = item->nballs; + joystick->nbuttons = item->nbuttons; + joystick->naxes = item->naxes; + + return (0); } /* Function to determine is this joystick is attached to the system right now */ SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick) { - return SDL_TRUE; + return !joystick->closed && (joystick->hwdata != NULL); } -/* Function to update the state of a joystick - called as a device poll. - * This function shouldn't update the joystick structure directly, - * but instead should call SDL_PrivateJoystick*() to deliver events - * and update joystick device state. - */ void SDL_SYS_JoystickUpdate(SDL_Joystick * joystick) { int i; Sint16 value; float values[3]; + SDL_joylist_item *item = SDL_joylist; - if (SYS_accelAsJoy && Android_JNI_GetAccelerometerValues(values) && - joystick->instance_id == ANDROID_ACCELEROMETER_INDEX) { - for ( i = 0; i < 3; i++ ) { - value = (Sint16)(values[i] * 32767.0f); - SDL_PrivateJoystickAxis(joystick, i, value); + while (item) { + if (item->is_accelerometer) { + if (item->joystick) { + Android_JNI_GetAccelerometerValues(values); + for ( i = 0; i < 3; i++ ) { + value = (Sint16)(values[i] * 32767.0f); + SDL_PrivateJoystickAxis(item->joystick, i, value); + } + } + break; } + item = item->next; } } @@ -271,14 +469,10 @@ SDL_SYS_JoystickUpdate(SDL_Joystick * joystick) void SDL_SYS_JoystickClose(SDL_Joystick * joystick) { - int device_index; - - for (device_index = 0; device_index < SYS_numjoysticks; device_index++) { - if ( SYS_Joysticks[device_index] == joystick ) { - SYS_Joysticks[device_index] = NULL; - } + if (joystick->hwdata) { + ((SDL_joylist_item*)joystick->hwdata)->joystick = NULL; + joystick->hwdata = NULL; } - joystick->closed = 1; } @@ -286,70 +480,29 @@ SDL_SYS_JoystickClose(SDL_Joystick * joystick) void SDL_SYS_JoystickQuit(void) { - SDL_free(SYS_JoystickNames); - SDL_free(SYS_Joysticks); - SYS_JoystickNames = NULL; - SYS_Joysticks = NULL; -} + SDL_joylist_item *item = NULL; + SDL_joylist_item *next = NULL; -SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index ) -{ - SDL_JoystickGUID guid; - /* the GUID is just the first 16 chars of the name for now */ - const char *name = SDL_SYS_JoystickNameForDeviceIndex( device_index ); - SDL_zero( guid ); - SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) ); - return guid; -} + for (item = SDL_joylist; item; item = next) { + next = item->next; + SDL_free(item->name); + SDL_free(item); + } -SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick) -{ - SDL_JoystickGUID guid; - /* the GUID is just the first 16 chars of the name for now */ - const char *name = joystick->name; - SDL_zero( guid ); - SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) ); - return guid; -} + SDL_joylist = SDL_joylist_tail = NULL; -int -Android_OnPadDown(int padId, int keycode) -{ - int button = keycode_to_SDL(keycode); - if (button >= 0) { - if (SYS_Joysticks[padId]) { - SDL_PrivateJoystickButton(SYS_Joysticks[padId], button , SDL_PRESSED); - } - return 0; - } - - return -1; + numjoysticks = 0; + instance_counter = 0; } -int -Android_OnPadUp(int padId, int keycode) +SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index ) { - int button = keycode_to_SDL(keycode); - if (button >= 0) { - if (SYS_Joysticks[padId]) { - SDL_PrivateJoystickButton(SYS_Joysticks[padId], button, SDL_RELEASED); - } - return 0; - } - - return -1; + return JoystickByDevIndex(device_index)->guid; } -int -Android_OnJoy(int joyId, int axis, float value) +SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick) { - /* Android gives joy info normalized as [-1.0, 1.0] or [0.0, 1.0] */ - /* TODO: Are the reported values right? */ - if (SYS_Joysticks[joyId]) { - SDL_PrivateJoystickAxis(SYS_Joysticks[joyId], axis, (Sint16) (32767.*value) ); - } - - return 0; + return ((SDL_joylist_item*)joystick->hwdata)->guid; } #endif /* SDL_JOYSTICK_ANDROID */ diff --git a/src/joystick/android/SDL_sysjoystick.h b/src/joystick/android/SDL_sysjoystick_c.h similarity index 52% rename from src/joystick/android/SDL_sysjoystick.h rename to src/joystick/android/SDL_sysjoystick_c.h index f98c6a04bd4d4..1c1d975c6b9be 100644 --- a/src/joystick/android/SDL_sysjoystick.h +++ b/src/joystick/android/SDL_sysjoystick_c.h @@ -21,8 +21,31 @@ #include "SDL_config.h" -extern int Android_OnPadDown(int padId, int keycode); -extern int Android_OnPadUp(int padId, int keycode); -extern int Android_OnJoy(int joyId, int axisnum, float value); +#ifdef SDL_JOYSTICK_ANDROID +#include "../SDL_sysjoystick.h" + +extern int Android_OnPadDown(int device_id, int keycode); +extern int Android_OnPadUp(int device_id, int keycode); +extern int Android_OnJoy(int device_id, int axisnum, float value); +extern int Android_AddJoystick(int device_id, const char *name, SDL_bool is_accelerometer, int nbuttons, int naxes, int nhats, int nballs); +extern int Android_RemoveJoystick(int device_id); + +/* A linked list of available joysticks */ +typedef struct SDL_joylist_item +{ + int device_instance; + int device_id; /* Android's device id */ + char *name; /* "SideWinder 3D Pro" or whatever */ + SDL_JoystickGUID guid; + SDL_bool is_accelerometer; + SDL_Joystick *joystick; + int nbuttons, naxes, nhats, nballs; + + struct SDL_joylist_item *next; +} SDL_joylist_item; + +typedef SDL_joylist_item joystick_hwdata; + +#endif /* SDL_JOYSTICK_ANDROID */ /* vi: set ts=4 sw=4 expandtab: */