From 581a288852c428382ee1a12bb7ecb3ac22707708 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 May 2020 11:10:04 -0700 Subject: [PATCH 1/7] Android: Refactor size maintenance to own class. --- .../src/org/ppsspp/ppsspp/NativeActivity.java | 160 ++--------------- .../src/org/ppsspp/ppsspp/SizeManager.java | 165 ++++++++++++++++++ 2 files changed, 175 insertions(+), 150 deletions(-) create mode 100644 android/src/org/ppsspp/ppsspp/SizeManager.java diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index 2a2b986702..dfd429d326 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -28,10 +28,8 @@ import android.os.PowerManager; import android.os.Vibrator; import android.provider.MediaStore; import android.text.InputType; -import android.util.DisplayMetrics; import android.util.Log; import android.view.Display; -import android.view.DisplayCutout; import android.view.Gravity; import android.view.HapticFeedbackConstants; import android.view.InputDevice; @@ -40,13 +38,9 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.OrientationEventListener; import android.view.Surface; -import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; -import android.view.View.OnSystemUiVisibilityChangeListener; -import android.view.ViewGroup; import android.view.Window; -import android.view.WindowInsets; import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; @@ -59,7 +53,7 @@ import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; -public abstract class NativeActivity extends Activity implements SurfaceHolder.Callback { +public abstract class NativeActivity extends Activity { // Remember to loadLibrary your JNI .so in a static {} block // Adjust these as necessary @@ -116,20 +110,10 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C private String inputPlayerADesc; private PowerSaveModeReceiver mPowerSaveModeReceiver = null; - + private SizeManager sizeManager = null; private static LocationHelper mLocationHelper; private static CameraHelper mCameraHelper; - private float densityDpi; - private float refreshRate; - private int pixelWidth; - private int pixelHeight; - - private int safeInsetLeft = 0; - private int safeInsetRight = 0; - private int safeInsetTop = 0; - private int safeInsetBottom = 0; - private static final String[] permissionsForStorage = { Manifest.permission.WRITE_EXTERNAL_STORAGE, }; @@ -467,12 +451,6 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C // Tells the render loop thread to exit, so we can restart it. public native void exitEGLRenderLoop(); - public void getDesiredBackbufferSize(Point sz) { - NativeApp.computeDesiredBackbufferDimensions(); - sz.x = NativeApp.getDesiredBackbufferWidth(); - sz.y = NativeApp.getDesiredBackbufferHeight(); - } - private SurfaceView getSurfaceView() { if (mGLSurfaceView != null) { return mGLSurfaceView; @@ -481,34 +459,15 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C } } - @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) public void updateDisplayMeasurements() { Display display = getWindowManager().getDefaultDisplay(); - - // Early in startup, we don't have a view to query. Do our best to get some kind of size - // that can be used by config default heuristics, and so on. - DisplayMetrics metrics = new DisplayMetrics(); - if (navigationHidden && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - display.getRealMetrics(metrics); - } else { - display.getMetrics(metrics); - } - - // Later on, we have the exact pixel size so let's just use it. - SurfaceView view = getSurfaceView(); - if (view != null) { - metrics.widthPixels = view.getWidth(); - metrics.heightPixels = view.getHeight(); - } - densityDpi = metrics.densityDpi; - refreshRate = display.getRefreshRate(); - - NativeApp.setDisplayParameters(metrics.widthPixels, metrics.heightPixels, (int) densityDpi, refreshRate); + sizeManager.updateDisplayMeasurements(display, getSurfaceView(), navigationHidden); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + sizeManager = new SizeManager(this); TextRenderer.init(this); shuttingDown = false; registerCallbacks(); @@ -540,7 +499,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C mGLSurfaceView = new NativeGLView(this); nativeRenderer = new NativeRenderer(this); mGLSurfaceView.setEGLContextClientVersion(2); - mGLSurfaceView.getHolder().addCallback(NativeActivity.this); + sizeManager.setupSurfaceView(mGLSurfaceView); // Setup the GLSurface and ask android for the correct // Number of bits for r, g, b, a, depth and stencil components @@ -572,93 +531,28 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C } mGLSurfaceView.setRenderer(nativeRenderer); setContentView(mGLSurfaceView); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - mGLSurfaceView.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() { - @Override - public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) { - checkInsets(windowInsets); - return windowInsets; - } - }); - } } else { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { updateSystemUiVisibility(); } mSurfaceView = new NativeSurfaceView(NativeActivity.this); - mSurfaceView.getHolder().addCallback(NativeActivity.this); + sizeManager.setupSurfaceView(mSurfaceView); Log.i(TAG, "setcontentview before"); setContentView(mSurfaceView); Log.i(TAG, "setcontentview after"); ensureRenderLoop(); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - mSurfaceView.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() { - @Override - public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) { - checkInsets(windowInsets); - return windowInsets; - } - }); - } } } - private Point desiredSize = new Point(); - private int badOrientationCount = 0; - - @Override - public void surfaceCreated(SurfaceHolder holder) { - pixelWidth = holder.getSurfaceFrame().width(); - pixelHeight = holder.getSurfaceFrame().height(); - - // Workaround for terrible bug when locking and unlocking the screen in landscape mode on Nexus 5X. - int requestedOr = getRequestedOrientation(); - boolean requestedPortrait = requestedOr == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || requestedOr == ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; - boolean detectedPortrait = pixelHeight > pixelWidth; - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT && badOrientationCount < 3 && requestedPortrait != detectedPortrait && requestedOr != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) { - Log.e(TAG, "Bad orientation detected (w=" + pixelWidth + " h=" + pixelHeight + "! Recreating activity."); - badOrientationCount++; - recreate(); - return; - } else if (requestedPortrait == detectedPortrait) { - Log.i(TAG, "Correct orientation detected, resetting orientation counter."); - badOrientationCount = 0; - } else { - Log.i(TAG, "Bad orientation detected but ignored" + (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT ? " (sdk version)" : "")); - } - - Log.d(TAG, "Surface created. pixelWidth=" + pixelWidth + ", pixelHeight=" + pixelHeight + " holder: " + holder.toString() + " or: " + requestedOr); - NativeApp.setDisplayParameters(pixelWidth, pixelHeight, (int) densityDpi, refreshRate); - getDesiredBackbufferSize(desiredSize); - - // Note that desiredSize might be 0,0 here - but that's fine when calling setFixedSize! It means auto. - Log.d(TAG, "Setting fixed size " + desiredSize.x + " x " + desiredSize.y); - holder.setFixedSize(desiredSize.x, desiredSize.y); - } - @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); updateSustainedPerformanceMode(); } - @Override - public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - Log.v(TAG, "surfaceChanged: isCreating:" + holder.isCreating() + " holder: " + holder.toString()); - if (holder.isCreating() && desiredSize.x > 0 && desiredSize.y > 0) { - // We have called setFixedSize which will trigger another surfaceChanged after the initial - // one. This one is the original one and we don't care about it. - Log.w(TAG, "holder.isCreating = true, ignoring. width=" + width + " height=" + height + " desWidth=" + desiredSize.x + " desHeight=" + desiredSize.y); - return; - } - Log.w(TAG, "Surface changed. Resolution: " + width + "x" + height + " Format: " + format); - // The window size might have changed (immersive mode, native fullscreen on some devices) - NativeApp.backbufferResize(width, height, format); - updateDisplayMeasurements(); - mSurface = holder.getSurface(); + public void notifySurface(Surface surface) { + mSurface = surface; if (!javaGL) { // If we got a surface, this starts the thread. If not, it doesn't. if (mSurface == null) { @@ -670,17 +564,6 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C updateSustainedPerformanceMode(); } - @Override - public void surfaceDestroyed(SurfaceHolder holder) { - mSurface = null; - Log.w(TAG, "Surface destroyed."); - if (!javaGL) { - joinRenderLoopThread(); - } - // Autosize the next created surface. - holder.setSizeFromLayout(); - } - // Invariants: After this, mRenderLoopThread will be set, and the thread will be running. protected synchronized void ensureRenderLoop() { if (javaGL) { @@ -728,7 +611,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C return; } - decorView.setOnSystemUiVisibilityChangeListener(new OnSystemUiVisibilityChangeListener() { + decorView.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() { @Override public void onSystemUiVisibilityChange(int visibility) { // Called when the system UI's visibility changes, regardless of @@ -864,29 +747,6 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C } } - private void checkInsets(WindowInsets insets) { - if (insets == null) { - return; - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - DisplayCutout cutout = insets.getDisplayCutout(); - if (cutout != null) { - safeInsetLeft = cutout.getSafeInsetLeft(); - safeInsetRight = cutout.getSafeInsetRight(); - safeInsetTop = cutout.getSafeInsetTop(); - safeInsetBottom = cutout.getSafeInsetBottom(); - Log.i(TAG, "Safe insets: left: " + safeInsetLeft + " right: " + safeInsetRight + " top: " + safeInsetTop + " bottom: " + safeInsetBottom); - } else { - Log.i(TAG, "Cutout was null"); - safeInsetLeft = 0; - safeInsetRight = 0; - safeInsetTop = 0; - safeInsetBottom = 0; - } - NativeApp.sendMessage("safe_insets", safeInsetLeft + ":" + safeInsetRight + ":" + safeInsetTop + ":" + safeInsetBottom); - } - } - @Override public void onAttachedToWindow() { Log.i(TAG, "onAttachedToWindow"); @@ -905,7 +765,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C updateSystemUiVisibility(); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - densityDpi = (float)newConfig.densityDpi; + sizeManager.updateDpi((float)newConfig.densityDpi); } } diff --git a/android/src/org/ppsspp/ppsspp/SizeManager.java b/android/src/org/ppsspp/ppsspp/SizeManager.java new file mode 100644 index 0000000000..646231d59b --- /dev/null +++ b/android/src/org/ppsspp/ppsspp/SizeManager.java @@ -0,0 +1,165 @@ +package org.ppsspp.ppsspp; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.pm.ActivityInfo; +import android.graphics.Point; +import android.os.Build; +import android.util.DisplayMetrics; +import android.util.Log; +import android.view.Display; +import android.view.DisplayCutout; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.View; +import android.view.WindowInsets; + +public class SizeManager implements SurfaceHolder.Callback { + private static String TAG = "PPSSPPSizeManager"; + + final NativeActivity activity; + + private int safeInsetLeft = 0; + private int safeInsetRight = 0; + private int safeInsetTop = 0; + private int safeInsetBottom = 0; + + private float densityDpi; + private float refreshRate; + private int pixelWidth; + private int pixelHeight; + + private Point desiredSize = new Point(); + private int badOrientationCount = 0; + + public SizeManager(final NativeActivity a) { + activity = a; + } + + @TargetApi(Build.VERSION_CODES.P) + public void setupSurfaceView(SurfaceView surfaceView) { + surfaceView.getHolder().addCallback(this); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + surfaceView.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() { + @Override + public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) { + updateInsets(windowInsets); + return windowInsets; + } + }); + } + } + + @Override + public void surfaceCreated(SurfaceHolder holder) { + pixelWidth = holder.getSurfaceFrame().width(); + pixelHeight = holder.getSurfaceFrame().height(); + + // Workaround for terrible bug when locking and unlocking the screen in landscape mode on Nexus 5X. + int requestedOr = activity.getRequestedOrientation(); + boolean requestedPortrait = requestedOr == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || requestedOr == ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; + boolean detectedPortrait = pixelHeight > pixelWidth; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT && badOrientationCount < 3 && requestedPortrait != detectedPortrait && requestedOr != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) { + Log.e(TAG, "Bad orientation detected (w=" + pixelWidth + " h=" + pixelHeight + "! Recreating activity."); + badOrientationCount++; + activity.recreate(); + return; + } else if (requestedPortrait == detectedPortrait) { + Log.i(TAG, "Correct orientation detected, resetting orientation counter."); + badOrientationCount = 0; + } else { + Log.i(TAG, "Bad orientation detected but ignored" + (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT ? " (sdk version)" : "")); + } + + Log.d(TAG, "Surface created. pixelWidth=" + pixelWidth + ", pixelHeight=" + pixelHeight + " holder: " + holder.toString() + " or: " + requestedOr); + NativeApp.setDisplayParameters(pixelWidth, pixelHeight, (int)densityDpi, refreshRate); + getDesiredBackbufferSize(desiredSize); + + // Note that desiredSize might be 0,0 here - but that's fine when calling setFixedSize! It means auto. + Log.d(TAG, "Setting fixed size " + desiredSize.x + " x " + desiredSize.y); + holder.setFixedSize(desiredSize.x, desiredSize.y); + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + Log.v(TAG, "surfaceChanged: isCreating:" + holder.isCreating() + " holder: " + holder.toString()); + if (holder.isCreating() && desiredSize.x > 0 && desiredSize.y > 0) { + // We have called setFixedSize which will trigger another surfaceChanged after the initial + // one. This one is the original one and we don't care about it. + Log.w(TAG, "holder.isCreating = true, ignoring. width=" + width + " height=" + height + " desWidth=" + desiredSize.x + " desHeight=" + desiredSize.y); + return; + } + + Log.w(TAG, "Surface changed. Resolution: " + width + "x" + height + " Format: " + format); + // The window size might have changed (immersive mode, native fullscreen on some devices) + NativeApp.backbufferResize(width, height, format); + + activity.updateDisplayMeasurements(); + activity.notifySurface(holder.getSurface()); + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + activity.notifySurface(null); + + // Autosize the next created surface. + holder.setSizeFromLayout(); + } + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + public void updateDisplayMeasurements(Display display, SurfaceView view, boolean navigationHidden) { + // Early in startup, we don't have a view to query. Do our best to get some kind of size + // that can be used by config default heuristics, and so on. + DisplayMetrics metrics = new DisplayMetrics(); + if (navigationHidden && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + display.getRealMetrics(metrics); + } else { + display.getMetrics(metrics); + } + + // Later on, we have the exact pixel size so let's just use it. + if (view != null) { + metrics.widthPixels = view.getWidth(); + metrics.heightPixels = view.getHeight(); + } + densityDpi = metrics.densityDpi; + refreshRate = display.getRefreshRate(); + + NativeApp.setDisplayParameters(metrics.widthPixels, metrics.heightPixels, (int)densityDpi, refreshRate); + } + + public void updateDpi(float dpi) { + densityDpi = dpi; + } + + private void getDesiredBackbufferSize(Point sz) { + NativeApp.computeDesiredBackbufferDimensions(); + sz.x = NativeApp.getDesiredBackbufferWidth(); + sz.y = NativeApp.getDesiredBackbufferHeight(); + } + + @TargetApi(Build.VERSION_CODES.P) + private void updateInsets(WindowInsets insets) { + if (insets == null) { + return; + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + DisplayCutout cutout = insets.getDisplayCutout(); + if (cutout != null) { + safeInsetLeft = cutout.getSafeInsetLeft(); + safeInsetRight = cutout.getSafeInsetRight(); + safeInsetTop = cutout.getSafeInsetTop(); + safeInsetBottom = cutout.getSafeInsetBottom(); + Log.i(TAG, "Safe insets: left: " + safeInsetLeft + " right: " + safeInsetRight + " top: " + safeInsetTop + " bottom: " + safeInsetBottom); + } else { + Log.i(TAG, "Safe insets: Cutout was null"); + safeInsetLeft = 0; + safeInsetRight = 0; + safeInsetTop = 0; + safeInsetBottom = 0; + } + NativeApp.sendMessage("safe_insets", safeInsetLeft + ":" + safeInsetRight + ":" + safeInsetTop + ":" + safeInsetBottom); + } + } +} From 040ba2b3ff316daa12242c6289d2b1d306cb9c02 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 May 2020 11:10:48 -0700 Subject: [PATCH 2/7] Android: Reassert sustained perf when going ingame. --- android/src/org/ppsspp/ppsspp/NativeActivity.java | 1 + 1 file changed, 1 insertion(+) diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index dfd429d326..97c36606ff 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -1316,6 +1316,7 @@ public abstract class NativeActivity extends Activity { if (params.equals("ingame")) { // Keep the screen bright - very annoying if it goes dark when tilting away window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + updateSustainedPerformanceMode(); } else { // Only keep the screen bright ingame. window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); From f60d80b24e831ca0f37806bc878eb8e302845056 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 May 2020 11:22:44 -0700 Subject: [PATCH 3/7] Android: Move a bit more size handling. --- .../src/org/ppsspp/ppsspp/NativeActivity.java | 40 ++++--------------- .../src/org/ppsspp/ppsspp/SizeManager.java | 38 +++++++++++++++--- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index 97c36606ff..9bfc178cd9 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -85,7 +85,6 @@ public abstract class NativeActivity extends Activity { private boolean sustainedPerfSupported; - private boolean navigationHidden; private View navigationCallbackView = null; // audioFocusChangeListener to listen to changes in audio state @@ -421,7 +420,7 @@ public abstract class NativeActivity extends Activity { } else { Log.e(TAG, "updateSystemUiVisibility: decor view not yet created, ignoring for now"); } - updateDisplayMeasurements(); + sizeManager.updateDisplayMeasurements(); } // Need API 11 to check for existence of a vibrator? Zany. @@ -451,19 +450,6 @@ public abstract class NativeActivity extends Activity { // Tells the render loop thread to exit, so we can restart it. public native void exitEGLRenderLoop(); - private SurfaceView getSurfaceView() { - if (mGLSurfaceView != null) { - return mGLSurfaceView; - } else { - return mSurfaceView; - } - } - - public void updateDisplayMeasurements() { - Display display = getWindowManager().getDefaultDisplay(); - sizeManager.updateDisplayMeasurements(display, getSurfaceView(), navigationHidden); - } - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -475,7 +461,7 @@ public abstract class NativeActivity extends Activity { // This calls NativeApp.setDisplayParameters. Make sure that's done early in order // to be able to set defaults when loading config for the first time. Like figuring out // whether to start at 1x or 2x. - updateDisplayMeasurements(); + sizeManager.updateDisplayMeasurements(); if (!initialized) { Initialize(); @@ -499,7 +485,7 @@ public abstract class NativeActivity extends Activity { mGLSurfaceView = new NativeGLView(this); nativeRenderer = new NativeRenderer(this); mGLSurfaceView.setEGLContextClientVersion(2); - sizeManager.setupSurfaceView(mGLSurfaceView); + sizeManager.setSurfaceView(mGLSurfaceView); // Setup the GLSurface and ask android for the correct // Number of bits for r, g, b, a, depth and stencil components @@ -537,7 +523,7 @@ public abstract class NativeActivity extends Activity { } mSurfaceView = new NativeSurfaceView(NativeActivity.this); - sizeManager.setupSurfaceView(mSurfaceView); + sizeManager.setSurfaceView(mSurfaceView); Log.i(TAG, "setcontentview before"); setContentView(mSurfaceView); Log.i(TAG, "setcontentview after"); @@ -611,20 +597,7 @@ public abstract class NativeActivity extends Activity { return; } - decorView.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() { - @Override - public void onSystemUiVisibilityChange(int visibility) { - // Called when the system UI's visibility changes, regardless of - // whether it's because of our or system actions. - // We will try to force it to follow our preference but will not stupidly - // act as if it's visible if it's not. - navigationHidden = ((visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0); - // TODO: Check here if it's the state we want. - Log.i(TAG, "SystemUiVisibilityChange! visibility=" + visibility + " navigationHidden: " + navigationHidden); - Log.i(TAG, "decorView: " + decorView.getWidth() + "x" + decorView.getHeight()); - updateDisplayMeasurements(); - } - }); + sizeManager.setupSystemUiCallback(decorView); navigationCallbackView = decorView; } @@ -659,6 +632,7 @@ public abstract class NativeActivity extends Activity { mSurfaceView.onDestroy(); mSurfaceView = null; } + sizeManager.setSurfaceView(null); if (mPowerSaveModeReceiver != null) { mPowerSaveModeReceiver.destroy(this); mPowerSaveModeReceiver = null; @@ -774,7 +748,7 @@ public abstract class NativeActivity extends Activity { // onConfigurationChanged not called on multi-window change Log.i(TAG, "onMultiWindowModeChanged: isInMultiWindowMode = " + isInMultiWindowMode); super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig); - updateDisplayMeasurements(); + sizeManager.updateDisplayMeasurements(); } // keep this static so we can call this even if we don't diff --git a/android/src/org/ppsspp/ppsspp/SizeManager.java b/android/src/org/ppsspp/ppsspp/SizeManager.java index 646231d59b..def28237a6 100644 --- a/android/src/org/ppsspp/ppsspp/SizeManager.java +++ b/android/src/org/ppsspp/ppsspp/SizeManager.java @@ -18,6 +18,7 @@ public class SizeManager implements SurfaceHolder.Callback { private static String TAG = "PPSSPPSizeManager"; final NativeActivity activity; + SurfaceView surfaceView = null; private int safeInsetLeft = 0; private int safeInsetRight = 0; @@ -29,6 +30,8 @@ public class SizeManager implements SurfaceHolder.Callback { private int pixelWidth; private int pixelHeight; + private boolean navigationHidden = false; + private Point desiredSize = new Point(); private int badOrientationCount = 0; @@ -37,7 +40,11 @@ public class SizeManager implements SurfaceHolder.Callback { } @TargetApi(Build.VERSION_CODES.P) - public void setupSurfaceView(SurfaceView surfaceView) { + public void setSurfaceView(SurfaceView view) { + surfaceView = view; + if (surfaceView == null) + return; + surfaceView.getHolder().addCallback(this); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { @@ -94,8 +101,8 @@ public class SizeManager implements SurfaceHolder.Callback { Log.w(TAG, "Surface changed. Resolution: " + width + "x" + height + " Format: " + format); // The window size might have changed (immersive mode, native fullscreen on some devices) NativeApp.backbufferResize(width, height, format); + updateDisplayMeasurements(); - activity.updateDisplayMeasurements(); activity.notifySurface(holder.getSurface()); } @@ -108,7 +115,8 @@ public class SizeManager implements SurfaceHolder.Callback { } @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) - public void updateDisplayMeasurements(Display display, SurfaceView view, boolean navigationHidden) { + public void updateDisplayMeasurements() { + Display display = activity.getWindowManager().getDefaultDisplay(); // Early in startup, we don't have a view to query. Do our best to get some kind of size // that can be used by config default heuristics, and so on. DisplayMetrics metrics = new DisplayMetrics(); @@ -119,9 +127,9 @@ public class SizeManager implements SurfaceHolder.Callback { } // Later on, we have the exact pixel size so let's just use it. - if (view != null) { - metrics.widthPixels = view.getWidth(); - metrics.heightPixels = view.getHeight(); + if (surfaceView != null) { + metrics.widthPixels = surfaceView.getWidth(); + metrics.heightPixels = surfaceView.getHeight(); } densityDpi = metrics.densityDpi; refreshRate = display.getRefreshRate(); @@ -129,6 +137,24 @@ public class SizeManager implements SurfaceHolder.Callback { NativeApp.setDisplayParameters(metrics.widthPixels, metrics.heightPixels, (int)densityDpi, refreshRate); } + @TargetApi(Build.VERSION_CODES.KITKAT) + public void setupSystemUiCallback(final View view) { + view.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() { + @Override + public void onSystemUiVisibilityChange(int visibility) { + // Called when the system UI's visibility changes, regardless of + // whether it's because of our or system actions. + // We will try to force it to follow our preference but will not stupidly + // act as if it's visible if it's not. + navigationHidden = ((visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0); + // TODO: Check here if it's the state we want. + Log.i(TAG, "SystemUiVisibilityChange! visibility=" + visibility + " navigationHidden: " + navigationHidden); + Log.i(TAG, "decorView: " + view.getWidth() + "x" + view.getHeight()); + updateDisplayMeasurements(); + } + }); + } + public void updateDpi(float dpi) { densityDpi = dpi; } From dddaa57392027227e666daa4796bf83cfc931955 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 May 2020 12:20:42 -0700 Subject: [PATCH 4/7] Windows: Fix GL reinit on backend switch. --- Windows/GPU/WindowsGLContext.cpp | 3 +++ ext/native/gfx_es2/gpu_features.cpp | 13 +++++++++++-- ext/native/gfx_es2/gpu_features.h | 1 + 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Windows/GPU/WindowsGLContext.cpp b/Windows/GPU/WindowsGLContext.cpp index f0aa0ba45a..71e215c0bc 100644 --- a/Windows/GPU/WindowsGLContext.cpp +++ b/Windows/GPU/WindowsGLContext.cpp @@ -290,6 +290,9 @@ bool WindowsGLContext::InitFromRenderThread(std::string *error_message) { // Unfortunately, glew will generate an invalid enum error, ignore. glGetError(); + // Reset in case we're in a backend switch. + ResetGLExtensions(); + int contextFlags = g_Config.bGfxDebugOutput ? WGL_CONTEXT_DEBUG_BIT_ARB : 0; HGLRC m_hrc = nullptr; diff --git a/ext/native/gfx_es2/gpu_features.cpp b/ext/native/gfx_es2/gpu_features.cpp index 757ccb43f3..ca4df8c26a 100644 --- a/ext/native/gfx_es2/gpu_features.cpp +++ b/ext/native/gfx_es2/gpu_features.cpp @@ -38,9 +38,9 @@ PFNGLISVERTEXARRAYOESPROC glIsVertexArrayOES; GLExtensions gl_extensions; std::string g_all_gl_extensions; -std::set g_set_gl_extensions; +static std::set g_set_gl_extensions; std::string g_all_egl_extensions; -std::set g_set_egl_extensions; +static std::set g_set_egl_extensions; static bool extensionsDone = false; static bool useCoreContext = false; @@ -564,6 +564,15 @@ void SetGLCoreContext(bool flag) { gl_extensions.IsCoreContext = useCoreContext; } +void ResetGLExtensions() { + extensionsDone = false; + + gl_extensions = {}; + gl_extensions.IsCoreContext = useCoreContext; + g_all_gl_extensions.clear(); + g_all_egl_extensions.clear(); +} + static const char *glsl_fragment_prelude = "#ifdef GL_ES\n" "precision mediump float;\n" diff --git a/ext/native/gfx_es2/gpu_features.h b/ext/native/gfx_es2/gpu_features.h index 95a57f095b..4617e544a4 100644 --- a/ext/native/gfx_es2/gpu_features.h +++ b/ext/native/gfx_es2/gpu_features.h @@ -120,5 +120,6 @@ extern std::string g_all_egl_extensions; void CheckGLExtensions(); void SetGLCoreContext(bool flag); +void ResetGLExtensions(); std::string ApplyGLSLPrelude(const std::string &source, uint32_t stage); From 03fc86a1475851ae315224c0b4704e3fb48b9661 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 May 2020 13:52:04 -0700 Subject: [PATCH 5/7] UI: Return to game settings after restart. This is more convenient. Also restarts the game if needed. Added a way to pass args to Android here as well, and fixed displaying an error if the shortcut file doesn't exist on Android (like if you deleted it after creating the shortcut.) --- UI/GameSettingsScreen.cpp | 27 ++++--- UI/GameSettingsScreen.h | 2 + UI/MiscScreens.cpp | 9 ++- UI/MiscScreens.h | 9 +-- UI/NativeApp.cpp | 26 +++++-- Windows/W32Util/Misc.cpp | 11 ++- Windows/W32Util/Misc.h | 2 +- Windows/main.cpp | 20 ++++-- android/jni/app-android.cpp | 72 +++++++++++++++++-- .../src/org/ppsspp/ppsspp/NativeActivity.java | 8 ++- .../src/org/ppsspp/ppsspp/PpssppActivity.java | 10 ++- ext/native/ui/screen.cpp | 50 ++++++------- ext/native/ui/screen.h | 2 +- 13 files changed, 183 insertions(+), 65 deletions(-) diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index c597a68394..f11be70585 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -1195,14 +1195,25 @@ void GameSettingsScreen::CallbackMemstickFolder(bool yes) { } #endif +void GameSettingsScreen::TriggerRestart(const char *why) { + // Extra save here to make sure the choice really gets saved even if there are shutdown bugs in + // the GPU backend code. + g_Config.Save(why); + std::string param = "--gamesettings"; + if (editThenRestore_) { + // We won't pass the gameID, so don't resume back into settings. + param = ""; + } else if (!gamePath_.empty()) { + param += " \"" + ReplaceAll(ReplaceAll(gamePath_, "\\", "\\\\"), "\"", "\\\"") + "\""; + } + System_SendMessage("graphics_restart", param.c_str()); +} + void GameSettingsScreen::CallbackRenderingBackend(bool yes) { // If the user ends up deciding not to restart, set the config back to the current backend // so it doesn't get switched by accident. if (yes) { - // Extra save here to make sure the choice really gets saved even if there are shutdown bugs in - // the GPU backend code. - g_Config.Save("GameSettingsScreen::RenderingBackendYes"); - System_SendMessage("graphics_restart", ""); + TriggerRestart("GameSettingsScreen::RenderingBackendYes"); } else { g_Config.iGPUBackend = (int)GetGPUBackend(); } @@ -1212,10 +1223,7 @@ void GameSettingsScreen::CallbackRenderingDevice(bool yes) { // If the user ends up deciding not to restart, set the config back to the current backend // so it doesn't get switched by accident. if (yes) { - // Extra save here to make sure the choice really gets saved even if there are shutdown bugs in - // the GPU backend code. - g_Config.Save("GameSettingsScreen::RenderingDeviceYes"); - System_SendMessage("graphics_restart", ""); + TriggerRestart("GameSettingsScreen::RenderingDeviceYes"); } else { std::string *deviceNameSetting = GPUDeviceNameSetting(); if (deviceNameSetting) @@ -1227,8 +1235,7 @@ void GameSettingsScreen::CallbackRenderingDevice(bool yes) { void GameSettingsScreen::CallbackInflightFrames(bool yes) { if (yes) { - g_Config.Save("GameSettingsScreen::InflightFramesYes"); - System_SendMessage("graphics_restart", ""); + TriggerRestart("GameSettingsScreen::InflightFramesYes"); } else { g_Config.iInflightFrames = prevInflightFrames_; } diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index 2cf38a3a36..cfcd7fae27 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -48,6 +48,8 @@ protected: bool UseVerticalLayout() const; private: + void TriggerRestart(const char *why); + std::string gameID_; bool lastVertical_; UI::CheckBox *enableReportsCheckbox_; diff --git a/UI/MiscScreens.cpp b/UI/MiscScreens.cpp index 8888df4b55..27fadc85fd 100644 --- a/UI/MiscScreens.cpp +++ b/UI/MiscScreens.cpp @@ -420,7 +420,14 @@ void NewLanguageScreen::OnCompleted(DialogResult result) { void LogoScreen::Next() { if (!switched_) { switched_ = true; - if (boot_filename.size()) { + if (gotoGameSettings_) { + if (boot_filename.size()) { + screenManager()->switchScreen(new EmuScreen(boot_filename)); + } else { + screenManager()->switchScreen(new MainScreen()); + } + screenManager()->push(new GameSettingsScreen(boot_filename)); + } else if (boot_filename.size()) { screenManager()->switchScreen(new EmuScreen(boot_filename)); } else { screenManager()->switchScreen(new MainScreen()); diff --git a/UI/MiscScreens.h b/UI/MiscScreens.h index 34b0e0f40b..459974de48 100644 --- a/UI/MiscScreens.h +++ b/UI/MiscScreens.h @@ -114,8 +114,8 @@ private: class LogoScreen : public UIScreen { public: - LogoScreen() - : frames_(0), switched_(false) {} + LogoScreen(bool gotoGameSettings = false) + : gotoGameSettings_(gotoGameSettings) {} bool key(const KeyInput &key) override; bool touch(const TouchInput &touch) override; void update() override; @@ -125,8 +125,9 @@ public: private: void Next(); - int frames_; - bool switched_; + int frames_ = 0; + bool switched_ = false; + bool gotoGameSettings_ = false; }; class CreditsScreen : public UIDialogScreenWithBackground { diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index b71f30a90f..3c29f900b2 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -92,17 +92,18 @@ #include "Core/WebServer.h" #include "GPU/GPUInterface.h" +#include "UI/BackgroundAudio.h" +#include "UI/ControlMappingScreen.h" +#include "UI/DiscordIntegration.h" #include "UI/EmuScreen.h" #include "UI/GameInfoCache.h" +#include "UI/GPUDriverTestScreen.h" #include "UI/HostTypes.h" -#include "UI/OnScreenDisplay.h" #include "UI/MiscScreens.h" +#include "UI/OnScreenDisplay.h" #include "UI/RemoteISOScreen.h" #include "UI/TiltEventProcessor.h" -#include "UI/BackgroundAudio.h" #include "UI/TextureUtil.h" -#include "UI/DiscordIntegration.h" -#include "UI/GPUDriverTestScreen.h" #if !defined(MOBILE_DEVICE) #include "Common/KeyMap.h" @@ -551,6 +552,8 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch const char *stateToLoad = 0; bool gotBootFilename = false; + bool gotoGameSettings = false; + bool gotoTouchScreenTest = false; boot_filename = ""; // Parse command line @@ -593,6 +596,10 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch g_Config.bPauseMenuExitsEmulator = true; if (!strcmp(argv[i], "--fullscreen")) g_Config.bFullScreen = true; + if (!strcmp(argv[i], "--touchscreentest")) + gotoTouchScreenTest = true; + if (!strcmp(argv[i], "--gamesettings")) + gotoGameSettings = true; break; } } else { @@ -628,7 +635,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch std::unique_ptr fileLoader(ConstructFileLoader(boot_filename)); if (!fileLoader->Exists()) { fprintf(stderr, "File not found: %s\n", boot_filename.c_str()); -#ifdef _WIN32 +#if defined(_WIN32) || defined(__ANDROID__) // Ignore and proceed. #else // Bail. @@ -638,7 +645,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch } } else { fprintf(stderr, "Can only boot one file"); -#ifdef _WIN32 +#if defined(_WIN32) || defined(__ANDROID__) // Ignore and proceed. #else // Bail. @@ -711,7 +718,12 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch } screenManager = new ScreenManager(); - if (skipLogo) { + if (gotoGameSettings) { + screenManager->switchScreen(new LogoScreen(true)); + } else if (gotoTouchScreenTest) { + screenManager->switchScreen(new MainScreen()); + screenManager->push(new TouchTestScreen()); + } else if (skipLogo) { screenManager->switchScreen(new EmuScreen(boot_filename)); } else { screenManager->switchScreen(new LogoScreen()); diff --git a/Windows/W32Util/Misc.cpp b/Windows/W32Util/Misc.cpp index ca26ad7a4b..3bb86f6bf6 100644 --- a/Windows/W32Util/Misc.cpp +++ b/Windows/W32Util/Misc.cpp @@ -153,13 +153,20 @@ namespace W32Util moduleFilename.resize(sz); } - void ExitAndRestart() { + void ExitAndRestart(bool overrideArgs, const std::string &args) { // This preserves arguments (for example, config file) and working directory. std::wstring workingDirectory; std::wstring moduleFilename; GetSelfExecuteParams(workingDirectory, moduleFilename); - const wchar_t *cmdline = RemoveExecutableFromCommandLine(GetCommandLineW()); + const wchar_t *cmdline; + std::wstring wargs; + if (overrideArgs) { + wargs = ConvertUTF8ToWString(args); + cmdline = wargs.c_str(); + } else { + cmdline = RemoveExecutableFromCommandLine(GetCommandLineW()); + } ShellExecute(nullptr, nullptr, moduleFilename.c_str(), cmdline, workingDirectory.c_str(), SW_SHOW); ExitProcess(0); diff --git a/Windows/W32Util/Misc.h b/Windows/W32Util/Misc.h index 680a48c62e..623de73233 100644 --- a/Windows/W32Util/Misc.h +++ b/Windows/W32Util/Misc.h @@ -11,7 +11,7 @@ namespace W32Util BOOL CopyTextToClipboard(HWND hwnd, const char *text); BOOL CopyTextToClipboard(HWND hwnd, const std::wstring &wtext); void MakeTopMost(HWND hwnd, bool topMost); - void ExitAndRestart(); + void ExitAndRestart(bool overrideArgs = false, const std::string &args = ""); void GetSelfExecuteParams(std::wstring &workingDirectory, std::wstring &moduleFilename); } diff --git a/Windows/main.cpp b/Windows/main.cpp index 73938e6093..3795597e26 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -96,6 +96,8 @@ static std::string langRegion; static std::string osName; static std::string gpuDriverVersion; +static std::string restartArgs; + HMENU g_hPopupMenus; int g_activeWindow = 0; @@ -283,6 +285,7 @@ void System_SendMessage(const char *command, const char *parameter) { PostMessage(MainWindow::GetHWND(), WM_CLOSE, 0, 0); } } else if (!strcmp(command, "graphics_restart")) { + restartArgs = parameter == nullptr ? "" : parameter; if (IsDebuggerPresent()) { PostMessage(MainWindow::GetHWND(), MainWindow::WM_USER_RESTART_EMUTHREAD, 0, 0); } else { @@ -415,7 +418,14 @@ static bool DetectVulkanInExternalProcess() { std::vector GetWideCmdLine() { wchar_t **wargv; int wargc = -1; - wargv = CommandLineToArgvW(GetCommandLineW(), &wargc); + // This is used for the WM_USER_RESTART_EMUTHREAD path. + if (!restartArgs.empty()) { + std::wstring wargs = ConvertUTF8ToWString("PPSSPP " + restartArgs); + wargv = CommandLineToArgvW(wargs.c_str(), &wargc); + restartArgs.clear(); + } else { + wargv = CommandLineToArgvW(GetCommandLineW(), &wargc); + } std::vector wideArgs(wargv, wargv + wargc); LocalFree(wargv); @@ -447,12 +457,12 @@ static void WinMainInit() { } static void WinMainCleanup() { - if (g_Config.bRestartRequired) { - W32Util::ExitAndRestart(); - } - net::Shutdown(); CoUninitialize(); + + if (g_Config.bRestartRequired) { + W32Util::ExitAndRestart(!restartArgs.empty(), restartArgs); + } } int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLine, int iCmdShow) { diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 88b66a00e9..94105f01cd 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -399,6 +399,60 @@ extern "C" jstring Java_org_ppsspp_ppsspp_NativeApp_queryConfig return jresult; } +static void parse_args(std::vector &args, const std::string value) { + // Simple argument parser so we can take args from extra params. + const char *p = value.c_str(); + + while (*p != '\0') { + while (isspace(*p)) { + p++; + } + if (*p == '\0') { + break; + } + + bool done = false; + bool quote = false; + std::string arg; + + while (!done) { + size_t sz = strcspn(p, "\"\\ \r\n\t"); + arg += std::string(p, sz); + p += sz; + + switch (*p) { + case '"': + quote = !quote; + p++; + break; + + case '\\': + p++; + arg += std::string(p, 1); + p++; + break; + + case '\0': + done = true; + break; + + default: + // If it's not the above, it's whitespace. + if (!quote) { + done = true; + } + break; + } + } + + args.push_back(arg); + + while (isspace(*p)) { + p++; + } + } +} + extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init (JNIEnv *env, jclass, jstring jmodel, jint jdeviceType, jstring jlangRegion, jstring japkpath, jstring jdataDir, jstring jexternalDir, jstring jlibraryDir, jstring jcacheDir, jstring jshortcutParam, @@ -455,18 +509,22 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init NativeGetAppInfo(&app_name, &app_nice_name, &landscape, &version); - // If shortcut_param is not empty, pass it as additional varargs argument to NativeInit() method. + // If shortcut_param is not empty, pass it as additional arguments to the NativeInit() method. // NativeInit() is expected to treat extra argument as boot_filename, which in turn will start game immediately. // NOTE: Will only work if ppsspp started from Activity.onCreate(). Won't work if ppsspp app start from onResume(). - if (shortcut_param.empty()) { - const char *argv[2] = {app_name.c_str(), 0}; - NativeInit(1, argv, user_data_path.c_str(), externalDir.c_str(), cacheDir.c_str()); - } else { - const char *argv[3] = {app_name.c_str(), shortcut_param.c_str(), 0}; - NativeInit(2, argv, user_data_path.c_str(), externalDir.c_str(), cacheDir.c_str()); + std::vector args; + std::vector temp; + args.push_back(app_name.c_str()); + if (!shortcut_param.empty()) { + parse_args(temp, shortcut_param); + for (const auto &arg : temp) { + args.push_back(arg.c_str()); + } } + NativeInit((int)args.size(), &args[0], user_data_path.c_str(), externalDir.c_str(), cacheDir.c_str()); + retry: // Now that we've loaded config, set javaGL. javaGL = NativeQueryConfig("androidJavaGL") == "true"; diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index 9bfc178cd9..be62d9abdd 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -75,6 +75,7 @@ public abstract class NativeActivity extends Activity { protected NativeRenderer nativeRenderer; private String shortcutParam = ""; + private static String overrideShortcutParam = null; public static String runCommand; public static String commandParameter; @@ -302,9 +303,11 @@ public abstract class NativeActivity extends Activity { String model = Build.MANUFACTURER + ":" + Build.MODEL; String languageRegion = Locale.getDefault().getLanguage() + "_" + Locale.getDefault().getCountry(); + String shortcut = overrideShortcutParam == null ? shortcutParam : overrideShortcutParam; + overrideShortcutParam = null; NativeApp.audioConfig(optimalFramesPerBuffer, optimalSampleRate); - NativeApp.init(model, deviceType, languageRegion, apkFilePath, dataDir, externalStorageDir, libraryDir, cacheDir, shortcutParam, Build.VERSION.SDK_INT, Build.BOARD); + NativeApp.init(model, deviceType, languageRegion, apkFilePath, dataDir, externalStorageDir, libraryDir, cacheDir, shortcut, Build.VERSION.SDK_INT, Build.BOARD); // Allow C++ to tell us to use JavaGL or not. javaGL = "true".equalsIgnoreCase(NativeApp.queryConfig("androidJavaGL")); @@ -1254,6 +1257,9 @@ public abstract class NativeActivity extends Activity { recreate(); } else if (command.equals("graphics_restart")) { Log.i(TAG, "graphics_restart"); + if (params != null && !params.equals("")) { + overrideShortcutParam = params; + } shuttingDown = true; recreate(); } else if (command.equals("ask_permission") && params.equals("storage")) { diff --git a/android/src/org/ppsspp/ppsspp/PpssppActivity.java b/android/src/org/ppsspp/ppsspp/PpssppActivity.java index f6496168b8..4269f7f98b 100644 --- a/android/src/org/ppsspp/ppsspp/PpssppActivity.java +++ b/android/src/org/ppsspp/ppsspp/PpssppActivity.java @@ -12,6 +12,8 @@ public class PpssppActivity extends NativeActivity { private static final String TAG = "PpssppActivity"; // Key used by shortcut. public static final String SHORTCUT_EXTRA_KEY = "org.ppsspp.ppsspp.Shortcuts"; + // Key used for debugging. + public static final String ARGS_EXTRA_KEY = "org.ppsspp.ppsspp.Args"; private static boolean m_hasUnsupportedABI = false; private static boolean m_hasNoNativeBinary = false; @@ -78,14 +80,18 @@ public class PpssppActivity extends NativeActivity { if (data != null) { String path = intent.getData().getPath(); Log.i(TAG, "Found Shortcut Parameter in data: " + path); - super.setShortcutParam(path); + super.setShortcutParam("\"" + path.replace("\\", "\\\\").replace("\"", "\\\"") + "\""); // Toast.makeText(getApplicationContext(), path, Toast.LENGTH_SHORT).show(); } else { String param = getIntent().getStringExtra(SHORTCUT_EXTRA_KEY); + String args = getIntent().getStringExtra(ARGS_EXTRA_KEY); Log.e(TAG, "Got ACTION_VIEW without a valid uri, trying param"); if (param != null) { Log.i(TAG, "Found Shortcut Parameter in extra-data: " + param); - super.setShortcutParam(getIntent().getStringExtra(SHORTCUT_EXTRA_KEY)); + super.setShortcutParam("\"" + param.replace("\\", "\\\\").replace("\"", "\\\"") + "\""); + } else if (args != null) { + Log.i(TAG, "Found args parameter in extra-data: " + args); + super.setShortcutParam(args); } else { Log.e(TAG, "Shortcut missing parameter!"); super.setShortcutParam(""); diff --git a/ext/native/ui/screen.cpp b/ext/native/ui/screen.cpp index a5e105aa56..958c1d70c3 100644 --- a/ext/native/ui/screen.cpp +++ b/ext/native/ui/screen.cpp @@ -8,7 +8,6 @@ #include "ui/view.h" ScreenManager::ScreenManager() { - nextScreen_ = 0; uiContext_ = 0; dialogFinished_ = 0; } @@ -18,7 +17,7 @@ ScreenManager::~ScreenManager() { } void ScreenManager::switchScreen(Screen *screen) { - if (screen == nextScreen_) { + if (!nextStack_.empty() && screen == nextStack_.front().screen) { ELOG("Already switching to this screen"); return; } @@ -26,23 +25,23 @@ void ScreenManager::switchScreen(Screen *screen) { // will only become apparent if the dialog is closed. The previous screen will stick around // until that switch. // TODO: is this still true? - if (nextScreen_ != 0) { - ELOG("Already had a nextScreen_! Asynchronous open while doing something? Deleting the new screen."); + if (!nextStack_.empty()) { + ELOG("Already had a nextStack_! Asynchronous open while doing something? Deleting the new screen."); delete screen; return; } - if (screen == 0) { + if (screen == nullptr) { WLOG("Swiching to a zero screen, this can't be good"); } if (stack_.empty() || screen != stack_.back().screen) { - nextScreen_ = screen; - nextScreen_->setScreenManager(this); + screen->setScreenManager(this); + nextStack_.push_back({ screen, 0 }); } } void ScreenManager::update() { std::lock_guard guard(inputLock_); - if (nextScreen_) { + if (!nextStack_.empty()) { switchToNext(); } @@ -53,22 +52,25 @@ void ScreenManager::update() { void ScreenManager::switchToNext() { std::lock_guard guard(inputLock_); - if (!nextScreen_) { - ELOG("switchToNext: No nextScreen_!"); + if (nextStack_.empty()) { + ELOG("switchToNext: No nextStack_!"); } - Layer temp = {0, 0}; + Layer temp = {nullptr, 0}; if (!stack_.empty()) { temp = stack_.back(); stack_.pop_back(); } - Layer newLayer = {nextScreen_, 0}; - stack_.push_back(newLayer); + stack_.push_back(nextStack_.front()); if (temp.screen) { delete temp.screen; } - nextScreen_ = 0; - UI::SetFocusedView(0); + UI::SetFocusedView(nullptr); + + for (size_t i = 1; i < nextStack_.size(); ++i) { + stack_.push_back(nextStack_[i]); + } + nextStack_.clear(); } bool ScreenManager::touch(const TouchInput &touch) { @@ -197,19 +199,16 @@ Screen *ScreenManager::topScreen() const { void ScreenManager::shutdown() { std::lock_guard guard(inputLock_); - for (auto x = stack_.begin(); x != stack_.end(); x++) - delete x->screen; + for (auto layer : stack_) + delete layer.screen; stack_.clear(); - delete nextScreen_; - nextScreen_ = nullptr; + for (auto layer : nextStack_) + delete layer.screen; + nextStack_.clear(); } void ScreenManager::push(Screen *screen, int layerFlags) { std::lock_guard guard(inputLock_); - if (nextScreen_ && stack_.empty()) { - // we're during init, this is OK - switchToNext(); - } screen->setScreenManager(this); if (screen->isTransparent()) { layerFlags |= LAYER_TRANSPARENT; @@ -224,7 +223,10 @@ void ScreenManager::push(Screen *screen, int layerFlags) { touch(input); Layer layer = {screen, layerFlags}; - stack_.push_back(layer); + if (nextStack_.empty()) + stack_.push_back(layer); + else + nextStack_.push_back(layer); } void ScreenManager::pop() { diff --git a/ext/native/ui/screen.h b/ext/native/ui/screen.h index abca7f9769..0b97108afd 100644 --- a/ext/native/ui/screen.h +++ b/ext/native/ui/screen.h @@ -147,7 +147,6 @@ private: void switchToNext(); void processFinishDialog(); - Screen *nextScreen_; UIContext *uiContext_; Draw::DrawContext *thin3DContext_; @@ -166,4 +165,5 @@ private: // Dialog stack. These are shown "on top" of base screens and the Android back button works as expected. // Used for options, in-game menus and other things you expect to be able to back out from onto something. std::vector stack_; + std::vector nextStack_; }; From d328e161d7fd5a40e1a1f6c75f12e911e51e3a99 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 May 2020 14:06:54 -0700 Subject: [PATCH 6/7] UI: Add more options to touch test screen. --- UI/ControlMappingScreen.cpp | 32 +++++++++++++++++++++++++++++++- UI/ControlMappingScreen.h | 5 ++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/UI/ControlMappingScreen.cpp b/UI/ControlMappingScreen.cpp index 8dac4ccae6..2008c19f50 100644 --- a/UI/ControlMappingScreen.cpp +++ b/UI/ControlMappingScreen.cpp @@ -636,6 +636,25 @@ void TouchTestScreen::CreateViews() { root_ = new LinearLayout(ORIENT_VERTICAL); LinearLayout *theTwo = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(1.0f)); root_->Add(theTwo); + +#if !PPSSPP_PLATFORM(UWP) + static const char *renderingBackend[] = { "OpenGL", "Direct3D 9", "Direct3D 11", "Vulkan" }; + PopupMultiChoice *renderingBackendChoice = root_->Add(new PopupMultiChoice(&g_Config.iGPUBackend, gr->T("Backend"), renderingBackend, (int)GPUBackend::OPENGL, ARRAY_SIZE(renderingBackend), gr->GetName(), screenManager())); + renderingBackendChoice->OnChoice.Handle(this, &TouchTestScreen::OnRenderingBackend); + + if (!g_Config.IsBackendEnabled(GPUBackend::OPENGL)) + renderingBackendChoice->HideChoice((int)GPUBackend::OPENGL); + if (!g_Config.IsBackendEnabled(GPUBackend::DIRECT3D9)) + renderingBackendChoice->HideChoice((int)GPUBackend::DIRECT3D9); + if (!g_Config.IsBackendEnabled(GPUBackend::DIRECT3D11)) + renderingBackendChoice->HideChoice((int)GPUBackend::DIRECT3D11); + if (!g_Config.IsBackendEnabled(GPUBackend::VULKAN)) + renderingBackendChoice->HideChoice((int)GPUBackend::VULKAN); +#endif + +#if PPSSPP_PLATFORM(ANDROID) + root_->Add(new Choice(gr->T("Recreate Activity")))->OnClick.Handle(this, &TouchTestScreen::OnRecreateActivity); +#endif root_->Add(new CheckBox(&g_Config.bImmersiveMode, gr->T("FullScreen", "Full Screen")))->OnClick.Handle(this, &TouchTestScreen::OnImmersiveModeChange); root_->Add(new Button(di->T("Back")))->OnClick.Handle(this, &UIScreen::OnBack); } @@ -687,7 +706,7 @@ void TouchTestScreen::render() { g_dpi_scale_x, g_dpi_scale_y, g_dpi_scale_real_x, g_dpi_scale_real_y); - ui_context->DrawTextShadow(buffer, bounds.x + 20.0f, dp_yres / 2.0f, 0xFFFFFFFF, ALIGN_VCENTER | FLAG_DYNAMIC_ASCII); + ui_context->DrawTextShadow(buffer, bounds.x + 20.0f, bounds.y + 20.0f, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); ui_context->Flush(); } @@ -700,3 +719,14 @@ UI::EventReturn TouchTestScreen::OnImmersiveModeChange(UI::EventParams &e) { } return UI::EVENT_DONE; } + +UI::EventReturn TouchTestScreen::OnRenderingBackend(UI::EventParams &e) { + g_Config.Save("GameSettingsScreen::RenderingBackend"); + System_SendMessage("graphics_restart", "--touchscreentest"); + return UI::EVENT_DONE; +} + +UI::EventReturn TouchTestScreen::OnRecreateActivity(UI::EventParams &e) { + RecreateActivity(); + return UI::EVENT_DONE; +} diff --git a/UI/ControlMappingScreen.h b/UI/ControlMappingScreen.h index 130596ee05..3d7b6879e5 100644 --- a/UI/ControlMappingScreen.h +++ b/UI/ControlMappingScreen.h @@ -132,6 +132,9 @@ protected: }; TrackedTouch touches_[MAX_TOUCH_POINTS]{}; - virtual void CreateViews() override; + void CreateViews() override; + UI::EventReturn OnImmersiveModeChange(UI::EventParams &e); + UI::EventReturn OnRenderingBackend(UI::EventParams &e); + UI::EventReturn OnRecreateActivity(UI::EventParams &e); }; From ccc7ae54af9f2cb229b3248d17c922c5954f14ec Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 May 2020 14:44:07 -0700 Subject: [PATCH 7/7] Android: Avoid multiple resizes in a row. This was sometimes happening when ui callbacks etc. would trigger in certain patterns of using the app. Might have been related to sizing problems. Delaying the resize by 10ms when the surface is unchanged seems okay. --- android/jni/app-android.cpp | 21 ++++++++++------ .../src/org/ppsspp/ppsspp/NativeActivity.java | 6 ++--- .../src/org/ppsspp/ppsspp/SizeManager.java | 25 ++++++++++++++++++- 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 94105f01cd..649ecade8a 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -1026,14 +1026,21 @@ void getDesiredBackbufferSize(int &sz_x, int &sz_y) { extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_setDisplayParameters(JNIEnv *, jclass, jint xres, jint yres, jint dpi, jfloat refreshRate) { ILOG("NativeApp.setDisplayParameters(%d x %d, dpi=%d, refresh=%0.2f)", xres, yres, dpi, refreshRate); - display_xres = xres; - display_yres = yres; - display_dpi_x = dpi; - display_dpi_y = dpi; - display_hz = refreshRate; + bool changed = false; + changed = changed || display_xres != xres || display_yres != yres; + changed = changed || display_dpi_x != dpi || display_dpi_y != dpi; + changed = changed || display_hz != refreshRate; - recalculateDpi(); - NativeResized(); + if (changed) { + display_xres = xres; + display_yres = yres; + display_dpi_x = dpi; + display_dpi_y = dpi; + display_hz = refreshRate; + + recalculateDpi(); + NativeResized(); + } } extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_computeDesiredBackbufferDimensions() { diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index be62d9abdd..2b6f2d4890 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -29,14 +29,12 @@ import android.os.Vibrator; import android.provider.MediaStore; import android.text.InputType; import android.util.Log; -import android.view.Display; import android.view.Gravity; import android.view.HapticFeedbackConstants; import android.view.InputDevice; import android.view.InputEvent; import android.view.KeyEvent; import android.view.MotionEvent; -import android.view.OrientationEventListener; import android.view.Surface; import android.view.SurfaceView; import android.view.View; @@ -423,7 +421,7 @@ public abstract class NativeActivity extends Activity { } else { Log.e(TAG, "updateSystemUiVisibility: decor view not yet created, ignoring for now"); } - sizeManager.updateDisplayMeasurements(); + sizeManager.checkDisplayMeasurements(); } // Need API 11 to check for existence of a vibrator? Zany. @@ -751,7 +749,7 @@ public abstract class NativeActivity extends Activity { // onConfigurationChanged not called on multi-window change Log.i(TAG, "onMultiWindowModeChanged: isInMultiWindowMode = " + isInMultiWindowMode); super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig); - sizeManager.updateDisplayMeasurements(); + sizeManager.checkDisplayMeasurements(); } // keep this static so we can call this even if we don't diff --git a/android/src/org/ppsspp/ppsspp/SizeManager.java b/android/src/org/ppsspp/ppsspp/SizeManager.java index def28237a6..d5977e974a 100644 --- a/android/src/org/ppsspp/ppsspp/SizeManager.java +++ b/android/src/org/ppsspp/ppsspp/SizeManager.java @@ -5,6 +5,8 @@ import android.app.Activity; import android.content.pm.ActivityInfo; import android.graphics.Point; import android.os.Build; +import android.os.Handler; +import android.os.Looper; import android.util.DisplayMetrics; import android.util.Log; import android.view.Display; @@ -13,6 +15,7 @@ import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.WindowInsets; +import java.lang.Runnable; public class SizeManager implements SurfaceHolder.Callback { private static String TAG = "PPSSPPSizeManager"; @@ -31,6 +34,7 @@ public class SizeManager implements SurfaceHolder.Callback { private int pixelHeight; private boolean navigationHidden = false; + private boolean displayUpdatePending = false; private Point desiredSize = new Point(); private int badOrientationCount = 0; @@ -114,8 +118,27 @@ public class SizeManager implements SurfaceHolder.Callback { holder.setSizeFromLayout(); } + public void checkDisplayMeasurements() { + if (displayUpdatePending) { + return; + } + displayUpdatePending = true; + + final Runnable updater = new Runnable() { + public void run() { + Log.d(TAG, "checkDisplayMeasurements: checking now"); + updateDisplayMeasurements(); + } + }; + + final Handler handler = new Handler(Looper.getMainLooper()); + handler.postDelayed(updater, 10); + } + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) public void updateDisplayMeasurements() { + displayUpdatePending = false; + Display display = activity.getWindowManager().getDefaultDisplay(); // Early in startup, we don't have a view to query. Do our best to get some kind of size // that can be used by config default heuristics, and so on. @@ -150,7 +173,7 @@ public class SizeManager implements SurfaceHolder.Callback { // TODO: Check here if it's the state we want. Log.i(TAG, "SystemUiVisibilityChange! visibility=" + visibility + " navigationHidden: " + navigationHidden); Log.i(TAG, "decorView: " + view.getWidth() + "x" + view.getHeight()); - updateDisplayMeasurements(); + checkDisplayMeasurements(); } }); }