2011-01-06 17:12:31 -08:00
|
|
|
package org.libsdl.app;
|
|
|
|
|
2011-08-26 13:15:05 +01:00
|
|
|
import javax.microedition.khronos.egl.EGL10;
|
2011-01-06 17:12:31 -08:00
|
|
|
import javax.microedition.khronos.egl.EGLConfig;
|
2011-08-26 13:15:05 +01:00
|
|
|
import javax.microedition.khronos.egl.EGLContext;
|
2011-01-06 17:12:31 -08:00
|
|
|
import javax.microedition.khronos.opengles.GL10;
|
|
|
|
import javax.microedition.khronos.egl.*;
|
|
|
|
|
|
|
|
import android.app.*;
|
|
|
|
import android.content.*;
|
|
|
|
import android.view.*;
|
2012-10-03 20:49:16 -07:00
|
|
|
import android.view.inputmethod.BaseInputConnection;
|
|
|
|
import android.view.inputmethod.EditorInfo;
|
|
|
|
import android.view.inputmethod.InputConnection;
|
2012-08-11 10:15:59 -07:00
|
|
|
import android.view.inputmethod.InputMethodManager;
|
2012-10-03 20:49:16 -07:00
|
|
|
import android.widget.AbsoluteLayout;
|
2011-01-06 17:12:31 -08:00
|
|
|
import android.os.*;
|
|
|
|
import android.util.Log;
|
|
|
|
import android.graphics.*;
|
|
|
|
import android.text.method.*;
|
|
|
|
import android.text.*;
|
|
|
|
import android.media.*;
|
|
|
|
import android.hardware.*;
|
|
|
|
import android.content.*;
|
|
|
|
|
|
|
|
import java.lang.*;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
SDL Activity
|
|
|
|
*/
|
|
|
|
public class SDLActivity extends Activity {
|
|
|
|
|
2012-06-24 21:10:17 -03:00
|
|
|
// Keep track of the paused state
|
2013-04-19 14:40:39 -03:00
|
|
|
public static boolean mIsPaused = false;
|
2012-06-24 21:10:17 -03:00
|
|
|
|
2011-01-12 14:53:23 -08:00
|
|
|
// Main components
|
2011-01-06 17:12:31 -08:00
|
|
|
private static SDLActivity mSingleton;
|
|
|
|
private static SDLSurface mSurface;
|
2012-10-03 20:49:16 -07:00
|
|
|
private static View mTextEdit;
|
|
|
|
private static ViewGroup mLayout;
|
2011-01-06 17:12:31 -08:00
|
|
|
|
2012-01-08 01:05:25 -05:00
|
|
|
// This is what SDL runs in. It invokes SDL_main(), eventually
|
|
|
|
private static Thread mSDLThread;
|
|
|
|
|
2011-01-12 14:53:23 -08:00
|
|
|
// Audio
|
2011-01-13 11:14:20 -08:00
|
|
|
private static Thread mAudioThread;
|
2011-01-06 17:12:31 -08:00
|
|
|
private static AudioTrack mAudioTrack;
|
|
|
|
|
2012-01-08 01:05:25 -05:00
|
|
|
// EGL private objects
|
|
|
|
private static EGLContext mEGLContext;
|
|
|
|
private static EGLSurface mEGLSurface;
|
|
|
|
private static EGLDisplay mEGLDisplay;
|
|
|
|
private static EGLConfig mEGLConfig;
|
|
|
|
private static int mGLMajor, mGLMinor;
|
|
|
|
|
2011-01-12 14:53:23 -08:00
|
|
|
// Load the .so
|
2011-01-06 17:12:31 -08:00
|
|
|
static {
|
2012-02-04 16:39:52 -05:00
|
|
|
System.loadLibrary("SDL2");
|
|
|
|
//System.loadLibrary("SDL2_image");
|
|
|
|
//System.loadLibrary("SDL2_mixer");
|
|
|
|
//System.loadLibrary("SDL2_ttf");
|
2011-01-06 17:12:31 -08:00
|
|
|
System.loadLibrary("main");
|
|
|
|
}
|
|
|
|
|
2011-01-12 14:53:23 -08:00
|
|
|
// Setup
|
2011-01-06 17:12:31 -08:00
|
|
|
protected void onCreate(Bundle savedInstanceState) {
|
2011-01-12 17:53:06 -08:00
|
|
|
//Log.v("SDL", "onCreate()");
|
2011-01-06 17:12:31 -08:00
|
|
|
super.onCreate(savedInstanceState);
|
|
|
|
|
2011-01-12 14:53:23 -08:00
|
|
|
// So we can call stuff from static callbacks
|
2011-01-06 17:12:31 -08:00
|
|
|
mSingleton = this;
|
|
|
|
|
2011-01-12 14:53:23 -08:00
|
|
|
// Set up the surface
|
2011-01-06 17:12:31 -08:00
|
|
|
mSurface = new SDLSurface(getApplication());
|
2012-10-03 20:49:16 -07:00
|
|
|
|
|
|
|
mLayout = new AbsoluteLayout(this);
|
|
|
|
mLayout.addView(mSurface);
|
|
|
|
|
|
|
|
setContentView(mLayout);
|
|
|
|
|
2011-01-06 17:12:31 -08:00
|
|
|
SurfaceHolder holder = mSurface.getHolder();
|
|
|
|
}
|
|
|
|
|
2011-01-12 14:29:01 -08:00
|
|
|
// Events
|
2012-06-19 13:57:42 -03:00
|
|
|
/*protected void onPause() {
|
2012-01-08 01:05:25 -05:00
|
|
|
Log.v("SDL", "onPause()");
|
2011-01-06 17:12:31 -08:00
|
|
|
super.onPause();
|
2012-06-19 13:57:42 -03:00
|
|
|
// Don't call SDLActivity.nativePause(); here, it will be called by SDLSurface::surfaceDestroyed
|
2011-01-06 17:12:31 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
protected void onResume() {
|
2012-01-08 01:05:25 -05:00
|
|
|
Log.v("SDL", "onResume()");
|
2011-01-06 17:12:31 -08:00
|
|
|
super.onResume();
|
2012-06-19 13:57:42 -03:00
|
|
|
// Don't call SDLActivity.nativeResume(); here, it will be called via SDLSurface::surfaceChanged->SDLActivity::startApp
|
|
|
|
}*/
|
2012-01-08 01:05:25 -05:00
|
|
|
|
|
|
|
protected void onDestroy() {
|
|
|
|
super.onDestroy();
|
|
|
|
Log.v("SDL", "onDestroy()");
|
|
|
|
// Send a quit message to the application
|
|
|
|
SDLActivity.nativeQuit();
|
|
|
|
|
|
|
|
// Now wait for the SDL thread to quit
|
|
|
|
if (mSDLThread != null) {
|
|
|
|
try {
|
|
|
|
mSDLThread.join();
|
|
|
|
} catch(Exception e) {
|
|
|
|
Log.v("SDL", "Problem stopping thread: " + e);
|
|
|
|
}
|
|
|
|
mSDLThread = null;
|
|
|
|
|
|
|
|
//Log.v("SDL", "Finished waiting for SDL thread");
|
|
|
|
}
|
2011-01-06 17:12:31 -08:00
|
|
|
}
|
|
|
|
|
2011-01-13 15:10:17 -08:00
|
|
|
// Messages from the SDLMain thread
|
2012-08-11 10:15:59 -07:00
|
|
|
static final int COMMAND_CHANGE_TITLE = 1;
|
2012-11-04 21:53:28 -08:00
|
|
|
static final int COMMAND_UNUSED = 2;
|
2012-10-03 20:49:16 -07:00
|
|
|
static final int COMMAND_TEXTEDIT_HIDE = 3;
|
2011-01-13 15:10:17 -08:00
|
|
|
|
|
|
|
// Handler for the messages
|
|
|
|
Handler commandHandler = new Handler() {
|
2012-08-11 10:15:59 -07:00
|
|
|
@Override
|
2011-01-13 15:10:17 -08:00
|
|
|
public void handleMessage(Message msg) {
|
2012-08-11 10:15:59 -07:00
|
|
|
switch (msg.arg1) {
|
|
|
|
case COMMAND_CHANGE_TITLE:
|
2011-01-13 15:10:17 -08:00
|
|
|
setTitle((String)msg.obj);
|
2012-08-11 10:15:59 -07:00
|
|
|
break;
|
2012-10-03 20:49:16 -07:00
|
|
|
case COMMAND_TEXTEDIT_HIDE:
|
|
|
|
if (mTextEdit != null) {
|
|
|
|
mTextEdit.setVisibility(View.GONE);
|
|
|
|
|
|
|
|
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
|
|
|
|
imm.hideSoftInputFromWindow(mTextEdit.getWindowToken(), 0);
|
|
|
|
}
|
|
|
|
break;
|
2011-01-13 15:10:17 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Send a message from the SDLMain thread
|
|
|
|
void sendCommand(int command, Object data) {
|
|
|
|
Message msg = commandHandler.obtainMessage();
|
|
|
|
msg.arg1 = command;
|
|
|
|
msg.obj = data;
|
|
|
|
commandHandler.sendMessage(msg);
|
|
|
|
}
|
2011-01-06 17:12:31 -08:00
|
|
|
|
2011-01-12 14:29:01 -08:00
|
|
|
// C functions we call
|
2011-01-06 17:12:31 -08:00
|
|
|
public static native void nativeInit();
|
|
|
|
public static native void nativeQuit();
|
2012-01-08 01:05:25 -05:00
|
|
|
public static native void nativePause();
|
|
|
|
public static native void nativeResume();
|
2011-01-13 11:14:20 -08:00
|
|
|
public static native void onNativeResize(int x, int y, int format);
|
2011-01-06 17:12:31 -08:00
|
|
|
public static native void onNativeKeyDown(int keycode);
|
|
|
|
public static native void onNativeKeyUp(int keycode);
|
2011-10-13 01:21:35 -04:00
|
|
|
public static native void onNativeTouch(int touchDevId, int pointerFingerId,
|
|
|
|
int action, float x,
|
2011-01-06 17:12:31 -08:00
|
|
|
float y, float p);
|
2013-04-01 13:20:22 -03:00
|
|
|
public static native void onNativeAccel(float x, float y, float z);
|
2011-01-13 11:14:20 -08:00
|
|
|
public static native void nativeRunAudioThread();
|
2011-01-06 17:12:31 -08:00
|
|
|
|
|
|
|
|
2011-01-12 14:53:23 -08:00
|
|
|
// Java functions called from C
|
2011-01-13 15:10:17 -08:00
|
|
|
|
2012-12-31 14:57:36 -08:00
|
|
|
public static boolean createGLContext(int majorVersion, int minorVersion, int[] attribs) {
|
|
|
|
return initEGL(majorVersion, minorVersion, attribs);
|
2011-01-06 17:12:31 -08:00
|
|
|
}
|
|
|
|
|
2011-01-12 14:29:01 -08:00
|
|
|
public static void flipBuffers() {
|
2012-01-08 01:05:25 -05:00
|
|
|
flipEGL();
|
2011-01-06 17:12:31 -08:00
|
|
|
}
|
|
|
|
|
2011-01-13 15:10:17 -08:00
|
|
|
public static void setActivityTitle(String title) {
|
|
|
|
// Called from SDLMain() thread and can't directly affect the view
|
|
|
|
mSingleton.sendCommand(COMMAND_CHANGE_TITLE, title);
|
|
|
|
}
|
|
|
|
|
2012-08-11 10:15:59 -07:00
|
|
|
public static void sendMessage(int command, int param) {
|
|
|
|
mSingleton.sendCommand(command, Integer.valueOf(param));
|
|
|
|
}
|
|
|
|
|
2011-08-26 13:11:53 +01:00
|
|
|
public static Context getContext() {
|
|
|
|
return mSingleton;
|
|
|
|
}
|
2011-07-29 16:51:25 -04:00
|
|
|
|
2012-01-08 01:05:25 -05:00
|
|
|
public static void startApp() {
|
|
|
|
// Start up the C app thread
|
|
|
|
if (mSDLThread == null) {
|
|
|
|
mSDLThread = new Thread(new SDLMain(), "SDLThread");
|
|
|
|
mSDLThread.start();
|
|
|
|
}
|
|
|
|
else {
|
2012-06-24 21:10:17 -03:00
|
|
|
/*
|
|
|
|
* Some Android variants may send multiple surfaceChanged events, so we don't need to resume every time
|
|
|
|
* every time we get one of those events, only if it comes after surfaceDestroyed
|
|
|
|
*/
|
|
|
|
if (mIsPaused) {
|
|
|
|
SDLActivity.nativeResume();
|
|
|
|
SDLActivity.mIsPaused = false;
|
|
|
|
}
|
2012-01-08 01:05:25 -05:00
|
|
|
}
|
|
|
|
}
|
2012-10-03 20:49:16 -07:00
|
|
|
|
|
|
|
static class ShowTextInputHandler implements Runnable {
|
|
|
|
/*
|
|
|
|
* This is used to regulate the pan&scan method to have some offset from
|
|
|
|
* the bottom edge of the input region and the top edge of an input
|
|
|
|
* method (soft keyboard)
|
|
|
|
*/
|
|
|
|
static final int HEIGHT_PADDING = 15;
|
|
|
|
|
|
|
|
public int x, y, w, h;
|
|
|
|
|
|
|
|
public ShowTextInputHandler(int x, int y, int w, int h) {
|
|
|
|
this.x = x;
|
|
|
|
this.y = y;
|
|
|
|
this.w = w;
|
|
|
|
this.h = h;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void run() {
|
|
|
|
AbsoluteLayout.LayoutParams params = new AbsoluteLayout.LayoutParams(
|
|
|
|
w, h + HEIGHT_PADDING, x, y);
|
|
|
|
|
|
|
|
if (mTextEdit == null) {
|
|
|
|
mTextEdit = new DummyEdit(getContext());
|
|
|
|
|
|
|
|
mLayout.addView(mTextEdit, params);
|
|
|
|
} else {
|
|
|
|
mTextEdit.setLayoutParams(params);
|
|
|
|
}
|
|
|
|
|
|
|
|
mTextEdit.setVisibility(View.VISIBLE);
|
|
|
|
mTextEdit.requestFocus();
|
|
|
|
|
|
|
|
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
|
|
|
imm.showSoftInput(mTextEdit, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void showTextInput(int x, int y, int w, int h) {
|
|
|
|
// Transfer the task to the main thread as a Runnable
|
|
|
|
mSingleton.commandHandler.post(new ShowTextInputHandler(x, y, w, h));
|
|
|
|
}
|
|
|
|
|
2012-01-08 01:05:25 -05:00
|
|
|
|
|
|
|
// EGL functions
|
2012-12-31 14:57:36 -08:00
|
|
|
public static boolean initEGL(int majorVersion, int minorVersion, int[] attribs) {
|
2012-11-04 08:51:43 -08:00
|
|
|
try {
|
|
|
|
if (SDLActivity.mEGLDisplay == null) {
|
|
|
|
Log.v("SDL", "Starting up OpenGL ES " + majorVersion + "." + minorVersion);
|
2012-01-08 01:05:25 -05:00
|
|
|
|
|
|
|
EGL10 egl = (EGL10)EGLContext.getEGL();
|
|
|
|
|
|
|
|
EGLDisplay dpy = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
|
|
|
|
|
|
|
|
int[] version = new int[2];
|
|
|
|
egl.eglInitialize(dpy, version);
|
|
|
|
|
|
|
|
EGLConfig[] configs = new EGLConfig[1];
|
|
|
|
int[] num_config = new int[1];
|
2012-12-31 14:57:36 -08:00
|
|
|
if (!egl.eglChooseConfig(dpy, attribs, configs, 1, num_config) || num_config[0] == 0) {
|
2012-01-08 01:05:25 -05:00
|
|
|
Log.e("SDL", "No EGL config available");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
EGLConfig config = configs[0];
|
|
|
|
|
|
|
|
SDLActivity.mEGLDisplay = dpy;
|
|
|
|
SDLActivity.mEGLConfig = config;
|
|
|
|
SDLActivity.mGLMajor = majorVersion;
|
|
|
|
SDLActivity.mGLMinor = minorVersion;
|
2012-11-04 08:51:43 -08:00
|
|
|
}
|
|
|
|
return SDLActivity.createEGLSurface();
|
2012-01-08 01:05:25 -05:00
|
|
|
|
2012-11-04 08:51:43 -08:00
|
|
|
} catch(Exception e) {
|
|
|
|
Log.v("SDL", e + "");
|
|
|
|
for (StackTraceElement s : e.getStackTrace()) {
|
|
|
|
Log.v("SDL", s.toString());
|
2012-01-08 01:05:25 -05:00
|
|
|
}
|
2012-11-04 08:51:43 -08:00
|
|
|
return false;
|
2012-01-08 01:05:25 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static boolean createEGLContext() {
|
|
|
|
EGL10 egl = (EGL10)EGLContext.getEGL();
|
|
|
|
int EGL_CONTEXT_CLIENT_VERSION=0x3098;
|
|
|
|
int contextAttrs[] = new int[] { EGL_CONTEXT_CLIENT_VERSION, SDLActivity.mGLMajor, EGL10.EGL_NONE };
|
|
|
|
SDLActivity.mEGLContext = egl.eglCreateContext(SDLActivity.mEGLDisplay, SDLActivity.mEGLConfig, EGL10.EGL_NO_CONTEXT, contextAttrs);
|
|
|
|
if (SDLActivity.mEGLContext == EGL10.EGL_NO_CONTEXT) {
|
|
|
|
Log.e("SDL", "Couldn't create context");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static boolean createEGLSurface() {
|
|
|
|
if (SDLActivity.mEGLDisplay != null && SDLActivity.mEGLConfig != null) {
|
|
|
|
EGL10 egl = (EGL10)EGLContext.getEGL();
|
|
|
|
if (SDLActivity.mEGLContext == null) createEGLContext();
|
|
|
|
|
|
|
|
Log.v("SDL", "Creating new EGL Surface");
|
|
|
|
EGLSurface surface = egl.eglCreateWindowSurface(SDLActivity.mEGLDisplay, SDLActivity.mEGLConfig, SDLActivity.mSurface, null);
|
|
|
|
if (surface == EGL10.EGL_NO_SURFACE) {
|
|
|
|
Log.e("SDL", "Couldn't create surface");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-06-19 13:57:42 -03:00
|
|
|
if (egl.eglGetCurrentContext() != SDLActivity.mEGLContext) {
|
2012-01-08 01:05:25 -05:00
|
|
|
if (!egl.eglMakeCurrent(SDLActivity.mEGLDisplay, surface, surface, SDLActivity.mEGLContext)) {
|
2012-06-19 13:57:42 -03:00
|
|
|
Log.e("SDL", "Old EGL Context doesnt work, trying with a new one");
|
|
|
|
// TODO: Notify the user via a message that the old context could not be restored, and that textures need to be manually restored.
|
|
|
|
createEGLContext();
|
|
|
|
if (!egl.eglMakeCurrent(SDLActivity.mEGLDisplay, surface, surface, SDLActivity.mEGLContext)) {
|
|
|
|
Log.e("SDL", "Failed making EGL Context current");
|
|
|
|
return false;
|
|
|
|
}
|
2012-01-08 01:05:25 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
SDLActivity.mEGLSurface = surface;
|
|
|
|
return true;
|
2012-11-04 08:51:43 -08:00
|
|
|
} else {
|
|
|
|
Log.e("SDL", "Surface creation failed, display = " + SDLActivity.mEGLDisplay + ", config = " + SDLActivity.mEGLConfig);
|
|
|
|
return false;
|
2012-01-08 01:05:25 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// EGL buffer flip
|
|
|
|
public static void flipEGL() {
|
|
|
|
try {
|
|
|
|
EGL10 egl = (EGL10)EGLContext.getEGL();
|
|
|
|
|
|
|
|
egl.eglWaitNative(EGL10.EGL_CORE_NATIVE_ENGINE, null);
|
|
|
|
|
|
|
|
// drawing here
|
|
|
|
|
|
|
|
egl.eglWaitGL();
|
|
|
|
|
|
|
|
egl.eglSwapBuffers(SDLActivity.mEGLDisplay, SDLActivity.mEGLSurface);
|
|
|
|
|
|
|
|
|
|
|
|
} catch(Exception e) {
|
|
|
|
Log.v("SDL", "flipEGL(): " + e);
|
|
|
|
for (StackTraceElement s : e.getStackTrace()) {
|
|
|
|
Log.v("SDL", s.toString());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-13 11:14:20 -08:00
|
|
|
// Audio
|
2013-01-08 08:28:39 -03:00
|
|
|
public static void audioInit(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) {
|
2011-01-13 11:14:20 -08:00
|
|
|
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") + " " + ((float)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);
|
|
|
|
|
|
|
|
mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
|
|
|
|
channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM);
|
|
|
|
|
|
|
|
audioStartThread();
|
|
|
|
|
|
|
|
Log.v("SDL", "SDL audio: got " + ((mAudioTrack.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioTrack.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + ((float)mAudioTrack.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void audioStartThread() {
|
|
|
|
mAudioThread = new Thread(new Runnable() {
|
|
|
|
public void run() {
|
|
|
|
mAudioTrack.play();
|
|
|
|
nativeRunAudioThread();
|
|
|
|
}
|
|
|
|
});
|
2011-01-06 17:12:31 -08:00
|
|
|
|
2011-01-13 11:14:20 -08:00
|
|
|
// I'd take REALTIME if I could get it!
|
|
|
|
mAudioThread.setPriority(Thread.MAX_PRIORITY);
|
|
|
|
mAudioThread.start();
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void audioWriteShortBuffer(short[] buffer) {
|
|
|
|
for (int i = 0; i < buffer.length; ) {
|
|
|
|
int result = mAudioTrack.write(buffer, i, buffer.length - i);
|
|
|
|
if (result > 0) {
|
|
|
|
i += result;
|
|
|
|
} else if (result == 0) {
|
|
|
|
try {
|
2011-01-13 12:32:55 -08:00
|
|
|
Thread.sleep(1);
|
2011-01-13 11:14:20 -08:00
|
|
|
} catch(InterruptedException e) {
|
|
|
|
// Nom nom
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Log.w("SDL", "SDL audio: error return from write(short)");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void audioWriteByteBuffer(byte[] buffer) {
|
|
|
|
for (int i = 0; i < buffer.length; ) {
|
|
|
|
int result = mAudioTrack.write(buffer, i, buffer.length - i);
|
|
|
|
if (result > 0) {
|
|
|
|
i += result;
|
|
|
|
} else if (result == 0) {
|
|
|
|
try {
|
2011-01-13 12:32:55 -08:00
|
|
|
Thread.sleep(1);
|
2011-01-13 11:14:20 -08:00
|
|
|
} catch(InterruptedException e) {
|
|
|
|
// Nom nom
|
|
|
|
}
|
|
|
|
} else {
|
2013-04-27 13:33:05 +02:00
|
|
|
Log.w("SDL", "SDL audio: error return from write(byte)");
|
2011-01-13 11:14:20 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2011-01-06 17:12:31 -08:00
|
|
|
}
|
|
|
|
|
2011-01-13 12:32:55 -08:00
|
|
|
public static void audioQuit() {
|
|
|
|
if (mAudioThread != null) {
|
|
|
|
try {
|
|
|
|
mAudioThread.join();
|
|
|
|
} catch(Exception e) {
|
|
|
|
Log.v("SDL", "Problem stopping audio thread: " + e);
|
|
|
|
}
|
|
|
|
mAudioThread = null;
|
|
|
|
|
2011-01-13 15:10:17 -08:00
|
|
|
//Log.v("SDL", "Finished waiting for audio thread");
|
2011-01-13 12:32:55 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (mAudioTrack != null) {
|
|
|
|
mAudioTrack.stop();
|
|
|
|
mAudioTrack = null;
|
|
|
|
}
|
|
|
|
}
|
2011-01-06 17:12:31 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Simple nativeInit() runnable
|
|
|
|
*/
|
2011-01-12 14:29:01 -08:00
|
|
|
class SDLMain implements Runnable {
|
|
|
|
public void run() {
|
|
|
|
// Runs SDL_main()
|
2011-01-06 17:12:31 -08:00
|
|
|
SDLActivity.nativeInit();
|
|
|
|
|
2011-01-12 17:53:06 -08:00
|
|
|
//Log.v("SDL", "SDL thread terminated");
|
2011-01-06 17:12:31 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
SDLSurface. This is what we draw on, so we need to know when it's created
|
|
|
|
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,
|
|
|
|
View.OnKeyListener, View.OnTouchListener, SensorEventListener {
|
|
|
|
|
2011-01-12 14:29:01 -08:00
|
|
|
// Sensors
|
2011-01-06 17:12:31 -08:00
|
|
|
private static SensorManager mSensorManager;
|
|
|
|
|
2012-08-24 13:10:29 -03:00
|
|
|
// Keep track of the surface size to normalize touch events
|
|
|
|
private static float mWidth, mHeight;
|
|
|
|
|
2011-01-12 14:29:01 -08:00
|
|
|
// Startup
|
2011-01-06 17:12:31 -08:00
|
|
|
public SDLSurface(Context context) {
|
|
|
|
super(context);
|
|
|
|
getHolder().addCallback(this);
|
|
|
|
|
|
|
|
setFocusable(true);
|
|
|
|
setFocusableInTouchMode(true);
|
|
|
|
requestFocus();
|
|
|
|
setOnKeyListener(this);
|
2013-04-01 13:20:22 -03:00
|
|
|
setOnTouchListener(this);
|
2011-01-12 14:29:01 -08:00
|
|
|
|
2012-08-24 13:10:29 -03:00
|
|
|
mSensorManager = (SensorManager)context.getSystemService("sensor");
|
|
|
|
|
|
|
|
// Some arbitrary defaults to avoid a potential division by zero
|
|
|
|
mWidth = 1.0f;
|
|
|
|
mHeight = 1.0f;
|
2011-01-06 17:12:31 -08:00
|
|
|
}
|
|
|
|
|
2011-01-12 14:29:01 -08:00
|
|
|
// Called when we have a valid drawing surface
|
2011-01-06 17:12:31 -08:00
|
|
|
public void surfaceCreated(SurfaceHolder holder) {
|
2012-01-08 01:05:25 -05:00
|
|
|
Log.v("SDL", "surfaceCreated()");
|
|
|
|
holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
|
2011-01-12 17:53:06 -08:00
|
|
|
enableSensor(Sensor.TYPE_ACCELEROMETER, true);
|
2011-01-06 17:12:31 -08:00
|
|
|
}
|
|
|
|
|
2011-01-12 14:29:01 -08:00
|
|
|
// Called when we lose the surface
|
2011-01-06 17:12:31 -08:00
|
|
|
public void surfaceDestroyed(SurfaceHolder holder) {
|
2012-01-08 01:05:25 -05:00
|
|
|
Log.v("SDL", "surfaceDestroyed()");
|
2012-06-24 21:10:17 -03:00
|
|
|
if (!SDLActivity.mIsPaused) {
|
|
|
|
SDLActivity.mIsPaused = true;
|
|
|
|
SDLActivity.nativePause();
|
|
|
|
}
|
2011-01-12 17:53:06 -08:00
|
|
|
enableSensor(Sensor.TYPE_ACCELEROMETER, false);
|
2011-01-06 17:12:31 -08:00
|
|
|
}
|
|
|
|
|
2011-01-12 14:29:01 -08:00
|
|
|
// Called when the surface is resized
|
|
|
|
public void surfaceChanged(SurfaceHolder holder,
|
|
|
|
int format, int width, int height) {
|
2012-01-08 01:05:25 -05:00
|
|
|
Log.v("SDL", "surfaceChanged()");
|
2011-01-12 14:29:01 -08:00
|
|
|
|
2013-03-03 21:52:51 -08:00
|
|
|
int sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565 by default
|
2011-01-12 14:29:01 -08:00
|
|
|
switch (format) {
|
|
|
|
case PixelFormat.A_8:
|
2011-01-12 17:53:06 -08:00
|
|
|
Log.v("SDL", "pixel format A_8");
|
2011-01-12 14:29:01 -08:00
|
|
|
break;
|
|
|
|
case PixelFormat.LA_88:
|
2011-01-12 17:53:06 -08:00
|
|
|
Log.v("SDL", "pixel format LA_88");
|
2011-01-12 14:29:01 -08:00
|
|
|
break;
|
|
|
|
case PixelFormat.L_8:
|
2011-01-12 17:53:06 -08:00
|
|
|
Log.v("SDL", "pixel format L_8");
|
2011-01-12 14:29:01 -08:00
|
|
|
break;
|
|
|
|
case PixelFormat.RGBA_4444:
|
2011-01-12 17:53:06 -08:00
|
|
|
Log.v("SDL", "pixel format RGBA_4444");
|
2013-03-03 21:52:51 -08:00
|
|
|
sdlFormat = 0x15421002; // SDL_PIXELFORMAT_RGBA4444
|
2011-01-12 14:29:01 -08:00
|
|
|
break;
|
|
|
|
case PixelFormat.RGBA_5551:
|
2011-01-12 17:53:06 -08:00
|
|
|
Log.v("SDL", "pixel format RGBA_5551");
|
2013-03-03 21:52:51 -08:00
|
|
|
sdlFormat = 0x15441002; // SDL_PIXELFORMAT_RGBA5551
|
2011-01-12 14:29:01 -08:00
|
|
|
break;
|
|
|
|
case PixelFormat.RGBA_8888:
|
2011-01-12 17:53:06 -08:00
|
|
|
Log.v("SDL", "pixel format RGBA_8888");
|
2013-03-03 21:52:51 -08:00
|
|
|
sdlFormat = 0x16462004; // SDL_PIXELFORMAT_RGBA8888
|
2011-01-12 14:29:01 -08:00
|
|
|
break;
|
|
|
|
case PixelFormat.RGBX_8888:
|
2011-01-12 17:53:06 -08:00
|
|
|
Log.v("SDL", "pixel format RGBX_8888");
|
2013-03-03 21:52:51 -08:00
|
|
|
sdlFormat = 0x16261804; // SDL_PIXELFORMAT_RGBX8888
|
2011-01-12 14:29:01 -08:00
|
|
|
break;
|
|
|
|
case PixelFormat.RGB_332:
|
2011-01-12 17:53:06 -08:00
|
|
|
Log.v("SDL", "pixel format RGB_332");
|
2013-03-03 21:52:51 -08:00
|
|
|
sdlFormat = 0x14110801; // SDL_PIXELFORMAT_RGB332
|
2011-01-12 14:29:01 -08:00
|
|
|
break;
|
|
|
|
case PixelFormat.RGB_565:
|
2011-01-12 17:53:06 -08:00
|
|
|
Log.v("SDL", "pixel format RGB_565");
|
2013-03-03 21:52:51 -08:00
|
|
|
sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565
|
2011-01-12 14:29:01 -08:00
|
|
|
break;
|
|
|
|
case PixelFormat.RGB_888:
|
2011-01-12 17:53:06 -08:00
|
|
|
Log.v("SDL", "pixel format RGB_888");
|
2011-01-12 14:29:01 -08:00
|
|
|
// Not sure this is right, maybe SDL_PIXELFORMAT_RGB24 instead?
|
2013-03-03 21:52:51 -08:00
|
|
|
sdlFormat = 0x16161804; // SDL_PIXELFORMAT_RGB888
|
2011-01-12 14:29:01 -08:00
|
|
|
break;
|
2011-01-12 19:33:29 -08:00
|
|
|
default:
|
|
|
|
Log.v("SDL", "pixel format unknown " + format);
|
|
|
|
break;
|
2011-01-12 14:29:01 -08:00
|
|
|
}
|
2012-08-24 13:10:29 -03:00
|
|
|
|
|
|
|
mWidth = (float) width;
|
|
|
|
mHeight = (float) height;
|
2011-01-12 16:35:03 -08:00
|
|
|
SDLActivity.onNativeResize(width, height, sdlFormat);
|
2012-01-08 01:05:25 -05:00
|
|
|
Log.v("SDL", "Window size:" + width + "x"+height);
|
2011-01-12 14:29:01 -08:00
|
|
|
|
2012-01-08 01:05:25 -05:00
|
|
|
SDLActivity.startApp();
|
2011-01-06 17:12:31 -08:00
|
|
|
}
|
|
|
|
|
2011-01-12 14:53:23 -08:00
|
|
|
// unused
|
2011-01-06 17:12:31 -08:00
|
|
|
public void onDraw(Canvas canvas) {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2011-01-12 14:29:01 -08:00
|
|
|
// Key events
|
2011-01-12 17:53:06 -08:00
|
|
|
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
2013-04-01 13:20:22 -03:00
|
|
|
|
|
|
|
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;
|
2011-01-06 17:12:31 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-01-12 14:29:01 -08:00
|
|
|
// Touch events
|
|
|
|
public boolean onTouch(View v, MotionEvent event) {
|
2011-10-13 01:21:35 -04:00
|
|
|
{
|
|
|
|
final int touchDevId = event.getDeviceId();
|
|
|
|
final int pointerCount = event.getPointerCount();
|
|
|
|
// touchId, pointerId, action, x, y, pressure
|
2012-08-03 23:59:05 -04:00
|
|
|
int actionPointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent. ACTION_POINTER_ID_SHIFT; /* API 8: event.getActionIndex(); */
|
2011-10-13 01:21:35 -04:00
|
|
|
int pointerFingerId = event.getPointerId(actionPointerIndex);
|
2012-08-03 23:59:05 -04:00
|
|
|
int action = (event.getAction() & MotionEvent.ACTION_MASK); /* API 8: event.getActionMasked(); */
|
2011-10-13 01:21:35 -04:00
|
|
|
|
2012-08-24 13:10:29 -03:00
|
|
|
float x = event.getX(actionPointerIndex) / mWidth;
|
|
|
|
float y = event.getY(actionPointerIndex) / mHeight;
|
2011-10-13 01:21:35 -04:00
|
|
|
float p = event.getPressure(actionPointerIndex);
|
|
|
|
|
|
|
|
if (action == MotionEvent.ACTION_MOVE && pointerCount > 1) {
|
|
|
|
// TODO send motion to every pointer if its position has
|
|
|
|
// changed since prev event.
|
|
|
|
for (int i = 0; i < pointerCount; i++) {
|
|
|
|
pointerFingerId = event.getPointerId(i);
|
2012-08-24 13:10:29 -03:00
|
|
|
x = event.getX(i) / mWidth;
|
|
|
|
y = event.getY(i) / mHeight;
|
2011-10-13 01:21:35 -04:00
|
|
|
p = event.getPressure(i);
|
|
|
|
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
2013-04-01 13:20:22 -03:00
|
|
|
}
|
2011-01-06 17:12:31 -08:00
|
|
|
|
2011-01-12 14:29:01 -08:00
|
|
|
// Sensor events
|
|
|
|
public void enableSensor(int sensortype, boolean enabled) {
|
2011-01-12 14:53:23 -08:00
|
|
|
// TODO: This uses getDefaultSensor - what if we have >1 accels?
|
2011-01-12 14:29:01 -08:00
|
|
|
if (enabled) {
|
2011-01-06 17:12:31 -08:00
|
|
|
mSensorManager.registerListener(this,
|
|
|
|
mSensorManager.getDefaultSensor(sensortype),
|
|
|
|
SensorManager.SENSOR_DELAY_GAME, null);
|
2011-01-12 14:29:01 -08:00
|
|
|
} else {
|
2011-01-06 17:12:31 -08:00
|
|
|
mSensorManager.unregisterListener(this,
|
|
|
|
mSensorManager.getDefaultSensor(sensortype));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-12 14:29:01 -08:00
|
|
|
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
2011-01-12 14:53:23 -08:00
|
|
|
// TODO
|
2011-01-06 17:12:31 -08:00
|
|
|
}
|
|
|
|
|
2011-01-12 14:29:01 -08:00
|
|
|
public void onSensorChanged(SensorEvent event) {
|
|
|
|
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
|
2012-01-13 20:57:35 -05:00
|
|
|
SDLActivity.onNativeAccel(event.values[0] / SensorManager.GRAVITY_EARTH,
|
|
|
|
event.values[1] / SensorManager.GRAVITY_EARTH,
|
|
|
|
event.values[2] / SensorManager.GRAVITY_EARTH);
|
2011-01-06 17:12:31 -08:00
|
|
|
}
|
|
|
|
}
|
2013-04-01 13:20:22 -03:00
|
|
|
|
2012-10-03 20:49:16 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* This is a fake invisible editor view that receives the input and defines the
|
|
|
|
* pan&scan region
|
|
|
|
*/
|
|
|
|
class DummyEdit extends View implements View.OnKeyListener {
|
|
|
|
InputConnection ic;
|
|
|
|
|
|
|
|
public DummyEdit(Context context) {
|
|
|
|
super(context);
|
|
|
|
setFocusableInTouchMode(true);
|
|
|
|
setFocusable(true);
|
|
|
|
setOnKeyListener(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean onCheckIsTextEditor() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
|
|
|
|
|
|
|
// This handles the hardware keyboard input
|
|
|
|
if (event.isPrintingKey()) {
|
|
|
|
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
|
|
|
ic.commitText(String.valueOf((char) event.getUnicodeChar()), 1);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
|
|
|
SDLActivity.onNativeKeyDown(keyCode);
|
|
|
|
return true;
|
|
|
|
} else if (event.getAction() == KeyEvent.ACTION_UP) {
|
|
|
|
SDLActivity.onNativeKeyUp(keyCode);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
|
|
|
|
ic = new SDLInputConnection(this, true);
|
|
|
|
|
|
|
|
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI
|
2012-11-02 02:37:49 -07:00
|
|
|
| 33554432 /* API 11: EditorInfo.IME_FLAG_NO_FULLSCREEN */;
|
2012-10-03 20:49:16 -07:00
|
|
|
|
|
|
|
return ic;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class SDLInputConnection extends BaseInputConnection {
|
|
|
|
|
|
|
|
public SDLInputConnection(View targetView, boolean fullEditor) {
|
|
|
|
super(targetView, fullEditor);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean sendKeyEvent(KeyEvent event) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This handles the keycodes from soft keyboard (and IME-translated
|
|
|
|
* input from hardkeyboard)
|
|
|
|
*/
|
|
|
|
int keyCode = event.getKeyCode();
|
|
|
|
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
2013-01-26 12:31:55 -03:00
|
|
|
if (event.isPrintingKey()) {
|
|
|
|
commitText(String.valueOf((char) event.getUnicodeChar()), 1);
|
|
|
|
}
|
2012-10-03 20:49:16 -07:00
|
|
|
SDLActivity.onNativeKeyDown(keyCode);
|
|
|
|
return true;
|
|
|
|
} else if (event.getAction() == KeyEvent.ACTION_UP) {
|
|
|
|
|
|
|
|
SDLActivity.onNativeKeyUp(keyCode);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return super.sendKeyEvent(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean commitText(CharSequence text, int newCursorPosition) {
|
|
|
|
|
|
|
|
nativeCommitText(text.toString(), newCursorPosition);
|
|
|
|
|
|
|
|
return super.commitText(text, newCursorPosition);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean setComposingText(CharSequence text, int newCursorPosition) {
|
|
|
|
|
|
|
|
nativeSetComposingText(text.toString(), newCursorPosition);
|
|
|
|
|
|
|
|
return super.setComposingText(text, newCursorPosition);
|
|
|
|
}
|
|
|
|
|
|
|
|
public native void nativeCommitText(String text, int newCursorPosition);
|
|
|
|
|
|
|
|
public native void nativeSetComposingText(String text, int newCursorPosition);
|
2011-01-06 17:12:31 -08:00
|
|
|
|
|
|
|
}
|