diff -r 48ca0191d7ee android-project/app/src/main/AndroidManifest.xml --- a/android-project/app/src/main/AndroidManifest.xml Wed Jun 03 14:58:38 2020 -0700 +++ b/android-project/app/src/main/AndroidManifest.xml Thu Jun 04 13:34:11 2020 +0200 @@ -69,6 +69,7 @@ @@ -85,6 +86,12 @@ --> + + + diff -r 48ca0191d7ee android-project/app/src/main/java/org/libsdl/app/SDLActivity.java --- a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java Wed Jun 03 14:58:38 2020 -0700 +++ b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java Thu Jun 04 13:34:11 2020 +0200 @@ -35,10 +35,9 @@ /** SDL Activity */ -public class SDLActivity extends Activity implements View.OnSystemUiVisibilityChangeListener { +public class SDLActivity extends Activity { private static final String TAG = "SDL"; - public static boolean mIsResumedCalled, mHasFocus; public static final boolean mHasMultiWindow = (Build.VERSION.SDK_INT >= 24); // Cursor types @@ -65,22 +64,13 @@ protected static int mCurrentOrientation; protected static Locale mCurrentLocale; - // Handle the state of the native layer - public enum NativeState { - INIT, RESUMED, PAUSED - } - - public static NativeState mNextNativeState; - public static NativeState mCurrentNativeState; - /** 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; - protected static View mTextEdit; - protected static boolean mScreenKeyboardShown; + public static View mTextEdit; + public static boolean mScreenKeyboardShown; protected static ViewGroup mLayout; protected static SDLClipboardHandler mClipboardHandler; protected static Hashtable mCursors; @@ -91,6 +81,11 @@ // This is what SDL runs in. It invokes SDL_main(), eventually protected static Thread mSDLThread; + // TODO + public static int getRootWindowID() { + return 1; + } + protected static SDLGenericMotionListener_API12 getMotionListener() { if (mMotionListener == null) { if (Build.VERSION.SDK_INT >= 26) { @@ -170,7 +165,6 @@ // 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 mSingleton = null; - mSurface = null; mTextEdit = null; mLayout = null; mClipboardHandler = null; @@ -178,10 +172,6 @@ mLastCursorID = 0; mSDLThread = null; mBrokenLibraries = false; - mIsResumedCalled = false; - mHasFocus = true; - mNextNativeState = NativeState.INIT; - mCurrentNativeState = NativeState.INIT; } // Setup @@ -249,16 +239,10 @@ mHIDDeviceManager = HIDDeviceManager.acquire(this); - // Set up the surface - mSurface = new SDLSurface(getApplication()); - - mLayout = new RelativeLayout(this); - mLayout.addView(mSurface); - // Get our current screen orientation and pass it down. mCurrentOrientation = SDLActivity.getCurrentOrientation(); // Only record current orientation - SDLActivity.onNativeOrientationChanged(mCurrentOrientation); + SDLActivity.onNativeOrientationChanged(-1, mCurrentOrientation); try { if (Build.VERSION.SDK_INT < 24) { @@ -269,12 +253,6 @@ } catch(Exception ignored) { } - setContentView(mLayout); - - setWindowStyle(false); - - getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(this); - // Get filename from "Open with" of another application Intent intent = getIntent(); if (intent != null && intent.getData() != null) { @@ -284,30 +262,16 @@ SDLActivity.onNativeDropFile(filename); } } + + // This is the entry point to the C app. + // Start up the C app thread and enable sensor input for the first time + // FIXME: Why aren't we enabling sensor input at start? + + mSDLThread = new Thread(new SDLMain(), "SDLThread"); + mSDLThread.start(); } - protected void pauseNativeThread() { - mNextNativeState = NativeState.PAUSED; - mIsResumedCalled = false; - - if (SDLActivity.mBrokenLibraries) { - return; - } - - SDLActivity.handleNativeState(); - } - - protected void resumeNativeThread() { - mNextNativeState = NativeState.RESUMED; - mIsResumedCalled = true; - - if (SDLActivity.mBrokenLibraries) { - return; - } - - SDLActivity.handleNativeState(); - } - + // Events @Override protected void onPause() { @@ -317,9 +281,6 @@ if (mHIDDeviceManager != null) { mHIDDeviceManager.setFrozen(true); } - if (!mHasMultiWindow) { - pauseNativeThread(); - } } @Override @@ -330,8 +291,15 @@ if (mHIDDeviceManager != null) { mHIDDeviceManager.setFrozen(false); } - if (!mHasMultiWindow) { - resumeNativeThread(); + + // After a background/foreground, SDLActivity is brought to front (but it has no UI). + // So bring an existing SDL window to front, instead. + if (SDLWindowActivity.mMapThis.size() != 0) { + Log.v(TAG, "SDLActivity resumes, bring to front some existing window"); + Intent i = new Intent(mSingleton.getApplicationContext(), SDLWindowActivity.class); + i.setFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT); // optionnal + i.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); + mSingleton.startActivity(i); } } @@ -339,18 +307,12 @@ protected void onStop() { Log.v(TAG, "onStop()"); super.onStop(); - if (mHasMultiWindow) { - pauseNativeThread(); - } } @Override protected void onStart() { Log.v(TAG, "onStart()"); super.onStart(); - if (mHasMultiWindow) { - resumeNativeThread(); - } } public static int getCurrentOrientation() { @@ -381,32 +343,6 @@ } @Override - public void onWindowFocusChanged(boolean hasFocus) { - super.onWindowFocusChanged(hasFocus); - Log.v(TAG, "onWindowFocusChanged(): " + hasFocus); - - if (SDLActivity.mBrokenLibraries) { - return; - } - - mHasFocus = hasFocus; - if (hasFocus) { - mNextNativeState = NativeState.RESUMED; - SDLActivity.getMotionListener().reclaimRelativeMouseModeIfNeeded(); - - SDLActivity.handleNativeState(); - nativeFocusChanged(true); - - } else { - nativeFocusChanged(false); - if (!mHasMultiWindow) { - mNextNativeState = NativeState.PAUSED; - SDLActivity.handleNativeState(); - } - } - } - - @Override public void onLowMemory() { Log.v(TAG, "onLowMemory()"); super.onLowMemory(); @@ -433,6 +369,17 @@ protected void onDestroy() { Log.v(TAG, "onDestroy()"); + + Log.v(TAG, "onDestroy() remaining windows=" + SDLWindowActivity.mMapThis.size()); + for (int id : SDLWindowActivity.mMapThis.keySet()) { + destroyWindow(id); + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + if (mHIDDeviceManager != null) { HIDDeviceManager.release(mHIDDeviceManager); mHIDDeviceManager = null; @@ -522,57 +469,6 @@ return super.dispatchKeyEvent(event); } - /* Transition to next state */ - public static void handleNativeState() { - - if (mNextNativeState == mCurrentNativeState) { - // Already in same state, discard. - return; - } - - // Try a transition to init state - if (mNextNativeState == NativeState.INIT) { - - mCurrentNativeState = mNextNativeState; - return; - } - - // Try a transition to paused state - if (mNextNativeState == NativeState.PAUSED) { - if (mSDLThread != null) { - nativePause(); - } - if (mSurface != null) { - mSurface.handlePause(); - } - mCurrentNativeState = mNextNativeState; - return; - } - - // Try a transition to resumed state - if (mNextNativeState == NativeState.RESUMED) { - if (mSurface.mIsSurfaceReady && mHasFocus && mIsResumedCalled) { - if (mSDLThread == null) { - // This is the entry point to the C app. - // Start up the C app thread and enable sensor input for the first time - // FIXME: Why aren't we enabling sensor input at start? - - mSDLThread = new Thread(new SDLMain(), "SDLThread"); - mSurface.enableSensor(Sensor.TYPE_ACCELEROMETER, true); - mSDLThread.start(); - - // No nativeResume(), don't signal Android_ResumeSem - mSurface.handleResume(); - } else { - nativeResume(); - mSurface.handleResume(); - } - - mCurrentNativeState = mNextNativeState; - } - } - } - // Messages from the SDLMain thread static final int COMMAND_CHANGE_TITLE = 1; static final int COMMAND_CHANGE_WINDOW_STYLE = 2; @@ -582,8 +478,6 @@ protected static final int COMMAND_USER = 0x8000; - protected static boolean mFullscreenModeActive; - /** * This method is called by SDL if SDL did not handle a message itself. * This happens if a received message contains an unsupported command. @@ -596,214 +490,34 @@ return false; } - /** - * A Handler class for Messages from native SDL applications. - * It uses current Activities as target (e.g. for the title). - * static to prevent implicit references to enclosing object. - */ - protected static class SDLCommandHandler extends Handler { - @Override - public void handleMessage(Message msg) { - Context context = SDL.getContext(); - if (context == null) { - Log.e(TAG, "error handling message, getContext() returned null"); - return; - } - switch (msg.arg1) { - case COMMAND_CHANGE_TITLE: - if (context instanceof Activity) { - ((Activity) context).setTitle((String)msg.obj); - } else { - Log.e(TAG, "error handling message, getContext() returned no Activity"); - } - break; - case COMMAND_CHANGE_WINDOW_STYLE: - if (Build.VERSION.SDK_INT < 19) { - // This version of Android doesn't support the immersive fullscreen mode - break; - } - if (context instanceof Activity) { - Window window = ((Activity) context).getWindow(); - if (window != null) { - if ((msg.obj instanceof Integer) && (((Integer) msg.obj).intValue() != 0)) { - int flags = View.SYSTEM_UI_FLAG_FULLSCREEN | - View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | - View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | - View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | - View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | - View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.INVISIBLE; - window.getDecorView().setSystemUiVisibility(flags); - window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); - window.clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); - SDLActivity.mFullscreenModeActive = true; - } else { - int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_VISIBLE; - window.getDecorView().setSystemUiVisibility(flags); - window.addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); - window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); - SDLActivity.mFullscreenModeActive = false; - } - } - } else { - Log.e(TAG, "error handling message, getContext() returned no Activity"); - } - break; - case COMMAND_TEXTEDIT_HIDE: - if (mTextEdit != null) { - // Note: On some devices setting view to GONE creates a flicker in landscape. - // Setting the View's sizes to 0 is similar to GONE but without the flicker. - // The sizes will be set to useful values when the keyboard is shown again. - mTextEdit.setLayoutParams(new RelativeLayout.LayoutParams(0, 0)); - - InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(mTextEdit.getWindowToken(), 0); - - mScreenKeyboardShown = false; - - mSurface.requestFocus(); - } - break; - case COMMAND_SET_KEEP_SCREEN_ON: - { - if (context instanceof Activity) { - 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; - } - case COMMAND_CHANGE_SURFACEVIEW_FORMAT: - { - int format = (Integer) msg.obj; - int pf; - - if (SDLActivity.mSurface == null) { - return; - } - - SurfaceHolder holder = SDLActivity.mSurface.getHolder(); - if (holder == null) { - return; - } - - if (format == 1) { - pf = PixelFormat.RGBA_8888; - } else if (format == 2) { - pf = PixelFormat.RGBX_8888; - } else { - pf = PixelFormat.RGB_565; - } - - holder.setFormat(pf); - - break; - } - default: - if ((context instanceof SDLActivity) && !((SDLActivity) context).onUnhandledMessage(msg.arg1, msg.obj)) { - Log.e(TAG, "error handling message, command is " + msg.arg1); - } - } - } - } - - // Handler for the messages - Handler commandHandler = new SDLCommandHandler(); - - // Send a message from the SDLMain thread - boolean sendCommand(int command, Object data) { - Message msg = commandHandler.obtainMessage(); - msg.arg1 = command; - msg.obj = data; - boolean result = commandHandler.sendMessage(msg); - - if ((Build.VERSION.SDK_INT >= 19) && (command == COMMAND_CHANGE_WINDOW_STYLE)) { - // Ensure we don't return until the resize has actually happened, - // or 500ms have passed. - - boolean bShouldWait = false; - - if (data instanceof Integer) { - // Let's figure out if we're already laid out fullscreen or not. - Display display = ((WindowManager)getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); - android.util.DisplayMetrics realMetrics = new android.util.DisplayMetrics(); - display.getRealMetrics( realMetrics ); - - boolean bFullscreenLayout = ((realMetrics.widthPixels == mSurface.getWidth()) && - (realMetrics.heightPixels == mSurface.getHeight())); - - if (((Integer)data).intValue() == 1) { - // If we aren't laid out fullscreen or actively in fullscreen mode already, we're going - // to change size and should wait for surfaceChanged() before we return, so the size - // is right back in native code. If we're already laid out fullscreen, though, we're - // not going to change size even if we change decor modes, so we shouldn't wait for - // surfaceChanged() -- which may not even happen -- and should return immediately. - bShouldWait = !bFullscreenLayout; - } - else { - // If we're laid out fullscreen (even if the status bar and nav bar are present), - // or are actively in fullscreen, we're going to change size and should wait for - // surfaceChanged before we return, so the size is right back in native code. - bShouldWait = bFullscreenLayout; - } - } - - if (bShouldWait && (SDLActivity.getContext() != null)) { - // We'll wait for the surfaceChanged() method, which will notify us - // when called. That way, we know our current size is really the - // size we need, instead of grabbing a size that's still got - // the navigation and/or status bars before they're hidden. - // - // We'll wait for up to half a second, because some devices - // take a surprisingly long time for the surface resize, but - // then we'll just give up and return. - // - synchronized(SDLActivity.getContext()) { - try { - SDLActivity.getContext().wait(500); - } - catch (InterruptedException ie) { - ie.printStackTrace(); - } - } - } - } - - return result; - } - // C functions we call public static native int nativeSetupJNI(); public static native int nativeRunMain(String library, String function, Object arguments); public static native void nativeLowMemory(); public static native void nativeSendQuit(); public static native void nativeQuit(); - public static native void nativePause(); - public static native void nativeResume(); - public static native void nativeFocusChanged(boolean hasFocus); + public static native void nativePause(int windowID); + public static native void nativeResume(int windowID); + public static native void nativeFocusChanged(int windowID, boolean hasFocus); public static native void onNativeDropFile(String filename); - public static native void nativeSetScreenResolution(int surfaceWidth, int surfaceHeight, int deviceWidth, int deviceHeight, int format, float rate); - public static native void onNativeResize(); + public static native void nativeSetScreenResolution(int windowID, int surfaceWidth, int surfaceHeight, int deviceWidth, int deviceHeight, int format, float rate); + public static native void onNativeResize(int windowID); public static native void onNativeKeyDown(int keycode); public static native void onNativeKeyUp(int keycode); public static native boolean onNativeSoftReturnKey(); public static native void onNativeKeyboardFocusLost(); - public static native void onNativeMouse(int button, int action, float x, float y, boolean relative); - public static native void onNativeTouch(int touchDevId, int pointerFingerId, + public static native void onNativeMouse(int windowID, int button, int action, float x, float y, boolean relative); + public static native void onNativeTouch(int windowID, int touchDevId, int pointerFingerId, int action, float x, float y, float p); public static native void onNativeAccel(float x, float y, float z); public static native void onNativeClipboardChanged(); - public static native void onNativeSurfaceCreated(); - public static native void onNativeSurfaceChanged(); - public static native void onNativeSurfaceDestroyed(); + public static native void onNativeSurfaceCreated(int windowID); + public static native void onNativeSurfaceChanged(int windowID); + public static native void onNativeSurfaceDestroyed(int windowID); public static native String nativeGetHint(String name); public static native void nativeSetenv(String name, String value); - public static native void onNativeOrientationChanged(int orientation); + public static native void onNativeOrientationChanged(int windowID, int orientation); public static native void nativeAddTouch(int touchId, String name); public static native void nativePermissionResult(int requestCode, boolean result); public static native void onNativeLocaleChanged(); @@ -811,17 +525,22 @@ /** * This method is called by SDL using JNI. */ - public static boolean setActivityTitle(String title) { - // Called from SDLMain() thread and can't directly affect the view - return mSingleton.sendCommand(COMMAND_CHANGE_TITLE, title); + public static boolean setActivityTitle(int windowID, String title) { + SDLWindowActivity window = SDLWindowActivity.mMapThis.get(windowID); + if (window != null) { + return window.sendCommand(COMMAND_CHANGE_TITLE, title); + } + return false; } /** * This method is called by SDL using JNI. */ - public static void setWindowStyle(boolean fullscreen) { - // Called from SDLMain() thread and can't directly affect the view - mSingleton.sendCommand(COMMAND_CHANGE_WINDOW_STYLE, fullscreen ? 1 : 0); + public static void setWindowStyle(int windowID, boolean fullscreen) { + SDLWindowActivity window = SDLWindowActivity.mMapThis.get(windowID); + if (window != null) { + window.sendCommand(COMMAND_CHANGE_WINDOW_STYLE, fullscreen ? 1 : 0); + } } /** @@ -829,17 +548,17 @@ * This is a static method for JNI convenience, it calls a non-static method * so that is can be overridden */ - public static void setOrientation(int w, int h, boolean resizable, String hint) + public static void setOrientation(int windowID, int w, int h, boolean resizable, String hint) { if (mSingleton != null) { - mSingleton.setOrientationBis(w, h, resizable, hint); + mSingleton.setOrientationBis(windowID, w, h, resizable, hint); } } /** * This can be overridden */ - public void setOrientationBis(int w, int h, boolean resizable, String hint) + public void setOrientationBis(int windowID, int w, int h, boolean resizable, String hint) { int orientation_landscape = -1; int orientation_portrait = -1; @@ -996,10 +715,12 @@ * This method is called by SDL using JNI. */ public static boolean sendMessage(int command, int param) { - if (mSingleton == null) { - return false; + for (SDLWindowActivity window : SDLWindowActivity.mMapThis.values()) { + if (window != null) { + window.sendCommand(command, Integer.valueOf(param)); + } } - return mSingleton.sendCommand(command, Integer.valueOf(param)); + return true; // TODO } /** @@ -1170,7 +891,12 @@ */ public static boolean showTextInput(int x, int y, int w, int h) { // Transfer the task to the main thread as a Runnable - return mSingleton.commandHandler.post(new ShowTextInputTask(x, y, w, h)); + for (SDLWindowActivity window : SDLWindowActivity.mMapThis.values()) { + if (window != null) { + window.commandHandler.post(new ShowTextInputTask(x, y, w, h)); + } + } + return true; // TODO } public static boolean isTextInputEvent(KeyEvent event) { @@ -1186,19 +912,42 @@ /** * This method is called by SDL using JNI. */ - public static Surface getNativeSurface() { - if (SDLActivity.mSurface == null) { - return null; + public static Surface getNativeSurface(int windowID) { + Log.v(TAG, "getNativeSurface windowID=" + windowID); + SDLWindowActivity window = SDLWindowActivity.mMapThis.get(windowID); + if (window != null) { + SDLSurface surface = window.mSurface; + if (surface != null) { + return surface.getNativeSurface(); + } } - return SDLActivity.mSurface.getNativeSurface(); + return null; } /** * This method is called by SDL using JNI. */ - public static void setSurfaceViewFormat(int format) { - mSingleton.sendCommand(COMMAND_CHANGE_SURFACEVIEW_FORMAT, format); - return; + public static boolean isSurfaceReady(int windowID) { + SDLWindowActivity window = SDLWindowActivity.mMapThis.get(windowID); + if (window != null) { + SDLSurface surface = window.mSurface; + if (surface != null) { + return surface.isSurfaceReady(); + } else { + Log.v(TAG, "Surface was destroyed - isSurfaceReady windowID=" + windowID); + } + } + return true; + } + + /** + * This method is called by SDL using JNI. + */ + public static void setSurfaceViewFormat(int windowID, int format) { + SDLWindowActivity window = SDLWindowActivity.mMapThis.get(windowID); + if (window != null) { + window.sendCommand(COMMAND_CHANGE_SURFACEVIEW_FORMAT, format); + } } // Input @@ -1492,32 +1241,6 @@ return dialog; } - private final Runnable rehideSystemUi = new Runnable() { - @Override - public void run() { - int flags = View.SYSTEM_UI_FLAG_FULLSCREEN | - View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | - View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | - View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | - View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | - View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.INVISIBLE; - - SDLActivity.this.getWindow().getDecorView().setSystemUiVisibility(flags); - } - }; - - public void onSystemUiVisibilityChange(int visibility) { - if (SDLActivity.mFullscreenModeActive && ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0 || (visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0)) { - - Handler handler = getWindow().getDecorView().getHandler(); - if (handler != null) { - handler.removeCallbacks(rehideSystemUi); // Prevent a hide loop. - handler.postDelayed(rehideSystemUi, 2000); - } - - } - } - /** * This method is called by SDL using JNI. */ @@ -1565,7 +1288,12 @@ if (Build.VERSION.SDK_INT >= 24) { try { - mSurface.setPointerIcon(mCursors.get(cursorID)); + for (SDLWindowActivity window : SDLWindowActivity.mMapThis.values()) { + SDLSurface surface = window.mSurface; + if (surface != null) { + surface.setPointerIcon(mCursors.get(cursorID)); + } + } } catch (Exception e) { return false; } @@ -1620,7 +1348,12 @@ } if (Build.VERSION.SDK_INT >= 24) { try { - mSurface.setPointerIcon(PointerIcon.getSystemIcon(SDL.getContext(), cursor_type)); + for (SDLWindowActivity window : SDLWindowActivity.mMapThis.values()) { + SDLSurface surface = window.mSurface; + if (surface != null) { + surface.setPointerIcon(PointerIcon.getSystemIcon(SDL.getContext(), cursor_type)); + } + } } catch (Exception e) { return false; } @@ -1653,6 +1386,46 @@ nativePermissionResult(requestCode, false); } } + + /** + * This method is called by SDL using JNI. + */ + public static void createWindow(int windowID) { + Log.v(TAG, "Create activity windowID=" + windowID); + + Intent i = new Intent(mSingleton.getApplicationContext(), SDLWindowActivity.class); + Bundle b = new Bundle(); + b.putInt("windowID", windowID); + i.putExtras(b); + i.setFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT); // optionnal +// i.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); // optionnal + i.setFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); + mSingleton.startActivity(i); + + // Wait for the activity to be created + while (SDLWindowActivity.mMapThis.containsKey(windowID) == false) { + try { + Thread.sleep(10); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + Log.v(TAG, "Create activity windowID=" + windowID + " done!"); + } + + /** + * This method is called by SDL using JNI. + */ + public static void destroyWindow(int windowID) { + Log.v(TAG, "Delete activity windowID=" + windowID); + if (windowID != 1) { + SDLWindowActivity window = SDLWindowActivity.mMapThis.get(windowID); + if (!window.isFinishing()) { + SDLWindowActivity.mMapThis.get(windowID).finish(); + } + SDLWindowActivity.mMapThis.remove(windowID); + } + } } /** @@ -1698,6 +1471,19 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback, View.OnKeyListener, View.OnTouchListener, SensorEventListener { + public int mWindowID; + + public boolean mIsResumedCalled, mHasFocus; + + // Handle the state of the native layer + public enum NativeState { + INIT, RESUMED, PAUSED + } + + public NativeState mNextNativeState; + public NativeState mCurrentNativeState; + + // Sensors protected SensorManager mSensorManager; protected Display mDisplay; @@ -1709,8 +1495,15 @@ public boolean mIsSurfaceReady; // Startup - public SDLSurface(Context context) { + public SDLSurface(Context context, int windowID) { super(context); + + mIsResumedCalled = false; + mHasFocus = false; + mNextNativeState = NativeState.INIT; + mCurrentNativeState = NativeState.INIT; + mIsSurfaceReady = false; + getHolder().addCallback(this); setFocusable(true); @@ -1719,6 +1512,7 @@ setOnKeyListener(this); setOnTouchListener(this); + mWindowID = windowID; mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE); @@ -1727,8 +1521,29 @@ // Some arbitrary defaults to avoid a potential division by zero mWidth = 1.0f; mHeight = 1.0f; + } - mIsSurfaceReady = false; + + protected void pauseNativeThread() { + mNextNativeState = NativeState.PAUSED; + mIsResumedCalled = false; + + if (SDLActivity.mBrokenLibraries) { + return; + } + + handleNativeState(); + } + + protected void resumeNativeThread() { + mNextNativeState = NativeState.RESUMED; + mIsResumedCalled = true; + + if (SDLActivity.mBrokenLibraries) { + return; + } + + handleNativeState(); } public void handlePause() { @@ -1744,6 +1559,53 @@ enableSensor(Sensor.TYPE_ACCELEROMETER, true); } + /* Transition to next state */ + public void handleNativeState() { + + if (mNextNativeState == mCurrentNativeState) { + // Already in same state, discard. + return; + } + + // Try a transition to init state + if (mNextNativeState == NativeState.INIT) { + + mCurrentNativeState = mNextNativeState; + return; + } + + // Try a transition to paused state + if (mNextNativeState == NativeState.PAUSED) { + if (SDLActivity.mSDLThread != null) { + SDLActivity.nativePause(mWindowID); + } + + handlePause(); + mCurrentNativeState = mNextNativeState; + return; + } + + // Try a transition to resumed state + if (mNextNativeState == NativeState.RESUMED) { + if (mIsSurfaceReady && mHasFocus && mIsResumedCalled) { + if (SDLActivity.mSDLThread != null) { + SDLActivity.nativeResume(mWindowID); + } + + handleResume(); + mCurrentNativeState = mNextNativeState; + } + } + } + + public boolean isSurfaceReady() { + if (mIsSurfaceReady && mHasFocus && mIsResumedCalled) { + return true; + } else { + return false; + } + } + public Surface getNativeSurface() { return getHolder().getSurface(); } @@ -1751,28 +1613,28 @@ // Called when we have a valid drawing surface @Override public void surfaceCreated(SurfaceHolder holder) { - Log.v("SDL", "surfaceCreated()"); - SDLActivity.onNativeSurfaceCreated(); + Log.v("SDL", "surfaceCreated() windowID=" + mWindowID); + SDLActivity.onNativeSurfaceCreated(mWindowID); } // Called when we lose the surface @Override public void surfaceDestroyed(SurfaceHolder holder) { - Log.v("SDL", "surfaceDestroyed()"); + Log.v("SDL", "surfaceDestroyed() windowID=" + mWindowID); // Transition to pause, if needed - SDLActivity.mNextNativeState = SDLActivity.NativeState.PAUSED; - SDLActivity.handleNativeState(); + mNextNativeState = NativeState.PAUSED; + handleNativeState(); mIsSurfaceReady = false; - SDLActivity.onNativeSurfaceDestroyed(); + SDLActivity.onNativeSurfaceDestroyed(mWindowID); } // Called when the surface is resized @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - Log.v("SDL", "surfaceChanged()"); + Log.v("SDL", "surfaceChanged() windowID=" + mWindowID); if (SDLActivity.mSingleton == null) { return; @@ -1824,8 +1686,8 @@ Log.v("SDL", "Window size: " + width + "x" + height); Log.v("SDL", "Device size: " + nDeviceWidth + "x" + nDeviceHeight); - SDLActivity.nativeSetScreenResolution(width, height, nDeviceWidth, nDeviceHeight, sdlFormat, mDisplay.getRefreshRate()); - SDLActivity.onNativeResize(); + SDLActivity.nativeSetScreenResolution(mWindowID, width, height, nDeviceWidth, nDeviceHeight, sdlFormat, mDisplay.getRefreshRate()); + SDLActivity.onNativeResize(mWindowID); // Prevent a screen distortion glitch, // for instance when the device is in Landscape and a Portrait App is resumed. @@ -1875,13 +1737,35 @@ } /* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */ - SDLActivity.onNativeSurfaceChanged(); + SDLActivity.onNativeSurfaceChanged(mWindowID); /* Surface is ready */ mIsSurfaceReady = true; - SDLActivity.mNextNativeState = SDLActivity.NativeState.RESUMED; - SDLActivity.handleNativeState(); + mNextNativeState = NativeState.RESUMED; + handleNativeState(); + } + + public void focusChanged(boolean hasFocus) { + if (SDLActivity.mBrokenLibraries) { + return; + } + + mHasFocus = hasFocus; + if (hasFocus) { + mNextNativeState = NativeState.RESUMED; + SDLActivity.getMotionListener().reclaimRelativeMouseModeIfNeeded(); + + handleNativeState(); + SDLActivity.nativeFocusChanged(mWindowID, true); + + } else { + SDLActivity.nativeFocusChanged(mWindowID, false); + if (!SDLActivity.mHasMultiWindow) { + mNextNativeState = NativeState.PAUSED; + handleNativeState(); + } + } } // Key events @@ -1982,7 +1866,7 @@ x = motionListener.getEventX(event); y = motionListener.getEventY(event); - SDLActivity.onNativeMouse(mouseButton, action, x, y, motionListener.inRelativeMode()); + SDLActivity.onNativeMouse(mWindowID, mouseButton, action, x, y, motionListener.inRelativeMode()); } else { switch(action) { case MotionEvent.ACTION_MOVE: @@ -1996,7 +1880,7 @@ // see the documentation of getPressure(i) p = 1.0f; } - SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p); + SDLActivity.onNativeTouch(mWindowID, touchDevId, pointerFingerId, action, x, y, p); } break; @@ -2020,7 +1904,7 @@ // see the documentation of getPressure(i) p = 1.0f; } - SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p); + SDLActivity.onNativeTouch(mWindowID, touchDevId, pointerFingerId, action, x, y, p); break; case MotionEvent.ACTION_CANCEL: @@ -2034,7 +1918,7 @@ // see the documentation of getPressure(i) p = 1.0f; } - SDLActivity.onNativeTouch(touchDevId, pointerFingerId, MotionEvent.ACTION_UP, x, y, p); + SDLActivity.onNativeTouch(mWindowID, touchDevId, pointerFingerId, MotionEvent.ACTION_UP, x, y, p); } break; @@ -2098,7 +1982,7 @@ if (newOrientation != SDLActivity.mCurrentOrientation) { SDLActivity.mCurrentOrientation = newOrientation; - SDLActivity.onNativeOrientationChanged(newOrientation); + SDLActivity.onNativeOrientationChanged(mWindowID, newOrientation); } SDLActivity.onNativeAccel(-x / SensorManager.GRAVITY_EARTH, @@ -2119,14 +2003,14 @@ case MotionEvent.ACTION_SCROLL: x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0); y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0); - SDLActivity.onNativeMouse(0, action, x, y, false); + SDLActivity.onNativeMouse(mWindowID, 0, action, x, y, false); return true; case MotionEvent.ACTION_HOVER_MOVE: case MotionEvent.ACTION_MOVE: x = event.getX(0); y = event.getY(0); - SDLActivity.onNativeMouse(0, action, x, y, true); + SDLActivity.onNativeMouse(mWindowID, 0, action, x, y, true); return true; case MotionEvent.ACTION_BUTTON_PRESS: @@ -2144,7 +2028,7 @@ y = event.getY(0); int button = event.getButtonState(); - SDLActivity.onNativeMouse(button, action, x, y, true); + SDLActivity.onNativeMouse(mWindowID, button, action, x, y, true); return true; } diff -r 48ca0191d7ee android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java --- a/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java Wed Jun 03 14:58:38 2020 -0700 +++ b/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java Thu Jun 04 13:34:11 2020 +0200 @@ -560,14 +560,14 @@ case MotionEvent.ACTION_SCROLL: x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0); y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0); - SDLActivity.onNativeMouse(0, action, x, y, false); + SDLActivity.onNativeMouse(SDLActivity.mSingleton.getRootWindowID(), 0, action, x, y, false); return true; case MotionEvent.ACTION_HOVER_MOVE: x = event.getX(0); y = event.getY(0); - SDLActivity.onNativeMouse(0, action, x, y, false); + SDLActivity.onNativeMouse(SDLActivity.mSingleton.getRootWindowID(), 0, action, x, y, false); return true; default: @@ -625,7 +625,7 @@ if (action == MotionEvent.ACTION_HOVER_MOVE) { float x = event.getAxisValue(MotionEvent.AXIS_RELATIVE_X); float y = event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y); - SDLActivity.onNativeMouse(0, action, x, y, true); + SDLActivity.onNativeMouse(SDLActivity.mSingleton.getRootWindowID(), 0, action, x, y, true); return true; } } @@ -696,13 +696,13 @@ case MotionEvent.ACTION_SCROLL: x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0); y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0); - SDLActivity.onNativeMouse(0, action, x, y, false); + SDLActivity.onNativeMouse(SDLActivity.mSingleton.getRootWindowID(), 0, action, x, y, false); return true; case MotionEvent.ACTION_HOVER_MOVE: x = event.getX(0); y = event.getY(0); - SDLActivity.onNativeMouse(0, action, x, y, false); + SDLActivity.onNativeMouse(SDLActivity.mSingleton.getRootWindowID(), 0, action, x, y, false); return true; default: @@ -716,13 +716,13 @@ case MotionEvent.ACTION_SCROLL: x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0); y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0); - SDLActivity.onNativeMouse(0, action, x, y, false); + SDLActivity.onNativeMouse(SDLActivity.mSingleton.getRootWindowID(), 0, action, x, y, false); return true; case MotionEvent.ACTION_HOVER_MOVE: x = event.getX(0); y = event.getY(0); - SDLActivity.onNativeMouse(0, action, x, y, true); + SDLActivity.onNativeMouse(SDLActivity.mSingleton.getRootWindowID(), 0, action, x, y, true); return true; default: diff -r 48ca0191d7ee android-project/app/src/main/java/org/libsdl/app/SDLWindowActivity.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/android-project/app/src/main/java/org/libsdl/app/SDLWindowActivity.java Thu Jun 04 13:34:11 2020 +0200 @@ -0,0 +1,350 @@ +package org.libsdl.app; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.Locale; +import java.lang.reflect.Method; +import java.lang.Math; + +import android.app.*; +import android.content.*; +import android.content.res.Configuration; +import android.text.InputType; +import android.view.*; +import android.view.inputmethod.BaseInputConnection; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputConnection; +import android.view.inputmethod.InputMethodManager; +import android.widget.RelativeLayout; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.os.*; +import android.util.DisplayMetrics; +import android.util.Log; +import android.util.SparseArray; +import android.graphics.*; +import android.graphics.drawable.Drawable; +import android.hardware.*; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.pm.ApplicationInfo; + +import java.util.HashMap; + +public class SDLWindowActivity extends Activity implements View.OnSystemUiVisibilityChangeListener +{ + public static final boolean mHasMultiWindow = SDLActivity.mHasMultiWindow; + private static final String TAG = "SDLWindow"; + + public static HashMap mMapThis = new HashMap(); + + protected SDLSurface mSurface; + protected ViewGroup mLayout; + protected int mWindowID; + + // Handler for the messages + Handler commandHandler; + + + protected boolean mFullscreenModeActive; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Bundle b = getIntent().getExtras(); + if(b != null) { + mWindowID = b.getInt("windowID"); + } else { + mWindowID = -1; + } + + Log.v(TAG, "onCreate() windowID=" + mWindowID); + + boolean launchedFromHistory = (getIntent().getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY; + if (launchedFromHistory) { + Log.v(TAG, "launchedFromHistory finish() windowID=" + mWindowID); + finish(); + return; + } + + + commandHandler= new SDLCommandHandler(); + + // Set up the surface + mSurface = new SDLSurface(getApplication(), mWindowID); + + mLayout = new RelativeLayout(this); + mLayout.addView(mSurface); + + setContentView(mLayout); + + mMapThis.put(mWindowID, this); + + + SDLActivity.setWindowStyle(mWindowID, false); + + getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(this); + + } + + @Override + protected void onDestroy() { + Log.v(TAG, "onDestroy() windowID=" + mWindowID); + super.onDestroy(); + } + + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + Log.v(TAG, "onWindowFocusChanged() windowID=" + mWindowID + " hasFocus=" + hasFocus); + mSurface.focusChanged(hasFocus); + } + + // Events + @Override + protected void onPause() { + Log.v(TAG, "onPause() windowID=" + mWindowID); + super.onPause(); + if (!mHasMultiWindow) { + mSurface.pauseNativeThread(); + } + } + + @Override + protected void onResume() { + Log.v(TAG, "onResume() windowID=" + mWindowID); + super.onResume(); + if (!mHasMultiWindow) { + mSurface.resumeNativeThread(); + } + } + + @Override + protected void onStop() { + Log.v(TAG, "onStop() windowID=" + mWindowID); + super.onStop(); + if (mHasMultiWindow) { + mSurface.pauseNativeThread(); + } + } + + @Override + protected void onStart() { + Log.v(TAG, "onStart() windowID=" + mWindowID); + super.onStart(); + if (mHasMultiWindow) { + mSurface.resumeNativeThread(); + } + } + + /** + * A Handler class for Messages from native SDL applications. + * It uses current Activities as target (e.g. for the title). + * static to prevent implicit references to enclosing object. + */ + protected class SDLCommandHandler extends Handler { + @Override + public void handleMessage(Message msg) { + Context context = SDL.getContext(); + if (context == null) { + Log.e(TAG, "error handling message, getContext() returned null"); + return; + } + switch (msg.arg1) { + case SDLActivity.COMMAND_CHANGE_TITLE: + if (context instanceof Activity) { + ((Activity) context).setTitle((String)msg.obj); + } else { + Log.e(TAG, "error handling message, getContext() returned no Activity"); + } + break; + case SDLActivity.COMMAND_CHANGE_WINDOW_STYLE: + if (Build.VERSION.SDK_INT < 19) { + // This version of Android doesn't support the immersive fullscreen mode + break; + } + if (context instanceof Activity) { + Window window = ((Activity) context).getWindow(); + if (window != null) { + if ((msg.obj instanceof Integer) && (((Integer) msg.obj).intValue() != 0)) { + int flags = View.SYSTEM_UI_FLAG_FULLSCREEN | + View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | + View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.INVISIBLE; + window.getDecorView().setSystemUiVisibility(flags); + window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + window.clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); + mFullscreenModeActive = true; + } else { + int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_VISIBLE; + window.getDecorView().setSystemUiVisibility(flags); + window.addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); + window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + mFullscreenModeActive = false; + } + } + } else { + Log.e(TAG, "error handling message, getContext() returned no Activity"); + } + break; + case SDLActivity.COMMAND_TEXTEDIT_HIDE: + if (SDLActivity.mSingleton.mTextEdit != null) { + // Note: On some devices setting view to GONE creates a flicker in landscape. + // Setting the View's sizes to 0 is similar to GONE but without the flicker. + // The sizes will be set to useful values when the keyboard is shown again. + SDLActivity.mSingleton.mTextEdit.setLayoutParams(new RelativeLayout.LayoutParams(0, 0)); + + InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(SDLActivity.mSingleton.mTextEdit.getWindowToken(), 0); + + SDLActivity.mSingleton.mScreenKeyboardShown = false; + + mSurface.requestFocus(); + } + break; + case SDLActivity.COMMAND_SET_KEEP_SCREEN_ON: + { + if (context instanceof Activity) { + 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; + } + case SDLActivity.COMMAND_CHANGE_SURFACEVIEW_FORMAT: + { + int format = (Integer) msg.obj; + int pf; + + if (mSurface == null) { + return; + } + + SurfaceHolder holder = mSurface.getHolder(); + if (holder == null) { + return; + } + + if (format == 1) { + pf = PixelFormat.RGBA_8888; + } else if (format == 2) { + pf = PixelFormat.RGBX_8888; + } else { + pf = PixelFormat.RGB_565; + } + + holder.setFormat(pf); + + break; + } + default: + if ((context instanceof SDLActivity) && !((SDLActivity) context).onUnhandledMessage(msg.arg1, msg.obj)) { + Log.e(TAG, "error handling message, command is " + msg.arg1); + } + } + } + } + + // Send a message from the SDLMain thread + boolean sendCommand(int command, Object data) { + Message msg = commandHandler.obtainMessage(); + msg.arg1 = command; + msg.obj = data; + boolean result = commandHandler.sendMessage(msg); + + if ((Build.VERSION.SDK_INT >= 19) && (command == SDLActivity.COMMAND_CHANGE_WINDOW_STYLE)) { + // Ensure we don't return until the resize has actually happened, + // or 500ms have passed. + + boolean bShouldWait = false; + + if (data instanceof Integer) { + // Let's figure out if we're already laid out fullscreen or not. + Display display = ((WindowManager)getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); + android.util.DisplayMetrics realMetrics = new android.util.DisplayMetrics(); + display.getRealMetrics( realMetrics ); + + boolean bFullscreenLayout = ((realMetrics.widthPixels == mSurface.getWidth()) && + (realMetrics.heightPixels == mSurface.getHeight())); + + if (((Integer)data).intValue() == 1) { + // If we aren't laid out fullscreen or actively in fullscreen mode already, we're going + // to change size and should wait for surfaceChanged() before we return, so the size + // is right back in native code. If we're already laid out fullscreen, though, we're + // not going to change size even if we change decor modes, so we shouldn't wait for + // surfaceChanged() -- which may not even happen -- and should return immediately. + bShouldWait = !bFullscreenLayout; + } + else { + // If we're laid out fullscreen (even if the status bar and nav bar are present), + // or are actively in fullscreen, we're going to change size and should wait for + // surfaceChanged before we return, so the size is right back in native code. + bShouldWait = bFullscreenLayout; + } + } + + if (bShouldWait && (SDLActivity.getContext() != null)) { + // We'll wait for the surfaceChanged() method, which will notify us + // when called. That way, we know our current size is really the + // size we need, instead of grabbing a size that's still got + // the navigation and/or status bars before they're hidden. + // + // We'll wait for up to half a second, because some devices + // take a surprisingly long time for the surface resize, but + // then we'll just give up and return. + // + synchronized(SDLActivity.getContext()) { + try { + SDLActivity.getContext().wait(500); + } + catch (InterruptedException ie) { + ie.printStackTrace(); + } + } + } + } + + return result; + } + + + private final Runnable rehideSystemUi = new Runnable() { + @Override + public void run() { + int flags = View.SYSTEM_UI_FLAG_FULLSCREEN | + View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | + View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.INVISIBLE; + + getWindow().getDecorView().setSystemUiVisibility(flags); + } + }; + + public void onSystemUiVisibilityChange(int visibility) { + if (mFullscreenModeActive && ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0 || (visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0)) { + + Handler handler = getWindow().getDecorView().getHandler(); + if (handler != null) { + handler.removeCallbacks(rehideSystemUi); // Prevent a hide loop. + handler.postDelayed(rehideSystemUi, 2000); + } + + } + } +} + + diff -r 48ca0191d7ee src/core/android/SDL_android.c --- a/src/core/android/SDL_android.c Wed Jun 03 14:58:38 2020 -0700 +++ b/src/core/android/SDL_android.c Thu Jun 04 13:34:11 2020 +0200 @@ -79,20 +79,25 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetScreenResolution)( JNIEnv *env, jclass jcls, + jint windowID, jint surfaceWidth, jint surfaceHeight, jint deviceWidth, jint deviceHeight, jint format, jfloat rate); JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeResize)( - JNIEnv *env, jclass cls); + JNIEnv *env, jclass cls, + jint windowID); JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceCreated)( - JNIEnv *env, jclass jcls); + JNIEnv *env, jclass jcls, + jint windowID); JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)( - JNIEnv *env, jclass jcls); + JNIEnv *env, jclass jcls, + jint windowID); JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed)( - JNIEnv *env, jclass jcls); + JNIEnv *env, jclass jcls, + jint windowID); JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyDown)( JNIEnv *env, jclass jcls, @@ -110,11 +115,13 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeTouch)( JNIEnv *env, jclass jcls, + jint windowID, jint touch_device_id_in, jint pointer_finger_id_in, jint action, jfloat x, jfloat y, jfloat p); JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeMouse)( JNIEnv *env, jclass jcls, + jint windowID, jint button, jint action, jfloat x, jfloat y, jboolean relative); JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeAccel)( @@ -137,13 +144,16 @@ JNIEnv *env, jclass cls); JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePause)( - JNIEnv *env, jclass cls); + JNIEnv *env, jclass cls, + jint windowID); JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeResume)( - JNIEnv *env, jclass cls); + JNIEnv *env, jclass cls, + jint windowID); JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeFocusChanged)( - JNIEnv *env, jclass cls, jboolean hasFocus); + JNIEnv *env, jclass cls, + jint windowID, jboolean hasFocus); JNIEXPORT jstring JNICALL SDL_JAVA_INTERFACE(nativeGetHint)( JNIEnv *env, jclass cls, @@ -155,7 +165,7 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeOrientationChanged)( JNIEnv *env, jclass cls, - jint orientation); + jint windowID, jint orientation); JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeAddTouch)( JNIEnv* env, jclass cls, @@ -169,29 +179,29 @@ { "nativeSetupJNI", "()I", SDL_JAVA_INTERFACE(nativeSetupJNI) }, { "nativeRunMain", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)I", SDL_JAVA_INTERFACE(nativeRunMain) }, { "onNativeDropFile", "(Ljava/lang/String;)V", SDL_JAVA_INTERFACE(onNativeDropFile) }, - { "nativeSetScreenResolution", "(IIIIIF)V", SDL_JAVA_INTERFACE(nativeSetScreenResolution) }, - { "onNativeResize", "()V", SDL_JAVA_INTERFACE(onNativeResize) }, - { "onNativeSurfaceCreated", "()V", SDL_JAVA_INTERFACE(onNativeSurfaceCreated) }, - { "onNativeSurfaceChanged", "()V", SDL_JAVA_INTERFACE(onNativeSurfaceChanged) }, - { "onNativeSurfaceDestroyed", "()V", SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed) }, + { "nativeSetScreenResolution", "(IIIIIIF)V", SDL_JAVA_INTERFACE(nativeSetScreenResolution) }, + { "onNativeResize", "(I)V", SDL_JAVA_INTERFACE(onNativeResize) }, + { "onNativeSurfaceCreated", "(I)V", SDL_JAVA_INTERFACE(onNativeSurfaceCreated) }, + { "onNativeSurfaceChanged", "(I)V", SDL_JAVA_INTERFACE(onNativeSurfaceChanged) }, + { "onNativeSurfaceDestroyed", "(I)V", SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed) }, { "onNativeKeyDown", "(I)V", SDL_JAVA_INTERFACE(onNativeKeyDown) }, { "onNativeKeyUp", "(I)V", SDL_JAVA_INTERFACE(onNativeKeyUp) }, { "onNativeSoftReturnKey", "()Z", SDL_JAVA_INTERFACE(onNativeSoftReturnKey) }, { "onNativeKeyboardFocusLost", "()V", SDL_JAVA_INTERFACE(onNativeKeyboardFocusLost) }, - { "onNativeTouch", "(IIIFFF)V", SDL_JAVA_INTERFACE(onNativeTouch) }, - { "onNativeMouse", "(IIFFZ)V", SDL_JAVA_INTERFACE(onNativeMouse) }, + { "onNativeTouch", "(IIIIFFF)V", SDL_JAVA_INTERFACE(onNativeTouch) }, + { "onNativeMouse", "(IIIFFZ)V", SDL_JAVA_INTERFACE(onNativeMouse) }, { "onNativeAccel", "(FFF)V", SDL_JAVA_INTERFACE(onNativeAccel) }, { "onNativeClipboardChanged", "()V", SDL_JAVA_INTERFACE(onNativeClipboardChanged) }, { "nativeLowMemory", "()V", SDL_JAVA_INTERFACE(nativeLowMemory) }, { "onNativeLocaleChanged", "()V", SDL_JAVA_INTERFACE(onNativeLocaleChanged) }, { "nativeSendQuit", "()V", SDL_JAVA_INTERFACE(nativeSendQuit) }, { "nativeQuit", "()V", SDL_JAVA_INTERFACE(nativeQuit) }, - { "nativePause", "()V", SDL_JAVA_INTERFACE(nativePause) }, - { "nativeResume", "()V", SDL_JAVA_INTERFACE(nativeResume) }, - { "nativeFocusChanged", "(Z)V", SDL_JAVA_INTERFACE(nativeFocusChanged) }, + { "nativePause", "(I)V", SDL_JAVA_INTERFACE(nativePause) }, + { "nativeResume", "(I)V", SDL_JAVA_INTERFACE(nativeResume) }, + { "nativeFocusChanged", "(IZ)V", SDL_JAVA_INTERFACE(nativeFocusChanged) }, { "nativeGetHint", "(Ljava/lang/String;)Ljava/lang/String;", SDL_JAVA_INTERFACE(nativeGetHint) }, { "nativeSetenv", "(Ljava/lang/String;Ljava/lang/String;)V", SDL_JAVA_INTERFACE(nativeSetenv) }, - { "onNativeOrientationChanged", "(I)V", SDL_JAVA_INTERFACE(onNativeOrientationChanged) }, + { "onNativeOrientationChanged", "(II)V", SDL_JAVA_INTERFACE(onNativeOrientationChanged) }, { "nativeAddTouch", "(ILjava/lang/String;)V", SDL_JAVA_INTERFACE(nativeAddTouch) }, { "nativePermissionResult", "(IZ)V", SDL_JAVA_INTERFACE(nativePermissionResult) } }; @@ -299,6 +309,8 @@ static jmethodID midClipboardHasText; static jmethodID midClipboardSetText; static jmethodID midCreateCustomCursor; +static jmethodID midCreateWindow; +static jmethodID midDestroyWindow; static jmethodID midGetContext; static jmethodID midGetDisplayDPI; static jmethodID midGetManifestEnvironmentVariables; @@ -307,6 +319,7 @@ static jmethodID midIsAndroidTV; static jmethodID midIsChromebook; static jmethodID midIsDeXMode; +static jmethodID midIsSurfaceReady; static jmethodID midIsScreenKeyboardShown; static jmethodID midIsTablet; static jmethodID midManualBackButton; @@ -550,7 +563,7 @@ __android_log_print(ANDROID_LOG_ERROR, "SDL", "failed to found a JavaVM"); } - /* Use a mutex to prevent concurrency issues between Java Activity and Native thread code, when using 'Android_Window'. + /* Use a mutex to prevent concurrency issues between Java Activities and Native thread code, when accessing shared datas. * (Eg. Java sending Touch events, while native code is destroying the main SDL_Window. ) */ if (Android_ActivityMutex == NULL) { @@ -578,14 +591,17 @@ midClipboardHasText = (*env)->GetStaticMethodID(env, mActivityClass, "clipboardHasText", "()Z"); midClipboardSetText = (*env)->GetStaticMethodID(env, mActivityClass, "clipboardSetText", "(Ljava/lang/String;)V"); midCreateCustomCursor = (*env)->GetStaticMethodID(env, mActivityClass, "createCustomCursor", "([IIIII)I"); + midCreateWindow = (*env)->GetStaticMethodID(env, mActivityClass, "createWindow", "(I)V"); + midDestroyWindow = (*env)->GetStaticMethodID(env, mActivityClass, "destroyWindow", "(I)V"); midGetContext = (*env)->GetStaticMethodID(env, mActivityClass, "getContext","()Landroid/content/Context;"); midGetDisplayDPI = (*env)->GetStaticMethodID(env, mActivityClass, "getDisplayDPI", "()Landroid/util/DisplayMetrics;"); midGetManifestEnvironmentVariables = (*env)->GetStaticMethodID(env, mActivityClass, "getManifestEnvironmentVariables", "()Z"); - midGetNativeSurface = (*env)->GetStaticMethodID(env, mActivityClass, "getNativeSurface","()Landroid/view/Surface;"); + midGetNativeSurface = (*env)->GetStaticMethodID(env, mActivityClass, "getNativeSurface","(I)Landroid/view/Surface;"); midInitTouch = (*env)->GetStaticMethodID(env, mActivityClass, "initTouch", "()V"); midIsAndroidTV = (*env)->GetStaticMethodID(env, mActivityClass, "isAndroidTV","()Z"); midIsChromebook = (*env)->GetStaticMethodID(env, mActivityClass, "isChromebook", "()Z"); midIsDeXMode = (*env)->GetStaticMethodID(env, mActivityClass, "isDeXMode", "()Z"); + midIsSurfaceReady = (*env)->GetStaticMethodID(env, mActivityClass, "isSurfaceReady", "(I)Z"); midIsScreenKeyboardShown = (*env)->GetStaticMethodID(env, mActivityClass, "isScreenKeyboardShown","()Z"); midIsTablet = (*env)->GetStaticMethodID(env, mActivityClass, "isTablet", "()Z"); midManualBackButton = (*env)->GetStaticMethodID(env, mActivityClass, "manualBackButton", "()V"); @@ -593,13 +609,13 @@ midOpenAPKExpansionInputStream = (*env)->GetStaticMethodID(env, mActivityClass, "openAPKExpansionInputStream", "(Ljava/lang/String;)Ljava/io/InputStream;"); midRequestPermission = (*env)->GetStaticMethodID(env, mActivityClass, "requestPermission", "(Ljava/lang/String;I)V"); midSendMessage = (*env)->GetStaticMethodID(env, mActivityClass, "sendMessage", "(II)Z"); - midSetActivityTitle = (*env)->GetStaticMethodID(env, mActivityClass, "setActivityTitle","(Ljava/lang/String;)Z"); + midSetActivityTitle = (*env)->GetStaticMethodID(env, mActivityClass, "setActivityTitle","(ILjava/lang/String;)Z"); midSetCustomCursor = (*env)->GetStaticMethodID(env, mActivityClass, "setCustomCursor", "(I)Z"); - midSetOrientation = (*env)->GetStaticMethodID(env, mActivityClass, "setOrientation","(IIZLjava/lang/String;)V"); + midSetOrientation = (*env)->GetStaticMethodID(env, mActivityClass, "setOrientation","(IIIZLjava/lang/String;)V"); midSetRelativeMouseEnabled = (*env)->GetStaticMethodID(env, mActivityClass, "setRelativeMouseEnabled", "(Z)Z"); - midSetSurfaceViewFormat = (*env)->GetStaticMethodID(env, mActivityClass, "setSurfaceViewFormat","(I)V"); + midSetSurfaceViewFormat = (*env)->GetStaticMethodID(env, mActivityClass, "setSurfaceViewFormat","(II)V"); midSetSystemCursor = (*env)->GetStaticMethodID(env, mActivityClass, "setSystemCursor", "(I)Z"); - midSetWindowStyle = (*env)->GetStaticMethodID(env, mActivityClass, "setWindowStyle","(Z)V"); + midSetWindowStyle = (*env)->GetStaticMethodID(env, mActivityClass, "setWindowStyle","(IZ)V"); midShouldMinimizeOnFocusLoss = (*env)->GetStaticMethodID(env, mActivityClass, "shouldMinimizeOnFocusLoss","()Z"); midShowTextInput = (*env)->GetStaticMethodID(env, mActivityClass, "showTextInput", "(IIII)Z"); midSupportsRelativeMouse = (*env)->GetStaticMethodID(env, mActivityClass, "supportsRelativeMouse", "()Z"); @@ -608,6 +624,8 @@ !midClipboardHasText || !midClipboardSetText || !midCreateCustomCursor || + !midCreateWindow || + !midDestroyWindow || !midGetContext || !midGetDisplayDPI || !midGetManifestEnvironmentVariables || @@ -616,6 +634,7 @@ !midIsAndroidTV || !midIsChromebook || !midIsDeXMode || + !midIsSurfaceReady || !midIsScreenKeyboardShown || !midIsTablet || !midManualBackButton || @@ -707,14 +726,24 @@ JNIEXPORT int JNICALL SDL_JAVA_INTERFACE(nativeRunMain)(JNIEnv *env, jclass cls, jstring library, jstring function, jobject array) { int status = -1; +#if 0 const char *library_file; void *library_handle; +#endif __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeRunMain()"); /* Save JNIEnv of SDLThread */ Android_JNI_SetEnv(env); +#if 1 + /* Compiling a unique shared library, main() will be added afterward. + * need to be marked as weak */ + { + extern int main(int argc, char **argv) __attribute__((weak)); + status = main(0, NULL); + } +#else library_file = (*env)->GetStringUTFChars(env, library, NULL); library_handle = dlopen(library_file, RTLD_GLOBAL); @@ -789,7 +818,7 @@ __android_log_print(ANDROID_LOG_ERROR, "SDL", "nativeRunMain(): Couldn't load library %s", library_file); } (*env)->ReleaseStringUTFChars(env, library, library_file); - +#endif /* This is a Java thread, it doesn't need to be Detached from the JVM. * Set to mThreadKey value to NULL not to call pthread_create destructor 'Android_JNI_ThreadDestroyed' */ Android_JNI_SetEnv(NULL); @@ -811,6 +840,13 @@ SDL_SendDropComplete(NULL); } + +static SDL_bool SDL_IsSurfaceReady(int windowID) +{ + JNIEnv *env = Android_JNI_GetEnv(); + return (*env)->CallStaticBooleanMethod(env, mActivityClass, midIsSurfaceReady, windowID); +} + /* Lock / Unlock Mutex */ void Android_ActivityMutex_Lock() { SDL_LockMutex(Android_ActivityMutex); @@ -821,65 +857,66 @@ } /* Lock the Mutex when the Activity is in its 'Running' state */ -void Android_ActivityMutex_Lock_Running() { - int pauseSignaled = 0; - int resumeSignaled = 0; - +void Android_ActivityMutex_Lock_Running(SDL_Window *window) { retry: - SDL_LockMutex(Android_ActivityMutex); - - pauseSignaled = SDL_SemValue(Android_PauseSem); - resumeSignaled = SDL_SemValue(Android_ResumeSem); - - if (pauseSignaled > resumeSignaled) { - SDL_UnlockMutex(Android_ActivityMutex); - SDL_Delay(50); - goto retry; + { + int windowID = SDL_GetWindowID(window); + if (! SDL_IsSurfaceReady(windowID)) { + SDL_UnlockMutex(Android_ActivityMutex); + SDL_Delay(20); + goto retry; + } } } /* Set screen resolution */ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetScreenResolution)( JNIEnv *env, jclass jcls, + jint windowID, jint surfaceWidth, jint surfaceHeight, jint deviceWidth, jint deviceHeight, jint format, jfloat rate) { SDL_LockMutex(Android_ActivityMutex); - - Android_SetScreenResolution(surfaceWidth, surfaceHeight, deviceWidth, deviceHeight, format, rate); - + { + SDL_Window *window = SDL_GetWindowFromID(windowID); + + Android_SetScreenResolution(window, surfaceWidth, surfaceHeight, deviceWidth, deviceHeight, format, rate); + } SDL_UnlockMutex(Android_ActivityMutex); } /* Resize */ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeResize)( - JNIEnv *env, jclass jcls) + JNIEnv *env, jclass jcls, + jint windowID) { SDL_LockMutex(Android_ActivityMutex); - - if (Android_Window) { - Android_SendResize(Android_Window); + SDL_Window *window = SDL_GetWindowFromID(windowID); + + if (window) { + Android_SendResize(window); + } } - SDL_UnlockMutex(Android_ActivityMutex); } JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeOrientationChanged)( JNIEnv *env, jclass jcls, - jint orientation) + jint windowID, jint orientation) { SDL_LockMutex(Android_ActivityMutex); - - displayOrientation = (SDL_DisplayOrientation)orientation; - - if (Android_Window) { - SDL_VideoDisplay *display = SDL_GetDisplay(0); - SDL_SendDisplayEvent(display, SDL_DISPLAYEVENT_ORIENTATION, orientation); + SDL_Window *window = SDL_GetWindowFromID(windowID); + + displayOrientation = (SDL_DisplayOrientation)orientation; + + if (window) { + SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window); + SDL_SendDisplayEvent(display, SDL_DISPLAYEVENT_ORIENTATION, orientation); + } } - SDL_UnlockMutex(Android_ActivityMutex); } @@ -980,83 +1017,85 @@ } /* Called from surfaceCreated() */ -JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceCreated)(JNIEnv *env, jclass jcls) +JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceCreated)(JNIEnv *env, jclass jcls, + jint windowID) { SDL_LockMutex(Android_ActivityMutex); - - if (Android_Window) { - SDL_WindowData *data = (SDL_WindowData *) Android_Window->driverdata; - - data->native_window = Android_JNI_GetNativeWindow(); - if (data->native_window == NULL) { - SDL_SetError("Could not fetch native window from UI thread"); + SDL_Window *window = SDL_GetWindowFromID(windowID); + + if (window) { + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + data->native_window = Android_JNI_GetNativeWindow(windowID); + if (data->native_window == NULL) { + SDL_SetError("Could not fetch native window from UI thread"); + } } } - SDL_UnlockMutex(Android_ActivityMutex); } /* Called from surfaceChanged() */ -JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)(JNIEnv *env, jclass jcls) +JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)(JNIEnv *env, jclass jcls, + jint windowID) { SDL_LockMutex(Android_ActivityMutex); - - if (Android_Window) { - SDL_VideoDevice *_this = SDL_GetVideoDevice(); - SDL_WindowData *data = (SDL_WindowData *) Android_Window->driverdata; - - /* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */ - if (data->egl_surface == EGL_NO_SURFACE) { - data->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType) data->native_window); + SDL_Window *window = SDL_GetWindowFromID(windowID); + + if (window) { + SDL_VideoDevice *_this = SDL_GetVideoDevice(); + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + /* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */ + if (data->egl_surface == EGL_NO_SURFACE) { + data->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType) data->native_window); + } + /* GL Context handling is done in the event loop because this function is run from the Java thread */ } - - /* GL Context handling is done in the event loop because this function is run from the Java thread */ } - SDL_UnlockMutex(Android_ActivityMutex); } /* Called from surfaceDestroyed() */ -JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed)(JNIEnv *env, jclass jcls) +JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed)(JNIEnv *env, jclass jcls, + jint windowID) { int nb_attempt = 50; - retry: SDL_LockMutex(Android_ActivityMutex); - - if (Android_Window) { - SDL_VideoDevice *_this = SDL_GetVideoDevice(); - SDL_WindowData *data = (SDL_WindowData *) Android_Window->driverdata; - - /* Wait for Main thread being paused and context un-activated to release 'egl_surface' */ - if (! data->backup_done) { - nb_attempt -= 1; - if (nb_attempt == 0) { - SDL_SetError("Try to release egl_surface with context probably still active"); - } else { - SDL_UnlockMutex(Android_ActivityMutex); - SDL_Delay(10); - goto retry; + SDL_Window *window = SDL_GetWindowFromID(windowID); + if (window) { + SDL_VideoDevice *_this = SDL_GetVideoDevice(); + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + if (data) { + /* Wait for Main thread being paused and context un-activated to release 'egl_surface' */ + if (data->backup_done != 2) { + nb_attempt -= 1; + if (nb_attempt == 0) { + SDL_SetError("Try to release egl_surface with context probably still active. windowID=%d", windowID); + } else { + SDL_UnlockMutex(Android_ActivityMutex); + SDL_Delay(10); + goto retry; + } + } + + if (data->egl_surface != EGL_NO_SURFACE) { + SDL_EGL_DestroySurface(_this, data->egl_surface); + data->egl_surface = EGL_NO_SURFACE; + } + + if (data->native_window) { + ANativeWindow_release(data->native_window); + data->native_window = NULL; + } + + /* GL Context handling is done in the event loop because this function is run from the Java thread */ } } - - if (data->egl_surface != EGL_NO_SURFACE) { - SDL_EGL_DestroySurface(_this, data->egl_surface); - data->egl_surface = EGL_NO_SURFACE; - } - - if (data->native_window) { - ANativeWindow_release(data->native_window); - data->native_window = NULL; - } - - /* GL Context handling is done in the event loop because this function is run from the Java thread */ } - SDL_UnlockMutex(Android_ActivityMutex); } @@ -1099,25 +1138,33 @@ /* Touch */ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeTouch)( JNIEnv *env, jclass jcls, + jint windowID, jint touch_device_id_in, jint pointer_finger_id_in, jint action, jfloat x, jfloat y, jfloat p) { SDL_LockMutex(Android_ActivityMutex); - - Android_OnTouch(Android_Window, touch_device_id_in, pointer_finger_id_in, action, x, y, p); - + { + SDL_Window *window = SDL_GetWindowFromID(windowID); + if (window) { + Android_OnTouch(window, touch_device_id_in, pointer_finger_id_in, action, x, y, p); + } + } SDL_UnlockMutex(Android_ActivityMutex); } /* Mouse */ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeMouse)( JNIEnv *env, jclass jcls, + jint windowID, jint button, jint action, jfloat x, jfloat y, jboolean relative) { SDL_LockMutex(Android_ActivityMutex); - - Android_OnMouse(Android_Window, button, action, x, y, relative); - + { + SDL_Window *window = SDL_GetWindowFromID(windowID); + if (window) { + Android_OnMouse(window, button, action, x, y, relative); + } + } SDL_UnlockMutex(Android_ActivityMutex); } @@ -1208,9 +1255,22 @@ /* Pause */ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePause)( - JNIEnv *env, jclass cls) + JNIEnv *env, jclass cls, + jint windowID) { - __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativePause()"); + __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativePause() windowID=%d", windowID); + + SDL_LockMutex(Android_ActivityMutex); + { + SDL_Window *window = SDL_GetWindowFromID(windowID); + if (window) { + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + if (data) { + data->activity_state = ACTIVITY_PAUSED; + } + } + } + SDL_UnlockMutex(Android_ActivityMutex); /* Signal the pause semaphore so the event loop knows to pause and (optionally) block itself. * Sometimes 2 pauses can be queued (eg pause/resume/pause), so it's always increased. */ @@ -1219,9 +1279,26 @@ /* Resume */ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeResume)( - JNIEnv *env, jclass cls) + JNIEnv *env, jclass cls, + jint windowID) { - __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeResume()"); + __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeResume() windowID=%d", windowID); + + SDL_LockMutex(Android_ActivityMutex); + { + SDL_Window *window = SDL_GetWindowFromID(windowID); + if (window) { + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + if (data) { + data->activity_state = ACTIVITY_RESUMED; + } else { + __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeResume() windowID=%d -- NO DATA", windowID); + } + } else { + __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeResume() windowID=%d -- NO WINDOW", windowID); + } + } + SDL_UnlockMutex(Android_ActivityMutex); /* Signal the resume semaphore so the event loop knows to resume and restore the GL Context * We can't restore the GL Context here because it needs to be done on the SDL main thread @@ -1231,15 +1308,18 @@ } JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeFocusChanged)( - JNIEnv *env, jclass cls, jboolean hasFocus) + JNIEnv *env, jclass cls, + jint windowID, jboolean hasFocus) { SDL_LockMutex(Android_ActivityMutex); - - if (Android_Window) { - __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeFocusChanged()"); - SDL_SendWindowEvent(Android_Window, (hasFocus ? SDL_WINDOWEVENT_FOCUS_GAINED : SDL_WINDOWEVENT_FOCUS_LOST), 0, 0); + { + SDL_Window *window = SDL_GetWindowFromID(windowID); + + if (window) { + __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeFocusChanged() windowID=%d hasFocus=%d", windowID, hasFocus); + SDL_SendWindowEvent(window, (hasFocus ? SDL_WINDOWEVENT_FOCUS_GAINED : SDL_WINDOWEVENT_FOCUS_LOST), 0, 0); + } } - SDL_UnlockMutex(Android_ActivityMutex); } @@ -1367,13 +1447,13 @@ } } -ANativeWindow* Android_JNI_GetNativeWindow(void) +ANativeWindow* Android_JNI_GetNativeWindow(int windowID) { ANativeWindow *anw = NULL; jobject s; JNIEnv *env = Android_JNI_GetEnv(); - s = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetNativeSurface); + s = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetNativeSurface, windowID); if (s) { anw = ANativeWindow_fromSurface(env, s); (*env)->DeleteLocalRef(env, s); @@ -1382,7 +1462,7 @@ return anw; } -void Android_JNI_SetSurfaceViewFormat(int format) +void Android_JNI_SetSurfaceViewFormat(int windowID, int format) { JNIEnv *env = Android_JNI_GetEnv(); int new_format = 0; @@ -1399,30 +1479,30 @@ new_format = 0; } - (*env)->CallStaticVoidMethod(env, mActivityClass, midSetSurfaceViewFormat, new_format); + (*env)->CallStaticVoidMethod(env, mActivityClass, midSetSurfaceViewFormat, windowID, new_format); } -void Android_JNI_SetActivityTitle(const char *title) +void Android_JNI_SetActivityTitle(int windowID, const char *title) { JNIEnv *env = Android_JNI_GetEnv(); jstring jtitle = (*env)->NewStringUTF(env, title); - (*env)->CallStaticBooleanMethod(env, mActivityClass, midSetActivityTitle, jtitle); + (*env)->CallStaticBooleanMethod(env, mActivityClass, midSetActivityTitle, windowID, jtitle); (*env)->DeleteLocalRef(env, jtitle); } -void Android_JNI_SetWindowStyle(SDL_bool fullscreen) +void Android_JNI_SetWindowStyle(int windowID, SDL_bool fullscreen) { JNIEnv *env = Android_JNI_GetEnv(); - (*env)->CallStaticVoidMethod(env, mActivityClass, midSetWindowStyle, fullscreen ? 1 : 0); + (*env)->CallStaticVoidMethod(env, mActivityClass, midSetWindowStyle, windowID, fullscreen ? 1 : 0); } -void Android_JNI_SetOrientation(int w, int h, int resizable, const char *hint) +void Android_JNI_SetOrientation(int windowID, int w, int h, int resizable, const char *hint) { JNIEnv *env = Android_JNI_GetEnv(); jstring jhint = (*env)->NewStringUTF(env, (hint ? hint : "")); - (*env)->CallStaticVoidMethod(env, mActivityClass, midSetOrientation, w, h, (resizable? 1 : 0), jhint); + (*env)->CallStaticVoidMethod(env, mActivityClass, midSetOrientation, windowID, w, h, (resizable? 1 : 0), jhint); (*env)->DeleteLocalRef(env, jhint); } @@ -2819,6 +2899,19 @@ return custom_cursor; } +int Android_JNI_CreateWindow(int windowID) +{ + JNIEnv *env = Android_JNI_GetEnv(); + (*env)->CallStaticVoidMethod(env, mActivityClass, midCreateWindow, windowID); + return 0; +} + +int Android_JNI_DestroyWindow(int windowID) +{ + JNIEnv *env = Android_JNI_GetEnv(); + (*env)->CallStaticVoidMethod(env, mActivityClass, midDestroyWindow, windowID); + return 0; +} SDL_bool Android_JNI_SetCustomCursor(int cursorID) { diff -r 48ca0191d7ee src/core/android/SDL_android.h --- a/src/core/android/SDL_android.h Wed Jun 03 14:58:38 2020 -0700 +++ b/src/core/android/SDL_android.h Thu Jun 04 13:34:11 2020 +0200 @@ -36,9 +36,9 @@ #include "SDL_video.h" /* Interface from the SDL library into the Android Java activity */ -extern void Android_JNI_SetActivityTitle(const char *title); -extern void Android_JNI_SetWindowStyle(SDL_bool fullscreen); -extern void Android_JNI_SetOrientation(int w, int h, int resizable, const char *hint); +extern void Android_JNI_SetActivityTitle(int windowID, const char *title); +extern void Android_JNI_SetWindowStyle(int windowID, SDL_bool fullscreen); +extern void Android_JNI_SetOrientation(int windowID, int w, int h, int resizable, const char *hint); extern void Android_JNI_MinizeWindow(void); extern SDL_bool Android_JNI_ShouldMinimizeOnFocusLoss(void); @@ -46,8 +46,8 @@ extern void Android_JNI_ShowTextInput(SDL_Rect *inputRect); extern void Android_JNI_HideTextInput(void); extern SDL_bool Android_JNI_IsScreenKeyboardShown(void); -extern ANativeWindow* Android_JNI_GetNativeWindow(void); -extern void Android_JNI_SetSurfaceViewFormat(int format); +extern ANativeWindow* Android_JNI_GetNativeWindow(int i); +extern void Android_JNI_SetSurfaceViewFormat(int windowID, int format); extern SDL_DisplayOrientation Android_JNI_GetDisplayOrientation(void); extern int Android_JNI_GetDisplayDPI(float *ddpi, float *xdpi, float *ydpi); @@ -129,6 +129,10 @@ /* Request permission */ SDL_bool Android_JNI_RequestPermission(const char *permission); +/* Multi-SDL_Window */ +int Android_JNI_CreateWindow(int windowID); +int Android_JNI_DestroyWindow(int windowID); + int SDL_GetAndroidSDKVersion(void); SDL_bool SDL_IsAndroidTablet(void); @@ -138,7 +142,7 @@ void Android_ActivityMutex_Lock(void); void Android_ActivityMutex_Unlock(void); -void Android_ActivityMutex_Lock_Running(void); +void Android_ActivityMutex_Lock_Running(SDL_Window *window); /* Ends C function definitions when using C++ */ #ifdef __cplusplus diff -r 48ca0191d7ee src/render/SDL_render.c --- a/src/render/SDL_render.c Wed Jun 03 14:58:38 2020 -0700 +++ b/src/render/SDL_render.c Thu Jun 04 13:34:11 2020 +0200 @@ -773,7 +773,7 @@ const char *hint; #if defined(__ANDROID__) - Android_ActivityMutex_Lock_Running(); + Android_ActivityMutex_Lock_Running(window); #endif if (!window) { diff -r 48ca0191d7ee src/video/SDL_egl.c --- a/src/video/SDL_egl.c Wed Jun 03 14:58:38 2020 -0700 +++ b/src/video/SDL_egl.c Thu Jun 04 13:34:11 2020 +0200 @@ -1099,7 +1099,7 @@ /* Update SurfaceView holder format. * May triggers a sequence surfaceDestroyed(), surfaceCreated(), surfaceChanged(). */ - Android_JNI_SetSurfaceViewFormat(format); + Android_JNI_SetSurfaceViewFormat(-1 /* TODO WindowID */, format); } #endif if (_this->gl_config.framebuffer_srgb_capable) { diff -r 48ca0191d7ee src/video/android/SDL_androidevents.c --- a/src/video/android/SDL_androidevents.c Wed Jun 03 14:58:38 2020 -0700 +++ b/src/video/android/SDL_androidevents.c Thu Jun 04 13:34:11 2020 +0200 @@ -55,34 +55,56 @@ return SDL_PeepEvents(NULL, 0, SDL_PEEKEVENT, type, type); } +static int +SDL_IsMinimizedEventConsummed(int windowID) +{ +#define NB_EVENTS 16 + SDL_Event tab[NB_EVENTS]; + int i; + int cnt = SDL_PeepEvents(tab, NB_EVENTS, SDL_PEEKEVENT, SDL_WINDOWEVENT_MINIMIZED, SDL_WINDOWEVENT_MINIMIZED); + + if (cnt == 0) { + return 1; + } + + for (i = 0; i < cnt && i < NB_EVENTS; i++) { + SDL_Event *evt = &tab[i]; + if (evt->window.windowID == windowID) { + return 0; + } + } + + return 1; +} + + static void android_egl_context_restore(SDL_Window *window) { - if (window) { - SDL_Event event; - SDL_WindowData *data = (SDL_WindowData *) window->driverdata; - if (SDL_GL_MakeCurrent(window, (SDL_GLContext) data->egl_context) < 0) { - /* The context is no longer valid, create a new one */ - data->egl_context = (EGLContext) SDL_GL_CreateContext(window); - SDL_GL_MakeCurrent(window, (SDL_GLContext) data->egl_context); - event.type = SDL_RENDER_DEVICE_RESET; - SDL_PushEvent(&event); - } - data->backup_done = 0; + SDL_Event event; + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + if (SDL_GL_MakeCurrent(window, (SDL_GLContext) data->egl_context) < 0) { + /* The context is no longer valid, create a new one */ + SDL_Log("SDL_RENDER_DEVICE_RESET ..."); + data->egl_context = (EGLContext) SDL_GL_CreateContext(window); + SDL_GL_MakeCurrent(window, (SDL_GLContext) data->egl_context); + event.type = SDL_RENDER_DEVICE_RESET; + SDL_PushEvent(&event); } + data->backup_done = 0; } static void android_egl_context_backup(SDL_Window *window) { - if (window) { - /* Keep a copy of the EGL Context so we can try to restore it when we resume */ - SDL_WindowData *data = (SDL_WindowData *) window->driverdata; - data->egl_context = SDL_GL_GetCurrentContext(); + /* Keep a copy of the EGL Context so we can try to restore it when we resume */ + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + data->egl_context = SDL_GL_GetCurrentContext(); + if (data->egl_context) { /* We need to do this so the EGLSurface can be freed */ SDL_GL_MakeCurrent(window, NULL); - data->backup_done = 1; } + data->backup_done = 2; } @@ -93,74 +115,187 @@ * No polling necessary */ +const static int debug = 1; /* 0, 1, 2 */ + +#define NEXT_STATE(state) \ +{ \ + if (debug) { \ + if (videodata->loop_state != state) { \ + SDL_Log("event loop state " # state); \ + } \ + videodata->loop_state = state; \ + } \ +} + + void Android_PumpEvents_Blocking(_THIS) { SDL_VideoData *videodata = (SDL_VideoData *)_this->driverdata; - if (videodata->isPaused) { - SDL_bool isContextExternal = SDL_IsVideoContextExternal(); + + + if (debug == 2) { + SDL_LockMutex(Android_ActivityMutex); + SDL_Window *window; + SDL_Log("=== Android_PumpEvents_Blocking === Start ==="); + for (window = _this->windows; window; window = window->next) { + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + SDL_Log("loop_state = %d windowID=%d activity_state=%d backup_done=%d", videodata->loop_state, SDL_GetWindowID(window), data->activity_state, data->backup_done); + } + SDL_UnlockMutex(Android_ActivityMutex); + } + + if (videodata->loop_state == LOOP_SENDING_BG_EVENTS) { +#if 0 + /* we need to make sure that the very last event (of the first pause sequence, if several) + * has reached the app */ + if (SDL_NumberOfEvents(SDL_APP_DIDENTERBACKGROUND) > SDL_SemValue(Android_PauseSem)) { + /* Wait for the event to be consumed */ + } else { + NEXT_STATE(LOOP_BLOCKED); + } +#else + if (SDL_NumberOfEvents(SDL_APP_DIDENTERBACKGROUND) == 0) { + NEXT_STATE(LOOP_BLOCKED); + } +#endif + } else if (videodata->loop_state == LOOP_BLOCKED) { + + /* Double-check before blocking, because an activity may have created in the meantimes */ + int all_paused = 1; + +#if 0 + /* Browse all SDL_Window */ + SDL_LockMutex(Android_ActivityMutex); + { + SDL_Window *window; + for (window = _this->windows; window; window = window->next) { + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + if (data->activity_state != ACTIVITY_PAUSED || data->backup_done != 2) { + all_paused = 0; + + if (debug == 1) { + SDL_Log("loop_state: LOOP_BLOCKED (canceled!) windowID=%d activity_state=%d backup_done=%d", + SDL_GetWindowID(window), data->activity_state, data->backup_done); + } + + break; + } + } + + if (all_paused == 0) { + NEXT_STATE(LOOP_TOGGLING_WINDOWS); + SDL_SendAppEvent(SDL_APP_WILLENTERFOREGROUND); + SDL_SendAppEvent(SDL_APP_DIDENTERFOREGROUND); + } + } + SDL_UnlockMutex(Android_ActivityMutex); +#endif + if (all_paused == 1) { + ANDROIDAUDIO_PauseDevices(); + openslES_PauseDevices(); + + if (debug == 1) { + SDL_Log("loop_state: LOOP_BLOCKED (blocked!)"); + } + + if (SDL_SemWait(Android_ResumeSem) == 0) { - /* Make sure this is the last thing we do before pausing */ - if (!isContextExternal) { - SDL_LockMutex(Android_ActivityMutex); - android_egl_context_backup(Android_Window); - SDL_UnlockMutex(Android_ActivityMutex); + NEXT_STATE(LOOP_TOGGLING_WINDOWS); + + /* Android_ResumeSem was signaled */ + SDL_SendAppEvent(SDL_APP_WILLENTERFOREGROUND); + SDL_SendAppEvent(SDL_APP_DIDENTERFOREGROUND); + + ANDROIDAUDIO_ResumeDevices(); + openslES_ResumeDevices(); + + /* Make sure SW Keyboard is restored when an app becomes foreground */ + if (SDL_IsTextInputActive()) { + Android_StartTextInput(_this); /* Only showTextInput */ + } + } + } + } else if (SDL_SemTryWait(Android_PauseSem) == 0 || SDL_SemTryWait(Android_ResumeSem) == 0 || videodata->loop_state == LOOP_TOGGLING_WINDOWS) { + int all_paused = 1; + int wait_minimized_event = 0; + + /* Browse all SDL_Window */ + SDL_LockMutex(Android_ActivityMutex); + { + SDL_Window *window; + SDL_bool should_restore = !SDL_IsVideoContextExternal() && !SDL_HasEvent(SDL_QUIT); + + for (window = _this->windows; window; window = window->next) { + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + + if (data->activity_state == ACTIVITY_PAUSED) { + if (data->backup_done == 0) { + data->backup_done = 1; + SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MINIMIZED, 0, 0); + all_paused = 0; + wait_minimized_event = 1; + } else if (data->backup_done == 1) { + // If the event is consumed, then backup. + if (SDL_IsMinimizedEventConsummed(SDL_GetWindowID(window))) { + SDL_Log("android_egl_context_backup windowID=%d", SDL_GetWindowID(window)); + android_egl_context_backup(window); + } else { + wait_minimized_event = 1; + all_paused = 0; + } + + } + } else if (data->activity_state == ACTIVITY_RESUMED) { + all_paused = 0; + if (data->backup_done == 2) { + /* Restore the GL Context from here, as this operation is thread dependent */ + if (should_restore) { + SDL_Log("android_egl_context_restore windowID=%d", SDL_GetWindowID(window)); + android_egl_context_restore(window); + } + SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESTORED, 0, 0); + } + } else /* ACTIVITY_INIT */ { + all_paused = 0; + } + } + } + SDL_UnlockMutex(Android_ActivityMutex); + + if (wait_minimized_event) { + NEXT_STATE(LOOP_TOGGLING_WINDOWS); + } else if (all_paused) { + SDL_SendAppEvent(SDL_APP_WILLENTERBACKGROUND); + SDL_SendAppEvent(SDL_APP_DIDENTERBACKGROUND); + + NEXT_STATE(LOOP_SENDING_BG_EVENTS); + } else { + NEXT_STATE(LOOP_INIT); } - ANDROIDAUDIO_PauseDevices(); - openslES_PauseDevices(); - - if (SDL_SemWait(Android_ResumeSem) == 0) { - - videodata->isPaused = 0; - - /* Android_ResumeSem was signaled */ - SDL_SendAppEvent(SDL_APP_WILLENTERFOREGROUND); - SDL_SendAppEvent(SDL_APP_DIDENTERFOREGROUND); - SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_RESTORED, 0, 0); - - ANDROIDAUDIO_ResumeDevices(); - openslES_ResumeDevices(); - - /* Restore the GL Context from here, as this operation is thread dependent */ - if (!isContextExternal && !SDL_HasEvent(SDL_QUIT)) { - SDL_LockMutex(Android_ActivityMutex); - android_egl_context_restore(Android_Window); - SDL_UnlockMutex(Android_ActivityMutex); - } + } - /* Make sure SW Keyboard is restored when an app becomes foreground */ - if (SDL_IsTextInputActive()) { - Android_StartTextInput(_this); /* Only showTextInput */ - } + if (debug == 2) { + SDL_Window *window; + SDL_Log("==========================================="); + for (window = _this->windows; window; window = window->next) { + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + SDL_Log("loop_state = %d windowID=%d activity_state=%d backup_done=%d", videodata->loop_state, SDL_GetWindowID(window), data->activity_state, data->backup_done); } - } else { - if (videodata->isPausing || SDL_SemTryWait(Android_PauseSem) == 0) { + + SDL_Log("=== Android_PumpEvents_Blocking === End ==="); + } - /* Android_PauseSem was signaled */ - if (videodata->isPausing == 0) { - SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_MINIMIZED, 0, 0); - SDL_SendAppEvent(SDL_APP_WILLENTERBACKGROUND); - SDL_SendAppEvent(SDL_APP_DIDENTERBACKGROUND); - } - /* We've been signaled to pause (potentially several times), but before we block ourselves, - * we need to make sure that the very last event (of the first pause sequence, if several) - * has reached the app */ - if (SDL_NumberOfEvents(SDL_APP_DIDENTERBACKGROUND) > SDL_SemValue(Android_PauseSem)) { - videodata->isPausing = 1; - } else { - videodata->isPausing = 0; - videodata->isPaused = 1; - } - } - } + } void Android_PumpEvents_NonBlocking(_THIS) { +#if 0 SDL_VideoData *videodata = (SDL_VideoData *)_this->driverdata; static int backup_context = 0; @@ -228,6 +363,7 @@ } } } +#endif } #endif /* SDL_VIDEO_DRIVER_ANDROID */ diff -r 48ca0191d7ee src/video/android/SDL_androidgl.c --- a/src/video/android/SDL_androidgl.c Wed Jun 03 14:58:38 2020 -0700 +++ b/src/video/android/SDL_androidgl.c Thu Jun 04 13:34:11 2020 +0200 @@ -51,7 +51,7 @@ { SDL_GLContext ret; - Android_ActivityMutex_Lock_Running(); + Android_ActivityMutex_Lock_Running(window); ret = SDL_EGL_CreateContext(_this, ((SDL_WindowData *) window->driverdata)->egl_surface); diff -r 48ca0191d7ee src/video/android/SDL_androidvideo.c --- a/src/video/android/SDL_androidvideo.c Wed Jun 03 14:58:38 2020 -0700 +++ b/src/video/android/SDL_androidvideo.c Thu Jun 04 13:34:11 2020 +0200 @@ -180,14 +180,10 @@ int Android_VideoInit(_THIS) { - SDL_VideoData *videodata = (SDL_VideoData *)_this->driverdata; int display_index; SDL_VideoDisplay *display; SDL_DisplayMode mode; - videodata->isPaused = SDL_FALSE; - videodata->isPausing = SDL_FALSE; - mode.format = Android_ScreenFormat; mode.w = Android_DeviceWidth; mode.h = Android_DeviceHeight; @@ -227,7 +223,7 @@ } void -Android_SetScreenResolution(int surfaceWidth, int surfaceHeight, int deviceWidth, int deviceHeight, Uint32 format, float rate) +Android_SetScreenResolution(SDL_Window *window, int surfaceWidth, int surfaceHeight, int deviceWidth, int deviceHeight, Uint32 format, float rate) { Android_SurfaceWidth = surfaceWidth; Android_SurfaceHeight = surfaceHeight; diff -r 48ca0191d7ee src/video/android/SDL_androidvideo.h --- a/src/video/android/SDL_androidvideo.h Wed Jun 03 14:58:38 2020 -0700 +++ b/src/video/android/SDL_androidvideo.h Thu Jun 04 13:34:11 2020 +0200 @@ -28,16 +28,21 @@ #include "../SDL_sysvideo.h" /* Called by the JNI layer when the screen changes size or format */ -extern void Android_SetScreenResolution(int surfaceWidth, int surfaceHeight, int deviceWidth, int deviceHeight, Uint32 format, float rate); +extern void Android_SetScreenResolution(SDL_Window *window, int surfaceWidth, int surfaceHeight, int deviceWidth, int deviceHeight, Uint32 format, float rate); extern void Android_SendResize(SDL_Window *window); /* Private display data */ + +#define LOOP_INIT 0 +#define LOOP_TOGGLING_WINDOWS 1 +#define LOOP_SENDING_BG_EVENTS 2 +#define LOOP_BLOCKED 3 + typedef struct SDL_VideoData { SDL_Rect textRect; - int isPaused; - int isPausing; + int loop_state; } SDL_VideoData; extern int Android_SurfaceWidth; diff -r 48ca0191d7ee src/video/android/SDL_androidwindow.c --- a/src/video/android/SDL_androidwindow.c Wed Jun 03 14:58:38 2020 -0700 +++ b/src/video/android/SDL_androidwindow.c Thu Jun 04 13:34:11 2020 +0200 @@ -33,24 +33,29 @@ #include "SDL_androidwindow.h" #include "SDL_hints.h" -/* Currently only one window */ -SDL_Window *Android_Window = NULL; - int Android_CreateWindow(_THIS, SDL_Window * window) { SDL_WindowData *data; int retval = 0; - - Android_ActivityMutex_Lock_Running(); + int windowID = SDL_GetWindowID(window); - if (Android_Window) { - retval = SDL_SetError("Android only supports one window"); + /* Allocate this memory first, because for a 2nd SDL_Window, we start a new Activty and surfaceView, + * they try fill data->native_window */ + data = (SDL_WindowData *) SDL_calloc(1, sizeof(*data)); + if (!data) { + retval = SDL_OutOfMemory(); goto endfunction; } + window->driverdata = data; + + /* Start a new Activity */ + Android_JNI_CreateWindow(windowID); + + Android_ActivityMutex_Lock_Running(window); /* Set orientation */ - Android_JNI_SetOrientation(window->w, window->h, window->flags & SDL_WINDOW_RESIZABLE, SDL_GetHint(SDL_HINT_ORIENTATIONS)); + Android_JNI_SetOrientation(SDL_GetWindowID(window), window->w, window->h, window->flags & SDL_WINDOW_RESIZABLE, SDL_GetHint(SDL_HINT_ORIENTATIONS)); /* Adjust the window data to match the screen */ window->x = 0; @@ -65,35 +70,33 @@ SDL_SetMouseFocus(window); SDL_SetKeyboardFocus(window); - data = (SDL_WindowData *) SDL_calloc(1, sizeof(*data)); - if (!data) { - retval = SDL_OutOfMemory(); - goto endfunction; - } - - data->native_window = Android_JNI_GetNativeWindow(); - - if (!data->native_window) { - SDL_free(data); - retval = SDL_SetError("Could not fetch native window"); - goto endfunction; +#if 0 + if (data->native_window == NULL) { + data->native_window = Android_JNI_GetNativeWindow(windowID); + if (!data->native_window) { + SDL_free(data); + retval = SDL_SetError("Could not fetch native window windowID=%d", windowID); + goto endfunction; + } } /* Do not create EGLSurface for Vulkan window since it will then make the window incompatible with vkCreateAndroidSurfaceKHR */ if ((window->flags & SDL_WINDOW_OPENGL) != 0) { - data->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType) data->native_window); + if (data->egl_surface == EGL_NO_SURFACE) { + + data->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType) data->native_window); - if (data->egl_surface == EGL_NO_SURFACE) { - ANativeWindow_release(data->native_window); - SDL_free(data); - retval = -1; - goto endfunction; + if (data->egl_surface == EGL_NO_SURFACE) { + ANativeWindow_release(data->native_window); + SDL_free(data); + retval = -1; + goto endfunction; + } } } +#endif - window->driverdata = data; - Android_Window = window; endfunction: @@ -105,19 +108,18 @@ void Android_SetWindowTitle(_THIS, SDL_Window *window) { - Android_JNI_SetActivityTitle(window->title); + Android_JNI_SetActivityTitle(SDL_GetWindowID(window), window->title); } void Android_SetWindowFullscreen(_THIS, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen) { SDL_LockMutex(Android_ActivityMutex); - - if (window == Android_Window) { + { /* If the window is being destroyed don't change visible state */ if (!window->is_destroying) { - Android_JNI_SetWindowStyle(fullscreen); + Android_JNI_SetWindowStyle(SDL_GetWindowID(window), fullscreen); } /* Ensure our size matches reality after we've executed the window style change. @@ -169,12 +171,9 @@ Android_DestroyWindow(_THIS, SDL_Window *window) { SDL_LockMutex(Android_ActivityMutex); - - if (window == Android_Window) { - Android_Window = NULL; - - if (window->driverdata) { - SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + { + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + if (data) { if (data->egl_surface != EGL_NO_SURFACE) { SDL_EGL_DestroySurface(_this, data->egl_surface); } @@ -182,11 +181,12 @@ ANativeWindow_release(data->native_window); } SDL_free(window->driverdata); - window->driverdata = NULL; } + window->driverdata = NULL; } + SDL_UnlockMutex(Android_ActivityMutex); - SDL_UnlockMutex(Android_ActivityMutex); + Android_JNI_DestroyWindow(SDL_GetWindowID(window)); } SDL_bool diff -r 48ca0191d7ee src/video/android/SDL_androidwindow.h --- a/src/video/android/SDL_androidwindow.h Wed Jun 03 14:58:38 2020 -0700 +++ b/src/video/android/SDL_androidwindow.h Thu Jun 04 13:34:11 2020 +0200 @@ -35,13 +35,17 @@ extern SDL_bool Android_GetWindowWMInfo(_THIS, SDL_Window *window, struct SDL_SysWMinfo *info); extern SDL_Window *Android_Window; +#define ACTIVITY_INIT 0 +#define ACTIVITY_RESUMED 1 +#define ACTIVITY_PAUSED 2 + typedef struct { - EGLSurface egl_surface; - EGLContext egl_context; /* We use this to preserve the context when losing focus */ - SDL_bool backup_done; - ANativeWindow *native_window; - + EGLSurface egl_surface; + EGLContext egl_context; /* We use this to preserve the context when losing focus */ + int backup_done; /* 0:running 1:pending backup 2:backup done */ + int activity_state; /* 0:INIT 1:RESUMED 2:PAUSED */ + ANativeWindow *native_window; /* handle SurfaceView */ } SDL_WindowData; #endif /* SDL_androidwindow_h_ */