android: Fix presentation layout on foldable and tablet devices.

This commit is contained in:
bunnei 2023-06-01 17:57:49 -07:00
parent ca4b07a2d7
commit 057117f009
5 changed files with 94 additions and 22 deletions

View file

@ -7,6 +7,7 @@ import android.app.Activity
import android.content.Context import android.content.Context
import android.content.DialogInterface import android.content.DialogInterface
import android.content.Intent import android.content.Intent
import android.content.res.Configuration
import android.graphics.Rect import android.graphics.Rect
import android.hardware.Sensor import android.hardware.Sensor
import android.hardware.SensorEvent import android.hardware.SensorEvent
@ -31,6 +32,7 @@ import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.fragments.EmulationFragment import org.yuzu.yuzu_emu.fragments.EmulationFragment
import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.utils.ControllerMappingHelper import org.yuzu.yuzu_emu.utils.ControllerMappingHelper
import org.yuzu.yuzu_emu.utils.EmulationMenuSettings
import org.yuzu.yuzu_emu.utils.ForegroundService import org.yuzu.yuzu_emu.utils.ForegroundService
import org.yuzu.yuzu_emu.utils.InputHandler import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.NfcReader import org.yuzu.yuzu_emu.utils.NfcReader
@ -128,6 +130,11 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
super.onResume() super.onResume()
nfcReader.startScanning() nfcReader.startScanning()
startMotionSensorListener() startMotionSensorListener()
NativeLibrary.notifyOrientationChange(
EmulationMenuSettings.landscapeScreenLayout,
getAdjustedRotation()
)
} }
override fun onPause() { override fun onPause() {
@ -233,6 +240,23 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
override fun onAccuracyChanged(sensor: Sensor, i: Int) {} override fun onAccuracyChanged(sensor: Sensor, i: Int) {}
private fun getAdjustedRotation():Int {
val rotation = windowManager.defaultDisplay.rotation;
val config: Configuration = resources.configuration
if ((config.screenLayout and Configuration.SCREENLAYOUT_LONG_YES) != 0 ||
(config.screenLayout and Configuration.SCREENLAYOUT_LONG_NO) == 0) {
return rotation;
}
when (rotation) {
Surface.ROTATION_0 -> return Surface.ROTATION_90;
Surface.ROTATION_90 -> return Surface.ROTATION_0;
Surface.ROTATION_180 -> return Surface.ROTATION_270;
Surface.ROTATION_270 -> return Surface.ROTATION_180;
}
return rotation;
}
private fun restoreState(savedInstanceState: Bundle) { private fun restoreState(savedInstanceState: Bundle) {
game = savedInstanceState.parcelable(EXTRA_SELECTED_GAME)!! game = savedInstanceState.parcelable(EXTRA_SELECTED_GAME)!!
} }

View file

@ -118,12 +118,6 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
override fun onStop() { override fun onStop() {
super.onStop() super.onStop()
presenter.onStop(isFinishing) presenter.onStop(isFinishing)
// Update framebuffer layout when closing the settings
NativeLibrary.notifyOrientationChange(
EmulationMenuSettings.landscapeScreenLayout,
windowManager.defaultDisplay.rotation
)
} }
override fun showSettingsFragment(menuTag: String, addToStack: Boolean, gameId: String) { override fun showSettingsFragment(menuTag: String, addToStack: Boolean, gameId: String) {

View file

@ -767,10 +767,11 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
var cutoutBottom = 0 var cutoutBottom = 0
val insets = context.windowManager.currentWindowMetrics.windowInsets.displayCutout val insets = context.windowManager.currentWindowMetrics.windowInsets.displayCutout
if (insets != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { if (insets != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
maxY = if (insets.boundingRectTop.bottom != 0 && insets.boundingRectTop.bottom > maxY / 2)
if (insets.boundingRectTop.bottom != 0) insets.boundingRectTop.bottom.toFloat() else maxY insets.boundingRectTop.bottom.toFloat() else maxY
maxX = if (insets.boundingRectRight.left != 0 && insets.boundingRectRight.left > maxX / 2)
if (insets.boundingRectRight.left != 0) insets.boundingRectRight.left.toFloat() else maxX insets.boundingRectRight.left.toFloat() else maxX
minX = insets.boundingRectLeft.right - insets.boundingRectLeft.left minX = insets.boundingRectLeft.right - insets.boundingRectLeft.left
minY = insets.boundingRectBottom.top - insets.boundingRectBottom.bottom minY = insets.boundingRectBottom.top - insets.boundingRectBottom.bottom
@ -778,7 +779,6 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
cutoutBottom = insets.boundingRectTop.top - insets.boundingRectTop.bottom cutoutBottom = insets.boundingRectTop.top - insets.boundingRectTop.bottom
} }
// This makes sure that if we have an inset on one side of the screen, we mirror it on // This makes sure that if we have an inset on one side of the screen, we mirror it on
// the other side. Since removing space from one of the max values messes with the scale, // the other side. Since removing space from one of the max values messes with the scale,
// we also have to account for it using our min values. // we also have to account for it using our min values.

View file

@ -89,8 +89,16 @@ public:
return m_native_window; return m_native_window;
} }
void SetNativeWindow(ANativeWindow* m_native_window_) { void SetNativeWindow(ANativeWindow* native_window) {
m_native_window = m_native_window_; m_native_window = native_window;
}
u32 ScreenRotation() const {
return m_screen_rotation;
}
void SetScreenRotation(u32 screen_rotation) {
m_screen_rotation = screen_rotation;
} }
void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir, void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir,
@ -379,6 +387,7 @@ private:
// Window management // Window management
std::unique_ptr<EmuWindow_Android> m_window; std::unique_ptr<EmuWindow_Android> m_window;
ANativeWindow* m_native_window{}; ANativeWindow* m_native_window{};
u32 m_screen_rotation{};
// Core emulation // Core emulation
Core::System m_system; Core::System m_system;
@ -404,6 +413,10 @@ private:
} // Anonymous namespace } // Anonymous namespace
u32 GetAndroidScreenRotation() {
return EmulationSession::GetInstance().ScreenRotation();
}
static Core::SystemResultStatus RunEmulation(const std::string& filepath) { static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
Common::Log::Initialize(); Common::Log::Initialize();
Common::Log::SetColorConsoleBackendEnabled(true); Common::Log::SetColorConsoleBackendEnabled(true);
@ -450,7 +463,9 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceDestroyed(JNIEnv* env,
void Java_org_yuzu_yuzu_1emu_NativeLibrary_notifyOrientationChange(JNIEnv* env, void Java_org_yuzu_yuzu_1emu_NativeLibrary_notifyOrientationChange(JNIEnv* env,
[[maybe_unused]] jclass clazz, [[maybe_unused]] jclass clazz,
jint layout_option, jint layout_option,
jint rotation) {} jint rotation) {
return EmulationSession::GetInstance().SetScreenRotation(static_cast<u32>(rotation));
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env,
[[maybe_unused]] jclass clazz, [[maybe_unused]] jclass clazz,

View file

@ -37,6 +37,10 @@
#include "video_core/vulkan_common/vulkan_memory_allocator.h" #include "video_core/vulkan_common/vulkan_memory_allocator.h"
#include "video_core/vulkan_common/vulkan_wrapper.h" #include "video_core/vulkan_common/vulkan_wrapper.h"
#ifdef ANDROID
extern u32 GetAndroidScreenRotation();
#endif
namespace Vulkan { namespace Vulkan {
namespace { namespace {
@ -74,23 +78,58 @@ struct ScreenRectVertex {
} }
}; };
constexpr std::array<f32, 4 * 4> MakeOrthographicMatrix(f32 width, f32 height) {
// clang-format off
#ifdef ANDROID #ifdef ANDROID
// Android renders in portrait, so rotate the matrix.
return { 0.f, 2.f / width, 0.f, 0.f, std::array<f32, 4 * 4> MakeOrthographicMatrix(f32 width, f32 height) {
-2.f / height, 0.f, 0.f, 0.f, constexpr u32 ROTATION_0 = 0;
0.f, 0.f, 1.f, 0.f, constexpr u32 ROTATION_90 = 1;
1.f, -1.f, 0.f, 1.f}; constexpr u32 ROTATION_180 = 2;
constexpr u32 ROTATION_270 = 3;
// clang-format off
switch (GetAndroidScreenRotation()) {
case ROTATION_0:
// Desktop
return { 2.f / width, 0.f, 0.f, 0.f,
0.f, 2.f / height, 0.f, 0.f,
0.f, 0.f, 1.f, 0.f,
-1.f, -1.f, 0.f, 1.f};
case ROTATION_180:
// Reverse desktop
return {-2.f / width, 0.f, 0.f, 0.f,
0.f, -2.f / height, 0.f, 0.f,
0.f, 0.f, 1.f, 0.f,
1.f, 1.f, 0.f, 1.f};
case ROTATION_270:
// Reverse landscape
return { 0.f, -2.f / width, 0.f, 0.f,
2.f / height, 0.f, 0.f, 0.f,
0.f, 0.f, 1.f, 0.f,
-1.f, 1.f, 0.f, 1.f};
case ROTATION_90:
default:
// Landscape
return { 0.f, 2.f / width, 0.f, 0.f,
-2.f / height, 0.f, 0.f, 0.f,
0.f, 0.f, 1.f, 0.f,
1.f, -1.f, 0.f, 1.f};
}
// clang-format on
}
#else #else
std::array<f32, 4 * 4> MakeOrthographicMatrix(f32 width, f32 height) {
// clang-format off
return { 2.f / width, 0.f, 0.f, 0.f, return { 2.f / width, 0.f, 0.f, 0.f,
0.f, 2.f / height, 0.f, 0.f, 0.f, 2.f / height, 0.f, 0.f,
0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 1.f, 0.f,
-1.f, -1.f, 0.f, 1.f}; -1.f, -1.f, 0.f, 1.f};
#endif // ANDROID
// clang-format on // clang-format on
} }
#endif
u32 GetBytesPerPixel(const Tegra::FramebufferConfig& framebuffer) { u32 GetBytesPerPixel(const Tegra::FramebufferConfig& framebuffer) {
using namespace VideoCore::Surface; using namespace VideoCore::Surface;
return BytesPerBlock(PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)); return BytesPerBlock(PixelFormatFromGPUPixelFormat(framebuffer.pixel_format));