diff --git a/CMakeLists.txt b/CMakeLists.txt index e1ab286ec..b5e560f03 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -247,7 +247,7 @@ endforeach() set_option(VIDEO_COCOA "Use Cocoa video driver" ${APPLE}) set_option(DIRECTX "Use DirectX for Windows audio/video" ${WINDOWS}) set_option(RENDER_D3D "Enable the Direct3D render driver" ${WINDOWS}) -set_option(VIDEO_MX6 "Use Freescale i.MX6 video driver" OFF) +set_option(VIDEO_VIVANTE "Use Vivante EGL video driver" ${UNIX_SYS}) # TODO: We should (should we?) respect cmake's ${BUILD_SHARED_LIBS} flag here # The options below are for compatibility to configure's default behaviour. @@ -679,7 +679,7 @@ if(UNIX AND NOT APPLE) CheckOpenGLX11() CheckOpenGLESX11() CheckWayland() - CheckMX6() + CheckVivante() endif() if(LINUX) diff --git a/android-project/src/org/libsdl/app/SDLActivity.java b/android-project/src/org/libsdl/app/SDLActivity.java index d3b22647a..cb8ef070b 100644 --- a/android-project/src/org/libsdl/app/SDLActivity.java +++ b/android-project/src/org/libsdl/app/SDLActivity.java @@ -17,9 +17,14 @@ import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import android.widget.AbsoluteLayout; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; import android.os.*; import android.util.Log; +import android.util.SparseArray; import android.graphics.*; +import android.graphics.drawable.Drawable; import android.media.*; import android.hardware.*; @@ -33,6 +38,9 @@ public class SDLActivity extends Activity { public static boolean mIsPaused, mIsSurfaceReady, mHasFocus; public static boolean mExitCalledFromJava; + /** If shared libraries (e.g. SDL or the native application) could not be loaded. */ + public static boolean mBrokenLibraries; + // Main components protected static SDLActivity mSingleton; protected static SDLSurface mSurface; @@ -46,16 +54,42 @@ public class SDLActivity extends Activity { // Audio protected static AudioTrack mAudioTrack; + /** + * This method is called by SDL before loading the native shared libraries. + * It can be overridden to provide names of shared libraries to be loaded. + * The default implementation returns the defaults. It never returns null. + * An array returned by a new implementation must at least contain "SDL2". + * Also keep in mind that the order the libraries are loaded may matter. + * @return names of shared libraries to be loaded (e.g. "SDL2", "main"). + */ + protected String[] getLibraries() { + return new String[] { + "SDL2", + // "SDL2_image", + // "SDL2_mixer", + // "SDL2_net", + // "SDL2_ttf", + "main" + }; + } + // Load the .so - static { - System.loadLibrary("SDL2"); - //System.loadLibrary("SDL2_image"); - //System.loadLibrary("SDL2_mixer"); - //System.loadLibrary("SDL2_net"); - //System.loadLibrary("SDL2_ttf"); - System.loadLibrary("main"); + public void loadLibraries() { + for (String lib : getLibraries()) { + System.loadLibrary(lib); + } } + /** + * This method is called by SDL using JNI. + * This method is called by SDL before starting the native application thread. + * It can be overridden to provide the arguments after the application name. + * The default implementation returns an empty array. It never returns null. + * @return arguments for the native application. + */ + protected String[] getArguments() { + return new String[0]; + } public static void initialize() { // The static nature of the singleton and Android quirkyness force us to initialize everything here @@ -68,6 +102,7 @@ public class SDLActivity extends Activity { mSDLThread = null; mAudioTrack = null; mExitCalledFromJava = false; + mBrokenLibraries = false; mIsPaused = false; mIsSurfaceReady = false; mHasFocus = true; @@ -83,6 +118,42 @@ public class SDLActivity extends Activity { // So we can call stuff from static callbacks mSingleton = this; + // Load shared libraries + String errorMsgBrokenLib = ""; + try { + loadLibraries(); + } catch(UnsatisfiedLinkError e) { + System.err.println(e.getMessage()); + mBrokenLibraries = true; + errorMsgBrokenLib = e.getMessage(); + } catch(Exception e) { + System.err.println(e.getMessage()); + mBrokenLibraries = true; + errorMsgBrokenLib = e.getMessage(); + } + + if (mBrokenLibraries) + { + AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this); + dlgAlert.setMessage("An error occurred while trying to start the application. Please try again and/or reinstall." + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + "Error: " + errorMsgBrokenLib); + dlgAlert.setTitle("SDL Error"); + dlgAlert.setPositiveButton("Exit", + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog,int id) { + // if this button is clicked, close current activity + SDLActivity.mSingleton.finish(); + } + }); + dlgAlert.setCancelable(false); + dlgAlert.create().show(); + + return; + } + // Set up the surface mSurface = new SDLSurface(getApplication()); @@ -104,6 +175,11 @@ public class SDLActivity extends Activity { protected void onPause() { Log.v("SDL", "onPause()"); super.onPause(); + + if (SDLActivity.mBrokenLibraries) { + return; + } + SDLActivity.handlePause(); } @@ -111,6 +187,11 @@ public class SDLActivity extends Activity { protected void onResume() { Log.v("SDL", "onResume()"); super.onResume(); + + if (SDLActivity.mBrokenLibraries) { + return; + } + SDLActivity.handleResume(); } @@ -120,6 +201,10 @@ public class SDLActivity extends Activity { super.onWindowFocusChanged(hasFocus); Log.v("SDL", "onWindowFocusChanged(): " + hasFocus); + if (SDLActivity.mBrokenLibraries) { + return; + } + SDLActivity.mHasFocus = hasFocus; if (hasFocus) { SDLActivity.handleResume(); @@ -130,12 +215,25 @@ public class SDLActivity extends Activity { public void onLowMemory() { Log.v("SDL", "onLowMemory()"); super.onLowMemory(); + + if (SDLActivity.mBrokenLibraries) { + return; + } + SDLActivity.nativeLowMemory(); } @Override protected void onDestroy() { Log.v("SDL", "onDestroy()"); + + if (SDLActivity.mBrokenLibraries) { + super.onDestroy(); + // Reset everything in case the user re opens the app + SDLActivity.initialize(); + return; + } + // Send a quit message to the application SDLActivity.mExitCalledFromJava = true; SDLActivity.nativeQuit(); @@ -159,6 +257,11 @@ public class SDLActivity extends Activity { @Override public boolean dispatchKeyEvent(KeyEvent event) { + + if (SDLActivity.mBrokenLibraries) { + return false; + } + int keyCode = event.getKeyCode(); // Ignore certain special keys so they're handled by Android if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || @@ -207,6 +310,7 @@ public class SDLActivity extends Activity { static final int COMMAND_CHANGE_TITLE = 1; static final int COMMAND_UNUSED = 2; static final int COMMAND_TEXTEDIT_HIDE = 3; + static final int COMMAND_SET_KEEP_SCREEN_ON = 5; protected static final int COMMAND_USER = 0x8000; @@ -251,7 +355,18 @@ public class SDLActivity extends Activity { imm.hideSoftInputFromWindow(mTextEdit.getWindowToken(), 0); } break; - + case COMMAND_SET_KEEP_SCREEN_ON: + { + Window window = ((Activity) context).getWindow(); + if (window != null) { + if ((msg.obj instanceof Integer) && (((Integer) msg.obj).intValue() != 0)) { + window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } else { + window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } + } + break; + } default: if ((context instanceof SDLActivity) && !((SDLActivity) context).onUnhandledMessage(msg.arg1, msg.obj)) { Log.e(TAG, "error handling message, command is " + msg.arg1); @@ -272,7 +387,7 @@ public class SDLActivity extends Activity { } // C functions we call - public static native int nativeInit(); + public static native int nativeInit(Object arguments); public static native void nativeLowMemory(); public static native void nativeQuit(); public static native void nativePause(); @@ -583,6 +698,211 @@ public class SDLActivity extends Activity { return fileStream; } + + // Messagebox + + /** Result of current messagebox. Also used for blocking the calling thread. */ + protected final int[] messageboxSelection = new int[1]; + + /** Id of current dialog. */ + protected int dialogs = 0; + + /** + * This method is called by SDL using JNI. + * Shows the messagebox from UI thread and block calling thread. + * buttonFlags, buttonIds and buttonTexts must have same length. + * @param buttonFlags array containing flags for every button. + * @param buttonIds array containing id for every button. + * @param buttonTexts array containing text for every button. + * @param colors null for default or array of length 5 containing colors. + * @return button id or -1. + */ + public int messageboxShowMessageBox( + final int flags, + final String title, + final String message, + final int[] buttonFlags, + final int[] buttonIds, + final String[] buttonTexts, + final int[] colors) { + + messageboxSelection[0] = -1; + + // sanity checks + + if ((buttonFlags.length != buttonIds.length) && (buttonIds.length != buttonTexts.length)) { + return -1; // implementation broken + } + + // collect arguments for Dialog + + final Bundle args = new Bundle(); + args.putInt("flags", flags); + args.putString("title", title); + args.putString("message", message); + args.putIntArray("buttonFlags", buttonFlags); + args.putIntArray("buttonIds", buttonIds); + args.putStringArray("buttonTexts", buttonTexts); + args.putIntArray("colors", colors); + + // trigger Dialog creation on UI thread + + runOnUiThread(new Runnable() { + @Override + public void run() { + showDialog(dialogs++, args); + } + }); + + // block the calling thread + + synchronized (messageboxSelection) { + try { + messageboxSelection.wait(); + } catch (InterruptedException ex) { + ex.printStackTrace(); + return -1; + } + } + + // return selected value + + return messageboxSelection[0]; + } + + @Override + protected Dialog onCreateDialog(int ignore, Bundle args) { + + // TODO set values from "flags" to messagebox dialog + + // get colors + + int[] colors = args.getIntArray("colors"); + int backgroundColor; + int textColor; + int buttonBorderColor; + int buttonBackgroundColor; + int buttonSelectedColor; + if (colors != null) { + int i = -1; + backgroundColor = colors[++i]; + textColor = colors[++i]; + buttonBorderColor = colors[++i]; + buttonBackgroundColor = colors[++i]; + buttonSelectedColor = colors[++i]; + } else { + backgroundColor = Color.TRANSPARENT; + textColor = Color.TRANSPARENT; + buttonBorderColor = Color.TRANSPARENT; + buttonBackgroundColor = Color.TRANSPARENT; + buttonSelectedColor = Color.TRANSPARENT; + } + + // create dialog with title and a listener to wake up calling thread + + final Dialog dialog = new Dialog(this); + dialog.setTitle(args.getString("title")); + dialog.setCancelable(false); + dialog.setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface unused) { + synchronized (messageboxSelection) { + messageboxSelection.notify(); + } + } + }); + + // create text + + TextView message = new TextView(this); + message.setGravity(Gravity.CENTER); + message.setText(args.getString("message")); + if (textColor != Color.TRANSPARENT) { + message.setTextColor(textColor); + } + + // create buttons + + int[] buttonFlags = args.getIntArray("buttonFlags"); + int[] buttonIds = args.getIntArray("buttonIds"); + String[] buttonTexts = args.getStringArray("buttonTexts"); + + final SparseArray