// This is what SDL runs in. It invokes SDL_main(), eventually
protected static Thread mSDLThread;
-
+
// Audio
protected static AudioTrack mAudioTrack;
// Load the .so
static {
System.loadLibrary("SDL2");
- //System.loadLibrary("SDL2_image");
- //System.loadLibrary("SDL2_mixer");
+ System.loadLibrary("SDL2_image");
+ System.loadLibrary("SDL2_mixer");
//System.loadLibrary("SDL2_net");
- //System.loadLibrary("SDL2_ttf");
+ System.loadLibrary("SDL2_ttf");
System.loadLibrary("main");
}
-
-
+
+
public static void initialize() {
// The static nature of the singleton and Android quirkyness force us to initialize everything here
// Otherwise, when exiting the app and returning to it, these variables *keep* their pre exit values
protected void onCreate(Bundle savedInstanceState) {
Log.v("SDL", "onCreate():" + mSingleton);
super.onCreate(savedInstanceState);
-
+
SDLActivity.initialize();
// So we can call stuff from static callbacks
mSingleton = this;
// Set up the surface
mSurface = new SDLSurface(getApplication());
-
+
if(Build.VERSION.SDK_INT >= 12) {
mJoystickHandler = new SDLJoystickHandler_API12();
}
//Log.v("SDL", "Finished waiting for SDL thread");
}
-
+
super.onDestroy();
// Reset everything in case the user re opens the app
SDLActivity.initialize();
mSurface.enableSensor(Sensor.TYPE_ACCELEROMETER, true);
}
}
-
+
/* The native thread has finished */
public static void handleNativeExit() {
SDLActivity.mSDLThread = null;
public static native void onNativeKeyUp(int keycode);
public static native void onNativeKeyboardFocusLost();
public static native void onNativeTouch(int touchDevId, int pointerFingerId,
- int action, float x,
+ int action, float x,
float y, float p);
public static native void onNativeAccel(float x, float y, float z);
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,
+ 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);
// Transfer the task to the main thread as a Runnable
return mSingleton.commandHandler.post(new ShowTextInputTask(x, y, w, h));
}
-
+
public static Surface getNativeSurface() {
return SDLActivity.mSurface.getNativeSurface();
}
int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO;
int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT;
int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1);
-
+
Log.v("SDL", "SDL audio: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + (sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer");
-
+
// Let the user pick a larger buffer if they really want -- but ye
// gods they probably shouldn't, the minimums are horrifyingly high
// latency already
desiredFrames = Math.max(desiredFrames, (AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize);
-
+
if (mAudioTrack == null) {
mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM);
-
+
// Instantiating AudioTrack can "succeed" without an exception and the track may still be invalid
// Ref: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/AudioTrack.java
// Ref: http://developer.android.com/reference/android/media/AudioTrack.html#getState()
-
+
if (mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
Log.e("SDL", "Failed during initialization of Audio Track");
mAudioTrack = null;
return -1;
}
-
+
mAudioTrack.play();
}
-
+
Log.v("SDL", "SDL audio: got " + ((mAudioTrack.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioTrack.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + (mAudioTrack.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer");
-
+
return 0;
}
-
+
public static void audioWriteShortBuffer(short[] buffer) {
for (int i = 0; i < buffer.length; ) {
int result = mAudioTrack.write(buffer, i, buffer.length - i);
}
}
}
-
+
public static void audioWriteByteBuffer(byte[] buffer) {
for (int i = 0; i < buffer.length; ) {
int result = mAudioTrack.write(buffer, i, buffer.length - i);
}
return Arrays.copyOf(filtered, used);
}
-
+
// Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance
public static boolean handleJoystickMotionEvent(MotionEvent event) {
return mJoystickHandler.handleMotionEvent(event);
}
-
+
public static void pollInputDevices() {
if (SDLActivity.mSDLThread != null) {
mJoystickHandler.pollInputDevices();
}
}
-
+
}
/**
/**
SDLSurface. This is what we draw on, so we need to know when it's created
- in order to do anything useful.
+ in order to do anything useful.
Because of this, that's where we set up the SDL thread
*/
-class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
+class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
View.OnKeyListener, View.OnTouchListener, SensorEventListener {
// Sensors
// Keep track of the surface size to normalize touch events
protected static float mWidth, mHeight;
- // Startup
+ // Startup
public SDLSurface(Context context) {
super(context);
- getHolder().addCallback(this);
-
+ getHolder().addCallback(this);
+
setFocusable(true);
setFocusableInTouchMode(true);
requestFocus();
- setOnKeyListener(this);
- setOnTouchListener(this);
+ setOnKeyListener(this);
+ setOnTouchListener(this);
mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
-
+
if(Build.VERSION.SDK_INT >= 12) {
setOnGenericMotionListener(new SDLGenericMotionListener_API12());
}
mWidth = 1.0f;
mHeight = 1.0f;
}
-
+
public Surface getNativeSurface() {
return getHolder().getSurface();
}
SDLActivity.mSDLThread = new Thread(new SDLMain(), "SDLThread");
enableSensor(Sensor.TYPE_ACCELEROMETER, true);
SDLActivity.mSDLThread.start();
-
+
// Set up a listener thread to catch when the native thread ends
new Thread(new Runnable(){
@Override
SDLActivity.mSDLThread.join();
}
catch(Exception e){}
- finally{
+ finally{
// Native thread has finished
if (! SDLActivity.mExitCalledFromJava) {
SDLActivity.handleNativeExit();
// Dispatch the different events depending on where they come from
// Some SOURCE_DPAD or SOURCE_GAMEPAD are also SOURCE_KEYBOARD
// So, we try to process them as DPAD or GAMEPAD events first, if that fails we try them as KEYBOARD
-
+
if ( (event.getSource() & 0x00000401) != 0 || /* API 12: SOURCE_GAMEPAD */
(event.getSource() & InputDevice.SOURCE_DPAD) != 0 ) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
}
}
}
-
+
if( (event.getSource() & InputDevice.SOURCE_KEYBOARD) != 0) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
//Log.v("SDL", "key down: " + keyCode);
return true;
}
}
-
+
return false;
}
int pointerFingerId;
int i = -1;
float x,y,p;
-
+
switch(action) {
case MotionEvent.ACTION_MOVE:
for (i = 0; i < pointerCount; i++) {
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
}
break;
-
+
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_DOWN:
// Primary pointer up/down, the index is always zero
if (i == -1) {
i = event.getActionIndex();
}
-
+
pointerFingerId = event.getPointerId(i);
x = event.getX(i) / mWidth;
y = event.getY(i) / mHeight;
p = event.getPressure(i);
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
break;
-
+
default:
break;
}
return true;
- }
+ }
// Sensor events
public void enableSensor(int sensortype, boolean enabled) {
// TODO: This uses getDefaultSensor - what if we have >1 accels?
if (enabled) {
- mSensorManager.registerListener(this,
- mSensorManager.getDefaultSensor(sensortype),
+ mSensorManager.registerListener(this,
+ mSensorManager.getDefaultSensor(sensortype),
SensorManager.SENSOR_DELAY_GAME, null);
} else {
- mSensorManager.unregisterListener(this,
+ mSensorManager.unregisterListener(this,
mSensorManager.getDefaultSensor(sensortype));
}
}
-
+
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO
y / SensorManager.GRAVITY_EARTH,
event.values[2] / SensorManager.GRAVITY_EARTH - 1);
}
- }
+ }
}
/* This is a fake invisible editor view that receives the input and defines the
return false;
}
-
+
//
@Override
public boolean onKeyPreIme (int keyCode, KeyEvent event) {
public native void nativeSetComposingText(String text, int newCursorPosition);
@Override
- public boolean deleteSurroundingText(int beforeLength, int afterLength) {
+ public boolean deleteSurroundingText(int beforeLength, int afterLength) {
// Workaround to capture backspace key. Ref: http://stackoverflow.com/questions/14560344/android-backspace-in-webview-baseinputconnection
if (beforeLength == 1 && afterLength == 0) {
// backspace
/* A null joystick handler for API level < 12 devices (the accelerometer is handled separately) */
class SDLJoystickHandler {
-
+
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 device_id;
public String name;
return arg0.getAxis() - arg1.getAxis();
}
}
-
+
private ArrayList<SDLJoystick> mJoysticks;
-
+
public SDLJoystickHandler_API12() {
-
+
mJoysticks = new ArrayList<SDLJoystick>();
}
// 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.name = joystickDevice.getName();
joystick.axes = new ArrayList<InputDevice.MotionRange>();
joystick.hats = new ArrayList<InputDevice.MotionRange>();
-
+
List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges();
Collections.sort(ranges, new RangeComparator());
for (InputDevice.MotionRange range : ranges ) {
}
}
}
-
+
mJoysticks.add(joystick);
- SDLActivity.nativeAddJoystick(joystick.device_id, joystick.name, 0, -1,
+ SDLActivity.nativeAddJoystick(joystick.device_id, joystick.name, 0, -1,
joystick.axes.size(), joystick.hats.size()/2, 0);
}
}
}
-
+
/* Check removed devices */
ArrayList<Integer> removedDevices = new ArrayList<Integer>();
for(int i=0; i < mJoysticks.size(); i++) {
removedDevices.add(device_id);
}
}
-
+
for(int i=0; i < removedDevices.size(); i++) {
int device_id = removedDevices.get(i);
SDLActivity.nativeRemoveJoystick(device_id);
break;
}
}
- }
+ }
}
-
+
protected SDLJoystick getJoystick(int device_id) {
for(int i=0; i < mJoysticks.size(); i++) {
if (mJoysticks.get(i).device_id == device_id) {
}
}
return null;
- }
-
- @Override
+ }
+
+ @Override
public boolean handleMotionEvent(MotionEvent event) {
if ( (event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) {
int actionPointerIndex = event.getActionIndex();
/* Normalize the value to -1...1 */
float value = ( event.getAxisValue( range.getAxis(), actionPointerIndex) - range.getMin() ) / range.getRange() * 2.0f - 1.0f;
SDLActivity.onNativeJoy(joystick.device_id, i, value );
- }
+ }
for (int i = 0; i < joystick.hats.size(); i+=2) {
int hatX = Math.round(event.getAxisValue( joystick.hats.get(i).getAxis(), actionPointerIndex ) );
int hatY = Math.round(event.getAxisValue( joystick.hats.get(i+1).getAxis(), actionPointerIndex ) );
}
}
return true;
- }
+ }
}
class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener {