diff --git a/android-project/src/org/libsdl/app/SDLActivity.java b/android-project/src/org/libsdl/app/SDLActivity.java --- a/android-project/src/org/libsdl/app/SDLActivity.java +++ b/android-project/src/org/libsdl/app/SDLActivity.java @@ -20,6 +20,10 @@ import android.media.*; import android.hardware.*; +import java.lang.*; +import java.util.List; +import java.util.ArrayList; + /** SDL Activity @@ -38,6 +42,9 @@ // This is what SDL runs in. It invokes SDL_main(), eventually protected static Thread mSDLThread; + + // Joystick + private static List mJoyIdList; // Audio protected static Thread mAudioThread; @@ -245,6 +252,10 @@ public static native void nativePause(); public static native void nativeResume(); public static native void onNativeResize(int x, int y, int format); + public static native void onNativePadDown(int padId, int keycode); + public static native void onNativePadUp(int padId, int keycode); + public static native void onNativeJoy(int joyId, int axis, + float value); public static native void onNativeKeyDown(int keycode); public static native void onNativeKeyUp(int keycode); public static native void onNativeKeyboardFocusLost(); @@ -283,6 +294,62 @@ // Called from SDLMain() thread and can't directly affect the view return mSingleton.sendCommand(COMMAND_CHANGE_TITLE, title); } + + // Create a list of valid ID's the first time this function is called + private static void createJoystickList() { + if(mJoyIdList != null) { + return; + } + + mJoyIdList = new ArrayList(); + // InputDevice.getDeviceIds requires SDK >= 16 + if(Build.VERSION.SDK_INT >= 16) { + int[] deviceIds = InputDevice.getDeviceIds(); + for(int i=0; i= 12) { + return InputDevice.getDevice(mJoyIdList.get(joy)).getMotionRanges().size(); + } else { + return 2; + } + } + + public static int getJoyId(int devId) { + int i=0; + + createJoystickList(); + + for(i=0; i= 12) { + setOnGenericMotionListener(new genericMotionHandler()); + } // Some arbitrary defaults to avoid a potential division by zero mWidth = 1.0f; @@ -736,16 +807,26 @@ // Key events @Override public boolean onKey(View v, int keyCode, KeyEvent event) { - - if (event.getAction() == KeyEvent.ACTION_DOWN) { - //Log.v("SDL", "key down: " + keyCode); - SDLActivity.onNativeKeyDown(keyCode); - return true; - } - else if (event.getAction() == KeyEvent.ACTION_UP) { - //Log.v("SDL", "key up: " + keyCode); - SDLActivity.onNativeKeyUp(keyCode); - return true; + // Dispatch the different events depending on where they come from + if(event.getSource() == InputDevice.SOURCE_KEYBOARD) { + if (event.getAction() == KeyEvent.ACTION_DOWN) { + //Log.v("SDL", "key down: " + keyCode); + SDLActivity.onNativeKeyDown(keyCode); + return true; + } + else if (event.getAction() == KeyEvent.ACTION_UP) { + //Log.v("SDL", "key up: " + keyCode); + SDLActivity.onNativeKeyUp(keyCode); + return true; + } + } else if ( (event.getSource() & 0x00000401) != 0 || /* API 12: SOURCE_GAMEPAD */ + (event.getSource() & InputDevice.SOURCE_DPAD) != 0 ) { + int id = SDLActivity.getJoyId( event.getDeviceId() ); + if (event.getAction() == KeyEvent.ACTION_DOWN) { + SDLActivity.onNativePadDown(id, keyCode); + } else if (event.getAction() == KeyEvent.ACTION_UP) { + SDLActivity.onNativePadUp(id, keyCode); + } } return false; @@ -808,6 +889,31 @@ } } + class genericMotionHandler extends Activity implements View.OnGenericMotionListener { + // Generic Motion (mouse hover, joystick...) events go here + // We only have joysticks yet + @Override + public boolean onGenericMotion(View v, MotionEvent event) { + int actionPointerIndex = event.getActionIndex(); + int action = event.getActionMasked(); + + if ( (event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) { + switch(action) { + case MotionEvent.ACTION_MOVE: + int id = SDLActivity.getJoyId( event.getDeviceId() ); + float x = event.getAxisValue(MotionEvent.AXIS_X, actionPointerIndex); + float y = event.getAxisValue(MotionEvent.AXIS_Y, actionPointerIndex); + SDLActivity.onNativeJoy(id, 0, x); + SDLActivity.onNativeJoy(id, 1, y); + + break; + } + } + return true; + } + + } + } /* This is a fake invisible editor view that receives the input and defines the diff --git a/include/SDL_hints.h b/include/SDL_hints.h --- a/include/SDL_hints.h +++ b/include/SDL_hints.h @@ -192,6 +192,17 @@ * "LandscapeLeft", "LandscapeRight", "Portrait" "PortraitUpsideDown" */ #define SDL_HINT_ORIENTATIONS "SDL_IOS_ORIENTATIONS" + +/** + * \brief A variable controlling whether a built-in accelerometer should be + * listed as a joystick device (and the only one), rather than listing actual + * joysticks only. Currently has an effect on Android. + * + * This variable can be set to the following values: + * "0" - List actual joysticks and accept input from them + * "1" - Emulate a built-in accelerometer as a 3-axis joystick and disable actual joystick support (the default) + */ +#define SDL_HINT_ACCEL_AS_JOY "SDL_ACCEL_AS_JOY" /** diff --git a/src/core/android/SDL_android.c b/src/core/android/SDL_android.c --- a/src/core/android/SDL_android.c +++ b/src/core/android/SDL_android.c @@ -151,6 +151,30 @@ Android_SetScreenResolution(width, height, format); } +// Paddown +void Java_org_libsdl_app_SDLActivity_onNativePadDown( + JNIEnv* env, jclass jcls, + jint padId, jint keycode) +{ + Android_OnPadDown(padId, keycode); +} + +// Padup +void Java_org_libsdl_app_SDLActivity_onNativePadUp( + JNIEnv* env, jclass jcls, + jint padId, jint keycode) +{ + Android_OnPadUp(padId, keycode); +} + +// Padup +void Java_org_libsdl_app_SDLActivity_onNativeJoy( + JNIEnv* env, jclass jcls, + jint joyId, jint axis, jfloat value) +{ + Android_OnJoy(joyId, axis, value); +} + // Keydown void Java_org_libsdl_app_SDLActivity_onNativeKeyDown( JNIEnv* env, jclass jcls, jint keycode) @@ -1169,6 +1193,61 @@ return 0; } +// 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) +{ + 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); +} + // 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 --- a/src/core/android/SDL_android.h +++ b/src/core/android/SDL_android.h @@ -60,6 +60,11 @@ /* Power support */ 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); /* Threads */ #include diff --git a/src/joystick/android/SDL_sysjoystick.c b/src/joystick/android/SDL_sysjoystick.c --- a/src/joystick/android/SDL_sysjoystick.c +++ b/src/joystick/android/SDL_sysjoystick.c @@ -29,12 +29,48 @@ #include "SDL_error.h" #include "SDL_events.h" #include "SDL_joystick.h" +#include "SDL_hints.h" #include "../SDL_sysjoystick.h" #include "../SDL_joystick_c.h" #include "../../core/android/SDL_android.h" static const char *accelerometerName = "Android accelerometer"; +static SDL_Joystick **SYS_Joysticks; +static char **SYS_JoystickNames; +static int SYS_numjoysticks; +static SDL_bool SYS_accelAsJoy; + +/* Function to convert Android keyCodes into SDL ones. + * This code manipulation is done to get a sequential list of codes. + */ +int +keycode_to_SDL(int keycode) +{ + int final = 0; + /* D-Pad key codes (API 1): + * KEYCODE_DPAD_UP=19, KEYCODE_DPAD_DOWN + * KEYCODE_DPAD_LEFT, KEYCODE_DPAD_RIGHT, KEYCODE_DPAD_CENTER + */ + if(keycode < 96) + return keycode-19; + /* Some gamepad buttons (API 9): + * KEYCODE_BUTTON_A=96, KEYCODE_BUTTON_B, KEYCODE_BUTTON_C, + * KEYCODE_BUTTON_X, KEYCODE_BUTTON_Y, KEYCODE_BUTTON_Z, + * KEYCODE_BUTTON_L1, KEYCODE_BUTTON_L2, + * KEYCODE_BUTTON_R1, KEYCODE_BUTTON_R2, + * KEYCODE_BUTTON_THUMBL, KEYCODE_BUTTON_THUMBR, + * KEYCODE_BUTTON_START, KEYCODE_BUTTON_SELECT, KEYCODE_BUTTON_MODE + */ + else if(keycode < 188) + return keycode-91; + /* More gamepad buttons (API 12): + * KEYCODE_BUTTON_1=188 to KEYCODE_BUTTON_16 + */ + else + return keycode-168; +} + /* 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. @@ -43,12 +79,58 @@ int SDL_SYS_JoystickInit(void) { + int i = 0; + const char *env; + + // Should we emulate the accelerometer as a joystick? + env = SDL_GetHint(SDL_HINT_ACCEL_AS_JOY); + if (env && !SDL_atoi(env)) + SYS_accelAsJoy = SDL_FALSE; + else + SYS_accelAsJoy = SDL_TRUE; /* Default behavior */ + + SYS_numjoysticks = Android_JNI_GetNumJoysticks(); + if (SYS_accelAsJoy) { + SYS_numjoysticks++; + } + SYS_Joysticks = (SDL_Joystick **)SDL_malloc(SYS_numjoysticks*sizeof(SDL_Joystick *)); + if (SYS_Joysticks == NULL) + { + SDL_OutOfMemory(); + return (-1); + } + SYS_JoystickNames = (char **)SDL_malloc(SYS_numjoysticks*sizeof(char *)); + if (SYS_JoystickNames == NULL) + { + SDL_free(SYS_Joysticks); + SYS_Joysticks = NULL; + SDL_OutOfMemory(); + return (-1); + } + SDL_memset(SYS_JoystickNames, 0, (SYS_numjoysticks*sizeof(char *))); + SDL_memset(SYS_Joysticks, 0, (SYS_numjoysticks*sizeof(SDL_Joystick *))); + + for (i = 0; i < SYS_numjoysticks; i++) + { + if (SYS_accelAsJoy && i == (SYS_numjoysticks-1)) { + SYS_JoystickNames[i] = accelerometerName; + } else { + SYS_JoystickNames[i] = Android_JNI_GetJoystickName(i); + } + } + + if (SYS_accelAsJoy) { + SYS_JoystickNames[SYS_numjoysticks-1] = accelerometerName; + } + + return (SYS_numjoysticks); + return (1); } int SDL_SYS_NumJoysticks() { - return 1; + return SYS_numjoysticks; } void SDL_SYS_JoystickDetect() @@ -64,7 +146,7 @@ const char * SDL_SYS_JoystickNameForDeviceIndex(int device_index) { - return accelerometerName; + return SYS_JoystickNames[device_index]; } /* Function to perform the mapping from device index to the instance id for this index */ @@ -81,11 +163,19 @@ int SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index) { - if (device_index == 0) { - joystick->nbuttons = 0; + if (device_index < SYS_numjoysticks) { joystick->nhats = 0; joystick->nballs = 0; - joystick->naxes = 3; + if (SYS_accelAsJoy && device_index == (SYS_numjoysticks-1)) { + joystick->nbuttons = 0; + joystick->naxes = 3; + } else { + // TODO: Get the real number of buttons in the device + joystick->nbuttons = 36; + joystick->naxes = Android_JNI_GetJoystickAxes(device_index); + } + + SYS_Joysticks[device_index] = joystick; return 0; } else { SDL_SetError("No joystick available with that index"); @@ -111,7 +201,8 @@ Sint16 value; float values[3]; - if (Android_JNI_GetAccelerometerValues(values)) { + if (SYS_accelAsJoy && Android_JNI_GetAccelerometerValues(values) && + joystick->instance_id == (SYS_numjoysticks-1)) { for ( i = 0; i < 3; i++ ) { value = (Sint16)(values[i] * 32767.0f); SDL_PrivateJoystickAxis(joystick, i, value); @@ -129,6 +220,10 @@ void SDL_SYS_JoystickQuit(void) { + SDL_free(SYS_JoystickNames); + SDL_free(SYS_Joysticks); + SYS_JoystickNames = NULL; + SYS_Joysticks = NULL; } SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index ) @@ -151,6 +246,32 @@ return guid; } +int +Android_OnPadDown(int padId, int keycode) +{ + SDL_PrivateJoystickButton(SYS_Joysticks[padId], keycode_to_SDL(keycode), SDL_PRESSED); + + return 0; +} + +int +Android_OnPadUp(int padId, int keycode) +{ + SDL_PrivateJoystickButton(SYS_Joysticks[padId], keycode_to_SDL(keycode), SDL_RELEASED); + + return 0; +} + +int +Android_OnJoy(int joyId, int axis, float value) +{ + // Android gives joy info normalized as [-1.0, 1.0] or [0.0, 1.0] + // TODO: Are the reported values right? + SDL_PrivateJoystickAxis(SYS_Joysticks[joyId], axis, (Sint16) (32767.*value) ); + + return 0; +} + #endif /* SDL_JOYSTICK_ANDROID */ /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/joystick/android/SDL_sysjoystick.h b/src/joystick/android/SDL_sysjoystick.h new file mode 100644 --- /dev/null +++ b/src/joystick/android/SDL_sysjoystick.h @@ -0,0 +1,28 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2013 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_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); + +/* vi: set ts=4 sw=4 expandtab: */