From 7e5b83f126dd45878b4d7bb9e737e40d236d5570 Mon Sep 17 00:00:00 2001 From: David Griswold Date: Sun, 11 Aug 2024 15:59:41 +0100 Subject: [PATCH] Refactored layout code in preparation for Android custom layout GUI --- .../github/lime3ds/android/NativeLibrary.kt | 7 +- .../android/display/PortraitScreenLayout.kt | 17 ++ .../android/display/ScreenAdjustmentUtil.kt | 39 ++- .../lime3ds/android/display/ScreenLayout.kt | 7 +- .../features/settings/model/BooleanSetting.kt | 1 + .../features/settings/model/IntSetting.kt | 1 + .../features/settings/model/Settings.kt | 1 - .../android/fragments/EmulationFragment.kt | 104 +++++-- .../android/utils/EmulationMenuSettings.kt | 13 +- src/android/app/src/main/jni/config.cpp | 7 +- src/android/app/src/main/jni/default_ini.h | 24 +- .../src/main/jni/emu_window/emu_window.cpp | 7 + src/android/app/src/main/jni/id_cache.cpp | 7 + src/android/app/src/main/jni/id_cache.h | 1 + src/android/app/src/main/jni/native.cpp | 16 +- .../app/src/main/res/menu/menu_emulation.xml | 2 +- .../app/src/main/res/menu/menu_in_game.xml | 5 + .../res/menu/menu_landscape_screen_layout.xml | 7 +- .../res/menu/menu_portrait_screen_layout.xml | 15 + .../app/src/main/res/values/strings.xml | 5 +- src/common/settings.cpp | 2 + src/common/settings.h | 41 +-- src/core/frontend/emu_window.cpp | 51 ++-- src/core/frontend/framebuffer_layout.cpp | 266 ++++++++++-------- src/core/frontend/framebuffer_layout.h | 11 +- src/lime/config.cpp | 2 +- src/lime_qt/configuration/config.cpp | 3 +- .../renderer_opengl/renderer_opengl.cpp | 32 ++- .../renderer_opengl/renderer_opengl.h | 4 +- 29 files changed, 453 insertions(+), 245 deletions(-) create mode 100644 src/android/app/src/main/java/io/github/lime3ds/android/display/PortraitScreenLayout.kt create mode 100644 src/android/app/src/main/res/menu/menu_portrait_screen_layout.xml diff --git a/src/android/app/src/main/java/io/github/lime3ds/android/NativeLibrary.kt b/src/android/app/src/main/java/io/github/lime3ds/android/NativeLibrary.kt index 7d91c09fd..1959eaba9 100644 --- a/src/android/app/src/main/java/io/github/lime3ds/android/NativeLibrary.kt +++ b/src/android/app/src/main/java/io/github/lime3ds/android/NativeLibrary.kt @@ -158,8 +158,9 @@ object NativeLibrary { /** * Notifies the core emulation that the orientation has changed. */ - external fun notifyOrientationChange(layoutOption: Int, rotation: Int) + external fun notifyOrientationChange(layoutOption: Int, rotation: Int, isPortrait: Boolean) + external fun notifyPortraitLayoutChange(layoutOption: Int, rotation: Int, isPortrait: Boolean) /** * Swaps the top and bottom screens. */ @@ -266,6 +267,10 @@ object NativeLibrary { @JvmStatic fun landscapeScreenLayout(): Int = EmulationMenuSettings.landscapeScreenLayout + @Keep + @JvmStatic + fun portraitScreenLayout(): Int = EmulationMenuSettings.portraitScreenLayout + @Keep @JvmStatic fun displayAlertMsg(title: String, message: String, yesNo: Boolean): Boolean { diff --git a/src/android/app/src/main/java/io/github/lime3ds/android/display/PortraitScreenLayout.kt b/src/android/app/src/main/java/io/github/lime3ds/android/display/PortraitScreenLayout.kt new file mode 100644 index 000000000..74d9c1820 --- /dev/null +++ b/src/android/app/src/main/java/io/github/lime3ds/android/display/PortraitScreenLayout.kt @@ -0,0 +1,17 @@ +// Copyright 2023 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +package io.github.lime3ds.android.display + +enum class PortraitScreenLayout(val int: Int) { + // These must match what is defined in src/common/settings.h + TOP_FULL_WIDTH(0), + CUSTOM_PORTRAIT_LAYOUT(1); + + companion object { + fun from(int: Int): PortraitScreenLayout { + return entries.firstOrNull { it.int == int } ?: TOP_FULL_WIDTH; + } + } +} diff --git a/src/android/app/src/main/java/io/github/lime3ds/android/display/ScreenAdjustmentUtil.kt b/src/android/app/src/main/java/io/github/lime3ds/android/display/ScreenAdjustmentUtil.kt index cd0310047..8d4734e29 100644 --- a/src/android/app/src/main/java/io/github/lime3ds/android/display/ScreenAdjustmentUtil.kt +++ b/src/android/app/src/main/java/io/github/lime3ds/android/display/ScreenAdjustmentUtil.kt @@ -12,8 +12,10 @@ import io.github.lime3ds.android.features.settings.model.Settings import io.github.lime3ds.android.features.settings.utils.SettingsFile import io.github.lime3ds.android.utils.EmulationMenuSettings -class ScreenAdjustmentUtil(private val windowManager: WindowManager, - private val settings: Settings) { +class ScreenAdjustmentUtil( + private val windowManager: WindowManager, + private val settings: Settings +) { fun swapScreen() { val isEnabled = !EmulationMenuSettings.swapScreens EmulationMenuSettings.swapScreens = isEnabled @@ -25,18 +27,39 @@ class ScreenAdjustmentUtil(private val windowManager: WindowManager, settings.saveSetting(BooleanSetting.SWAP_SCREEN, SettingsFile.FILE_NAME_CONFIG) } + // TODO: Consider how cycling should handle custom layout + // right now it simply skips it fun cycleLayouts() { - val nextLayout = (EmulationMenuSettings.landscapeScreenLayout + 1) % ScreenLayout.entries.size - changeScreenOrientation(ScreenLayout.from(nextLayout)) + val nextLayout = if (NativeLibrary.isPortraitMode) { + (EmulationMenuSettings.portraitScreenLayout + 1) % (PortraitScreenLayout.entries.size - 1) + } else { + (EmulationMenuSettings.landscapeScreenLayout + 1) % (ScreenLayout.entries.size - 1) + } + settings.loadSettings() + + changeScreenOrientation(nextLayout) } - fun changeScreenOrientation(layoutOption: ScreenLayout) { - EmulationMenuSettings.landscapeScreenLayout = layoutOption.int + fun changePortraitOrientation(layoutOption: Int) { + EmulationMenuSettings.portraitScreenLayout = layoutOption + NativeLibrary.notifyPortraitLayoutChange( + EmulationMenuSettings.portraitScreenLayout, + windowManager.defaultDisplay.rotation, + NativeLibrary::isPortraitMode.get() + ) + IntSetting.PORTRAIT_SCREEN_LAYOUT.int = layoutOption + settings.saveSetting(IntSetting.PORTRAIT_SCREEN_LAYOUT, SettingsFile.FILE_NAME_CONFIG) + } + + fun changeScreenOrientation(layoutOption: Int) { + EmulationMenuSettings.landscapeScreenLayout = layoutOption NativeLibrary.notifyOrientationChange( EmulationMenuSettings.landscapeScreenLayout, - windowManager.defaultDisplay.rotation + windowManager.defaultDisplay.rotation, + NativeLibrary::isPortraitMode.get() ) - IntSetting.SCREEN_LAYOUT.int = layoutOption.int + IntSetting.SCREEN_LAYOUT.int = layoutOption settings.saveSetting(IntSetting.SCREEN_LAYOUT, SettingsFile.FILE_NAME_CONFIG) + } } diff --git a/src/android/app/src/main/java/io/github/lime3ds/android/display/ScreenLayout.kt b/src/android/app/src/main/java/io/github/lime3ds/android/display/ScreenLayout.kt index 1c6635a78..a6fc57e10 100644 --- a/src/android/app/src/main/java/io/github/lime3ds/android/display/ScreenLayout.kt +++ b/src/android/app/src/main/java/io/github/lime3ds/android/display/ScreenLayout.kt @@ -6,17 +6,18 @@ package io.github.lime3ds.android.display enum class ScreenLayout(val int: Int) { // These must match what is defined in src/common/settings.h - DEFAULT(0), + TOP_BOTTOM(0), SINGLE_SCREEN(1), LARGE_SCREEN(2), SIDE_SCREEN(3), HYBRID_SCREEN(4), - MOBILE_PORTRAIT(5), + CUSTOM_LAYOUT(5), MOBILE_LANDSCAPE(6); + companion object { fun from(int: Int): ScreenLayout { - return entries.firstOrNull { it.int == int } ?: DEFAULT + return entries.firstOrNull { it.int == int } ?: MOBILE_LANDSCAPE } } } diff --git a/src/android/app/src/main/java/io/github/lime3ds/android/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/io/github/lime3ds/android/features/settings/model/BooleanSetting.kt index ecdd19267..d4e15543a 100644 --- a/src/android/app/src/main/java/io/github/lime3ds/android/features/settings/model/BooleanSetting.kt +++ b/src/android/app/src/main/java/io/github/lime3ds/android/features/settings/model/BooleanSetting.kt @@ -14,6 +14,7 @@ enum class BooleanSetting( PLUGIN_LOADER("plugin_loader", Settings.SECTION_SYSTEM, false), ALLOW_PLUGIN_LOADER("allow_plugin_loader", Settings.SECTION_SYSTEM, true), SWAP_SCREEN("swap_screen", Settings.SECTION_LAYOUT, false), + CUSTOM_LAYOUT("custom_layout",Settings.SECTION_LAYOUT,false), ADRENO_GPU_BOOST("adreno_gpu_boost", Settings.SECTION_RENDERER, false); override var boolean: Boolean = defaultValue diff --git a/src/android/app/src/main/java/io/github/lime3ds/android/features/settings/model/IntSetting.kt b/src/android/app/src/main/java/io/github/lime3ds/android/features/settings/model/IntSetting.kt index 34b5dc128..4e240c3a5 100644 --- a/src/android/app/src/main/java/io/github/lime3ds/android/features/settings/model/IntSetting.kt +++ b/src/android/app/src/main/java/io/github/lime3ds/android/features/settings/model/IntSetting.kt @@ -23,6 +23,7 @@ enum class IntSetting( CARDBOARD_X_SHIFT("cardboard_x_shift", Settings.SECTION_LAYOUT, 0), CARDBOARD_Y_SHIFT("cardboard_y_shift", Settings.SECTION_LAYOUT, 0), SCREEN_LAYOUT("layout_option", Settings.SECTION_LAYOUT, 0), + PORTRAIT_SCREEN_LAYOUT("portrait_layout_option",Settings.SECTION_LAYOUT,0), AUDIO_INPUT_TYPE("output_type", Settings.SECTION_AUDIO, 0), NEW_3DS("is_new_3ds", Settings.SECTION_SYSTEM, 1), LLE_APPLETS("lle_applets", Settings.SECTION_SYSTEM, 0), diff --git a/src/android/app/src/main/java/io/github/lime3ds/android/features/settings/model/Settings.kt b/src/android/app/src/main/java/io/github/lime3ds/android/features/settings/model/Settings.kt index eead17ecf..d13d324d7 100644 --- a/src/android/app/src/main/java/io/github/lime3ds/android/features/settings/model/Settings.kt +++ b/src/android/app/src/main/java/io/github/lime3ds/android/features/settings/model/Settings.kt @@ -131,7 +131,6 @@ class Settings { const val KEY_CSTICK_AXIS_HORIZONTAL = "cstick_axis_horizontal" const val KEY_DPAD_AXIS_VERTICAL = "dpad_axis_vertical" const val KEY_DPAD_AXIS_HORIZONTAL = "dpad_axis_horizontal" - const val HOTKEY_SCREEN_SWAP = "hotkey_screen_swap" const val HOTKEY_CYCLE_LAYOUT = "hotkey_toggle_layout" const val HOTKEY_CLOSE_GAME = "hotkey_close_game" diff --git a/src/android/app/src/main/java/io/github/lime3ds/android/fragments/EmulationFragment.kt b/src/android/app/src/main/java/io/github/lime3ds/android/fragments/EmulationFragment.kt index de4ffb944..455ce3e89 100644 --- a/src/android/app/src/main/java/io/github/lime3ds/android/fragments/EmulationFragment.kt +++ b/src/android/app/src/main/java/io/github/lime3ds/android/fragments/EmulationFragment.kt @@ -51,6 +51,7 @@ import io.github.lime3ds.android.activities.EmulationActivity import io.github.lime3ds.android.databinding.DialogCheckboxBinding import io.github.lime3ds.android.databinding.DialogSliderBinding import io.github.lime3ds.android.databinding.FragmentEmulationBinding +import io.github.lime3ds.android.display.PortraitScreenLayout import io.github.lime3ds.android.display.ScreenAdjustmentUtil import io.github.lime3ds.android.display.ScreenLayout import io.github.lime3ds.android.features.settings.model.SettingsViewModel @@ -142,7 +143,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram retainInstance = true emulationState = EmulationState(game.path) emulationActivity = requireActivity() as EmulationActivity - screenAdjustmentUtil = ScreenAdjustmentUtil(emulationActivity.windowManager, settingsViewModel.settings) + screenAdjustmentUtil = + ScreenAdjustmentUtil(emulationActivity.windowManager, settingsViewModel.settings) EmulationLifecycleUtil.addShutdownHook(hook = { emulationState.stop() }) EmulationLifecycleUtil.addPauseResumeHook(hook = { togglePause() }) } @@ -207,16 +209,18 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram } }) binding.inGameMenu.menu.findItem(R.id.menu_lock_drawer).apply { - val titleId = if (EmulationMenuSettings.drawerLockMode == DrawerLayout.LOCK_MODE_LOCKED_CLOSED) { - R.string.unlock_drawer - } else { - R.string.lock_drawer - } - val iconId = if (EmulationMenuSettings.drawerLockMode == DrawerLayout.LOCK_MODE_UNLOCKED) { - R.drawable.ic_unlocked - } else { - R.drawable.ic_lock - } + val titleId = + if (EmulationMenuSettings.drawerLockMode == DrawerLayout.LOCK_MODE_LOCKED_CLOSED) { + R.string.unlock_drawer + } else { + R.string.lock_drawer + } + val iconId = + if (EmulationMenuSettings.drawerLockMode == DrawerLayout.LOCK_MODE_UNLOCKED) { + R.drawable.ic_unlocked + } else { + R.drawable.ic_lock + } title = getString(titleId) icon = ResourcesCompat.getDrawable( @@ -267,7 +271,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram } R.id.menu_landscape_screen_layout -> { - showScreenLayoutMenu() + showLandscapeScreenLayoutMenu() + true + } + + R.id.menu_portrait_screen_layout -> { + showPortraitScreenLayoutMenu() true } @@ -423,7 +432,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram } private fun togglePause() { - if(emulationState.isPaused) { + if (emulationState.isPaused) { emulationState.unpause() } else { emulationState.pause() @@ -769,7 +778,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram popupMenu.show() } - private fun showScreenLayoutMenu() { + private fun showLandscapeScreenLayoutMenu() { val popupMenu = PopupMenu( requireContext(), binding.inGameMenu.findViewById(R.id.menu_landscape_screen_layout) @@ -784,12 +793,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram ScreenLayout.SIDE_SCREEN.int -> R.id.menu_screen_layout_sidebyside - ScreenLayout.MOBILE_PORTRAIT.int -> - R.id.menu_screen_layout_portrait - ScreenLayout.HYBRID_SCREEN.int -> R.id.menu_screen_layout_hybrid + ScreenLayout.CUSTOM_LAYOUT.int -> + R.id.menu_screen_layout_custom + else -> R.id.menu_screen_layout_landscape } popupMenu.menu.findItem(layoutOptionMenuItem).setChecked(true) @@ -797,27 +806,68 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram popupMenu.setOnMenuItemClickListener { when (it.itemId) { R.id.menu_screen_layout_landscape -> { - screenAdjustmentUtil.changeScreenOrientation(ScreenLayout.MOBILE_LANDSCAPE) - true - } - - R.id.menu_screen_layout_portrait -> { - screenAdjustmentUtil.changeScreenOrientation(ScreenLayout.MOBILE_PORTRAIT) + screenAdjustmentUtil.changeScreenOrientation(ScreenLayout.MOBILE_LANDSCAPE.int) true } R.id.menu_screen_layout_single -> { - screenAdjustmentUtil.changeScreenOrientation(ScreenLayout.SINGLE_SCREEN) + screenAdjustmentUtil.changeScreenOrientation(ScreenLayout.SINGLE_SCREEN.int) true } R.id.menu_screen_layout_sidebyside -> { - screenAdjustmentUtil.changeScreenOrientation(ScreenLayout.SIDE_SCREEN) + screenAdjustmentUtil.changeScreenOrientation(ScreenLayout.SIDE_SCREEN.int) true } R.id.menu_screen_layout_hybrid -> { - screenAdjustmentUtil.changeScreenOrientation(ScreenLayout.HYBRID_SCREEN) + screenAdjustmentUtil.changeScreenOrientation(ScreenLayout.HYBRID_SCREEN.int) + true + } + + R.id.menu_screen_layout_custom -> { + screenAdjustmentUtil.changeScreenOrientation(ScreenLayout.CUSTOM_LAYOUT.int) + true + } + + else -> true + } + } + + popupMenu.show() + } + + private fun showPortraitScreenLayoutMenu() { + val popupMenu = PopupMenu( + requireContext(), + binding.inGameMenu.findViewById(R.id.menu_portrait_screen_layout) + ) + + popupMenu.menuInflater.inflate(R.menu.menu_portrait_screen_layout, popupMenu.menu) + + val layoutOptionMenuItem = when (EmulationMenuSettings.portraitScreenLayout) { + PortraitScreenLayout.TOP_FULL_WIDTH.int -> + R.id.menu_portrait_layout_top_full + + PortraitScreenLayout.CUSTOM_PORTRAIT_LAYOUT.int -> + R.id.menu_portrait_layout_custom + + else -> + R.id.menu_portrait_layout_top_full + + } + + popupMenu.menu.findItem(layoutOptionMenuItem).setChecked(true) + + popupMenu.setOnMenuItemClickListener { + when (it.itemId) { + R.id.menu_portrait_layout_top_full -> { + screenAdjustmentUtil.changePortraitOrientation(PortraitScreenLayout.TOP_FULL_WIDTH.int) + true + } + + R.id.menu_portrait_layout_custom -> { + screenAdjustmentUtil.changePortraitOrientation(PortraitScreenLayout.CUSTOM_PORTRAIT_LAYOUT.int) true } @@ -959,7 +1009,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram resetScale("controlScale-" + NativeLibrary.ButtonType.BUTTON_SWAP) binding.surfaceInputOverlay.refreshControls() } - + private fun setControlOpacity(opacity: Int) { preferences.edit() .putInt("controlOpacity", opacity) diff --git a/src/android/app/src/main/java/io/github/lime3ds/android/utils/EmulationMenuSettings.kt b/src/android/app/src/main/java/io/github/lime3ds/android/utils/EmulationMenuSettings.kt index 904caec41..eead8dc9e 100644 --- a/src/android/app/src/main/java/io/github/lime3ds/android/utils/EmulationMenuSettings.kt +++ b/src/android/app/src/main/java/io/github/lime3ds/android/utils/EmulationMenuSettings.kt @@ -7,6 +7,7 @@ package io.github.lime3ds.android.utils import androidx.drawerlayout.widget.DrawerLayout import androidx.preference.PreferenceManager import io.github.lime3ds.android.LimeApplication +import io.github.lime3ds.android.display.PortraitScreenLayout import io.github.lime3ds.android.display.ScreenLayout object EmulationMenuSettings { @@ -30,13 +31,23 @@ object EmulationMenuSettings { var landscapeScreenLayout: Int get() = preferences.getInt( "EmulationMenuSettings_LandscapeScreenLayout", - ScreenLayout.MOBILE_LANDSCAPE.int + ScreenLayout.LARGE_SCREEN.int ) set(value) { preferences.edit() .putInt("EmulationMenuSettings_LandscapeScreenLayout", value) .apply() } + var portraitScreenLayout: Int + get() = preferences.getInt( + "EmulationMenuSettings_PortraitScreenLayout", + PortraitScreenLayout.TOP_FULL_WIDTH.int + ) + set(value) { + preferences.edit() + .putInt("EmulationMenuSettings_PortraitScreenLayout", value) + .apply() + } var showFps: Boolean get() = preferences.getBoolean("EmulationMenuSettings_ShowFps", false) set(value) { diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp index 6abfbf2f1..ff016a8e5 100644 --- a/src/android/app/src/main/jni/config.cpp +++ b/src/android/app/src/main/jni/config.cpp @@ -174,7 +174,7 @@ void Config::ReadValues() { // Layout Settings::values.layout_option = static_cast(sdl2_config->GetInteger( - "Layout", "layout_option", static_cast(Settings::LayoutOption::MobileLandscape))); + "Layout", "layout_option", static_cast(Settings::LayoutOption::LargeScreen))); ReadSetting("Layout", Settings::values.custom_layout); ReadSetting("Layout", Settings::values.custom_top_x); ReadSetting("Layout", Settings::values.custom_top_y); @@ -188,7 +188,10 @@ void Config::ReadValues() { ReadSetting("Layout", Settings::values.cardboard_x_shift); ReadSetting("Layout", Settings::values.cardboard_y_shift); - ReadSetting("Layout", Settings::values.custom_portrait_layout); + Settings::values.portrait_layout_option = + static_cast(sdl2_config->GetInteger( + "Layout", "portrait_layout_option", + static_cast(Settings::PortraitLayoutOption::PortraitTopFullWidth))); ReadSetting("Layout", Settings::values.custom_portrait_top_x); ReadSetting("Layout", Settings::values.custom_portrait_top_y); ReadSetting("Layout", Settings::values.custom_portrait_top_width); diff --git a/src/android/app/src/main/jni/default_ini.h b/src/android/app/src/main/jni/default_ini.h index 02bc7f41b..e7e67e646 100644 --- a/src/android/app/src/main/jni/default_ini.h +++ b/src/android/app/src/main/jni/default_ini.h @@ -179,15 +179,15 @@ anaglyph_shader_name = filter_mode = [Layout] -# Layout for the screen inside the render window. -# 0 (default): Default Top Bottom Screen, 1: Single Screen Only, 2: Large Screen Small Screen, 3: Side by Side +# Layout for the screen inside the render window, landscape mode +# 0 (default): Default Top Bottom Screen, +# 1: Single Screen Only, +# 2: Large Screen Small Screen +# 3: Side by Side +# 4: Hybrid +# 5: Custom Layout layout_option = -# Toggle custom layout (using the settings below) on or off. -# Only applies to landscape on Android -# 0 (default): Off, 1: On -custom_layout = - # Screen placement when using Custom layout option # 0x, 0y is the top left corner of the render window. custom_top_x = @@ -199,12 +199,12 @@ custom_bottom_y = custom_bottom_width = custom_bottom_height = -# Custom Layout Options for Android Portrait Mode -# Toggle custom layout (using the settings below) on or off. -# 0 (default): Off, 1: On -custom_portrait_layout = +# Layout for the portrait mode +# 0 (default): Top and bottom screens at top, full width +# 1: Custom Layout +portrait_layout_option = -# Screen placement when using Custom layout option +# Screen placement when using Portrait Custom layout option # 0x, 0y is the top left corner of the render window. custom_portrait_top_x = custom_portrait_top_y = diff --git a/src/android/app/src/main/jni/emu_window/emu_window.cpp b/src/android/app/src/main/jni/emu_window/emu_window.cpp index e847cdb2b..c589ce54c 100644 --- a/src/android/app/src/main/jni/emu_window/emu_window.cpp +++ b/src/android/app/src/main/jni/emu_window/emu_window.cpp @@ -27,6 +27,12 @@ static void UpdateLandscapeScreenLayout() { IDCache::GetNativeLibraryClass(), IDCache::GetLandscapeScreenLayout())); } +static void UpdatePortraitScreenLayout() { + Settings::values.portrait_layout_option = + static_cast(IDCache::GetEnvForThread()->CallStaticIntMethod( + IDCache::GetNativeLibraryClass(), IDCache::GetPortraitScreenLayout())); +} + bool EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) { if (render_window == surface) { return false; @@ -58,6 +64,7 @@ void EmuWindow_Android::OnTouchMoved(int x, int y) { void EmuWindow_Android::OnFramebufferSizeChanged() { UpdateLandscapeScreenLayout(); + UpdatePortraitScreenLayout(); const bool is_portrait_mode{IsPortraitMode()}; const int bigger{window_width > window_height ? window_width : window_height}; diff --git a/src/android/app/src/main/jni/id_cache.cpp b/src/android/app/src/main/jni/id_cache.cpp index 1b36bfd32..b3bf27f96 100644 --- a/src/android/app/src/main/jni/id_cache.cpp +++ b/src/android/app/src/main/jni/id_cache.cpp @@ -27,6 +27,7 @@ static jclass s_native_library_class; static jmethodID s_on_core_error; static jmethodID s_is_portrait_mode; static jmethodID s_landscape_screen_layout; +static jmethodID s_portrait_screen_layout; static jmethodID s_exit_emulation_activity; static jmethodID s_request_camera_permission; static jmethodID s_request_mic_permission; @@ -90,6 +91,10 @@ jmethodID GetLandscapeScreenLayout() { return s_landscape_screen_layout; } +jmethodID GetPortraitScreenLayout() { + return s_portrait_screen_layout; +} + jmethodID GetExitEmulationActivity() { return s_exit_emulation_activity; } @@ -174,6 +179,8 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) { s_is_portrait_mode = env->GetStaticMethodID(s_native_library_class, "isPortraitMode", "()Z"); s_landscape_screen_layout = env->GetStaticMethodID(s_native_library_class, "landscapeScreenLayout", "()I"); + s_portrait_screen_layout = + env->GetStaticMethodID(s_native_library_class, "portraitScreenLayout", "()I"); s_exit_emulation_activity = env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V"); s_request_camera_permission = diff --git a/src/android/app/src/main/jni/id_cache.h b/src/android/app/src/main/jni/id_cache.h index 7c08a1c0b..71a1cb67c 100644 --- a/src/android/app/src/main/jni/id_cache.h +++ b/src/android/app/src/main/jni/id_cache.h @@ -27,6 +27,7 @@ jmethodID GetDisplayAlertPrompt(); jmethodID GetAlertPromptButton(); jmethodID GetIsPortraitMode(); jmethodID GetLandscapeScreenLayout(); +jmethodID GetPortraitScreenLayout(); jmethodID GetExitEmulationActivity(); jmethodID GetRequestCameraPermission(); jmethodID GetRequestMicPermission(); diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 471be3d7c..ff1636730 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -347,9 +347,23 @@ void JNICALL Java_io_github_lime3ds_android_NativeLibrary_enableAdrenoTurboMode( } void Java_io_github_lime3ds_android_NativeLibrary_notifyOrientationChange( - [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jobject obj, jint layout_option, jint rotation) { + [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jobject obj, jint layout_option, jint rotation, + jboolean portrait) { Settings::values.layout_option = static_cast(layout_option); auto& system = Core::System::GetInstance(); + if (system.IsPoweredOn()) { + + system.GPU().Renderer().UpdateCurrentFramebufferLayout(portrait); + } + InputManager::screen_rotation = rotation; + Camera::NDK::g_rotation = rotation; +} + +void Java_io_github_lime3ds_android_NativeLibrary_notifyPortraitLayoutChange( + [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jobject obj, jint layout_option, jint rotation) { + Settings::values.portrait_layout_option = + static_cast(layout_option); + auto& system = Core::System::GetInstance(); if (system.IsPoweredOn()) { system.GPU().Renderer().UpdateCurrentFramebufferLayout(!(rotation % 2)); } diff --git a/src/android/app/src/main/res/menu/menu_emulation.xml b/src/android/app/src/main/res/menu/menu_emulation.xml index a0dec63be..d09f99d93 100644 --- a/src/android/app/src/main/res/menu/menu_emulation.xml +++ b/src/android/app/src/main/res/menu/menu_emulation.xml @@ -69,7 +69,7 @@ + + - - @@ -23,6 +19,9 @@ android:id="@+id/menu_screen_layout_hybrid" android:title="@string/emulation_screen_layout_hybrid" /> + diff --git a/src/android/app/src/main/res/menu/menu_portrait_screen_layout.xml b/src/android/app/src/main/res/menu/menu_portrait_screen_layout.xml new file mode 100644 index 000000000..33bb729a6 --- /dev/null +++ b/src/android/app/src/main/res/menu/menu_portrait_screen_layout.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 72d63373e..744683d30 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -359,12 +359,15 @@ Open Settings Open Cheats Landscape Screen Layout + Portrait Screen Layout Default Portrait Single Screen Side by Side Screens Hybrid Screens - Cycle Landscape Layouts + Default + Custom Layout + Cycle Layouts Swap Screens Reset Overlay Show Overlay diff --git a/src/common/settings.cpp b/src/common/settings.cpp index dbf85e710..e53644328 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -108,6 +108,7 @@ void LogSettings() { log_setting("Renderer_AnaglyphShader", values.anaglyph_shader_name.GetValue()); } log_setting("Layout_LayoutOption", values.layout_option.GetValue()); + log_setting("Layout_PortraitLayoutOption", values.portrait_layout_option.GetValue()); log_setting("Layout_SwapScreen", values.swap_screen.GetValue()); log_setting("Layout_UprightScreen", values.upright_screen.GetValue()); log_setting("Layout_LargeScreenProportion", values.large_screen_proportion.GetValue()); @@ -196,6 +197,7 @@ void RestoreGlobalState(bool is_powered_on) { values.texture_filter.SetGlobal(true); values.texture_sampling.SetGlobal(true); values.layout_option.SetGlobal(true); + values.portrait_layout_option.SetGlobal(true); values.swap_screen.SetGlobal(true); values.upright_screen.SetGlobal(true); values.large_screen_proportion.SetGlobal(true); diff --git a/src/common/settings.h b/src/common/settings.h index 3f294ae05..4d91f81b4 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -33,6 +33,7 @@ enum class InitTicks : u32 { Fixed = 1, }; +/** Defines the layout option for desktop and mobile landscape */ enum class LayoutOption : u32 { Default, SingleScreen, @@ -42,18 +43,20 @@ enum class LayoutOption : u32 { SeparateWindows, #endif HybridScreen, -#ifndef ANDROID // TODO: Implement custom layouts on Android CustomLayout, -#endif - // Similiar to default, but better for mobile devices in portrait mode. Top screen in clamped to - // the top of the frame, and the bottom screen is enlarged to match the top screen. - MobilePortrait, // Similiar to LargeScreen, but better for mobile devices in landscape mode. The screens are // clamped to the top of the frame, and the bottom screen is a bit bigger. MobileLandscape, }; +/** Defines the layout option for mobile portrait */ +enum class PortraitLayoutOption : u32 { + // formerly mobile portrait + PortraitTopFullWidth, + PortraitCustomLayout, +}; + enum class StereoRenderOption : u32 { Off = 0, SideBySide = 1, @@ -482,21 +485,22 @@ struct Values { SwitchableSetting texture_filter{TextureFilter::None, "texture_filter"}; SwitchableSetting texture_sampling{TextureSampling::GameControlled, "texture_sampling"}; - SwitchableSetting layout_option{LayoutOption::Default, "layout_option"}; SwitchableSetting swap_screen{false, "swap_screen"}; SwitchableSetting upright_screen{false, "upright_screen"}; SwitchableSetting large_screen_proportion{4.f, 1.f, 16.f, "large_screen_proportion"}; + // I think the custom_layout setting below is no longer needed + // since custom layout is now just part of the layout option above? Setting custom_layout{false, "custom_layout"}; Setting custom_top_x{0, "custom_top_x"}; Setting custom_top_y{0, "custom_top_y"}; - Setting custom_top_width{400, "custom_top_width"}; - Setting custom_top_height{240, "custom_top_height"}; - Setting custom_bottom_x{40, "custom_bottom_x"}; - Setting custom_bottom_y{240, "custom_bottom_y"}; - Setting custom_bottom_width{320, "custom_bottom_width"}; - Setting custom_bottom_height{240, "custom_bottom_height"}; + Setting custom_top_width{800, "custom_top_width"}; + Setting custom_top_height{480, "custom_top_height"}; + Setting custom_bottom_x{80, "custom_bottom_x"}; + Setting custom_bottom_y{500, "custom_bottom_y"}; + Setting custom_bottom_width{640, "custom_bottom_width"}; + Setting custom_bottom_height{480, "custom_bottom_height"}; Setting custom_second_layer_opacity{100, "custom_second_layer_opacity"}; SwitchableSetting screen_top_stretch{false, "screen_top_stretch"}; @@ -506,14 +510,15 @@ struct Values { Setting screen_bottom_leftright_padding{0, "screen_bottom_leftright_padding"}; Setting screen_bottom_topbottom_padding{0, "screen_bottom_topbottom_padding"}; - Setting custom_portrait_layout{false, "custom_portrait_layout"}; + SwitchableSetting portrait_layout_option{ + PortraitLayoutOption::PortraitTopFullWidth, "portrait_layout_option"}; Setting custom_portrait_top_x{0, "custom_portrait_top_x"}; Setting custom_portrait_top_y{0, "custom_portrait_top_y"}; - Setting custom_portrait_top_width{400, "custom_portrait_top_width"}; - Setting custom_portrait_top_height{240, "custom_portrait_top_height"}; - Setting custom_portrait_bottom_x{40, "custom_portrait_bottom_x"}; - Setting custom_portrait_bottom_y{240, "custom_portrait_bottom_y"}; - Setting custom_portrait_bottom_width{360, "custom_portrait_bottom_width"}; + Setting custom_portrait_top_width{800, "custom_portrait_top_width"}; + Setting custom_portrait_top_height{480, "custom_portrait_top_height"}; + Setting custom_portrait_bottom_x{80, "custom_portrait_bottom_x"}; + Setting custom_portrait_bottom_y{500, "custom_portrait_bottom_y"}; + Setting custom_portrait_bottom_width{640, "custom_portrait_bottom_width"}; Setting custom_portrait_bottom_height{480, "custom_portrait_bottom_height"}; SwitchableSetting bg_red{0.f, "bg_red"}; diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index 3765986cb..b3733f892 100644 --- a/src/core/frontend/emu_window.cpp +++ b/src/core/frontend/emu_window.cpp @@ -176,22 +176,33 @@ void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) { void EmuWindow::UpdateCurrentFramebufferLayout(u32 width, u32 height, bool is_portrait_mode) { Layout::FramebufferLayout layout; - // If in portrait mode, only the MobilePortrait option really makes sense - const Settings::LayoutOption layout_option = is_portrait_mode - ? Settings::LayoutOption::MobilePortrait - : Settings::values.layout_option.GetValue(); - const auto min_size = - Layout::GetMinimumSizeFromLayout(layout_option, Settings::values.upright_screen.GetValue()); + const Settings::LayoutOption layout_option = Settings::values.layout_option.GetValue(); + const Settings::PortraitLayoutOption portrait_layout_option = + Settings::values.portrait_layout_option.GetValue(); + const auto min_size = is_portrait_mode + ? Layout::GetMinimumSizeFromPortraitLayout() + : Layout::GetMinimumSizeFromLayout( + layout_option, Settings::values.upright_screen.GetValue()); - if ((Settings::values.custom_layout.GetValue() == true && !is_portrait_mode) || - (Settings::values.custom_portrait_layout.GetValue() == true && is_portrait_mode)) { - layout = Layout::CustomFrameLayout(width, height, Settings::values.swap_screen.GetValue(), - is_portrait_mode); + width = std::max(width, min_size.first); + height = std::max(height, min_size.second); + if (is_portrait_mode) { + switch (portrait_layout_option) { + case Settings::PortraitLayoutOption::PortraitTopFullWidth: + layout = Layout::PortraitTopFullFrameLayout(width, height, + Settings::values.swap_screen.GetValue()); + break; + case Settings::PortraitLayoutOption::PortraitCustomLayout: + layout = Layout::CustomFrameLayout( + width, height, Settings::values.swap_screen.GetValue(), is_portrait_mode); + break; + } } else { - width = std::max(width, min_size.first); - height = std::max(height, min_size.second); - switch (layout_option) { + case Settings::LayoutOption::CustomLayout: + layout = Layout::CustomFrameLayout( + width, height, Settings::values.swap_screen.GetValue(), is_portrait_mode); + break; case Settings::LayoutOption::SingleScreen: layout = Layout::SingleFrameLayout(width, height, Settings::values.swap_screen.GetValue(), @@ -221,21 +232,12 @@ void EmuWindow::UpdateCurrentFramebufferLayout(u32 width, u32 height, bool is_po Settings::values.upright_screen.GetValue()); break; #endif - case Settings::LayoutOption::MobilePortrait: - layout = Layout::MobilePortraitFrameLayout(width, height, - Settings::values.swap_screen.GetValue()); - break; case Settings::LayoutOption::MobileLandscape: layout = Layout::LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(), false, 2.25f, Layout::VerticalAlignment::Top); break; -#ifndef ANDROID // TODO: Implement custom layouts on Android - case Settings::LayoutOption::CustomLayout: - layout = - Layout::CustomFrameLayout(width, height, Settings::values.swap_screen.GetValue()); - break; -#endif + case Settings::LayoutOption::Default: default: layout = @@ -243,8 +245,9 @@ void EmuWindow::UpdateCurrentFramebufferLayout(u32 width, u32 height, bool is_po Settings::values.upright_screen.GetValue()); break; } - UpdateMinimumWindowSize(min_size); } + UpdateMinimumWindowSize(min_size); + if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::CardboardVR) { layout = Layout::GetCardboardSettings(layout); } diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp index c55444feb..8c67aa7c1 100644 --- a/src/core/frontend/framebuffer_layout.cpp +++ b/src/core/frontend/framebuffer_layout.cpp @@ -117,7 +117,7 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height, bool swapped, bool u return res; } -FramebufferLayout MobilePortraitFrameLayout(u32 width, u32 height, bool swapped) { +FramebufferLayout PortraitTopFullFrameLayout(u32 width, u32 height, bool swapped) { ASSERT(width > 0); ASSERT(height > 0); @@ -419,119 +419,126 @@ FramebufferLayout CustomFrameLayout(u32 width, u32 height, bool is_swapped, bool return res; } -FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale, bool is_secondary) { - bool is_portrait_mode = - Settings::values.layout_option.GetValue() == Settings::LayoutOption::MobilePortrait; - if (Settings::values.custom_layout.GetValue() == true && !is_portrait_mode) { - return CustomFrameLayout(std::max(Settings::values.custom_top_x.GetValue() + - Settings::values.custom_top_width.GetValue(), - Settings::values.custom_bottom_x.GetValue() + - Settings::values.custom_bottom_width.GetValue()), - std::max(Settings::values.custom_top_y.GetValue() + - Settings::values.custom_top_height.GetValue(), - Settings::values.custom_bottom_y.GetValue() + - Settings::values.custom_bottom_height.GetValue()), - Settings::values.swap_screen.GetValue(), is_portrait_mode); - } else if (Settings::values.custom_portrait_layout.GetValue() == true && is_portrait_mode) { - return CustomFrameLayout( - std::max(Settings::values.custom_portrait_top_x.GetValue() + - Settings::values.custom_portrait_top_width.GetValue(), - Settings::values.custom_portrait_bottom_x.GetValue() + - Settings::values.custom_portrait_bottom_width.GetValue()), - std::max(Settings::values.custom_portrait_top_y.GetValue() + - Settings::values.custom_portrait_top_height.GetValue(), - Settings::values.custom_portrait_bottom_y.GetValue() + - Settings::values.custom_portrait_bottom_height.GetValue()), - Settings::values.swap_screen.GetValue(), is_portrait_mode); - } - +FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale, bool is_secondary, + bool is_portrait) { int width, height; - switch (Settings::values.layout_option.GetValue()) { - case Settings::LayoutOption::SingleScreen: -#ifndef ANDROID - case Settings::LayoutOption::SeparateWindows: -#endif - { - const bool swap_screens = is_secondary || Settings::values.swap_screen.GetValue(); - if (swap_screens) { - width = Core::kScreenBottomWidth * res_scale; - height = Core::kScreenBottomHeight * res_scale; - } else { + if (is_portrait) { + auto layout_option = Settings::values.portrait_layout_option.GetValue(); + switch (layout_option) { + case Settings::PortraitLayoutOption::PortraitCustomLayout: + return CustomFrameLayout( + std::max(Settings::values.custom_portrait_top_x.GetValue() + + Settings::values.custom_portrait_top_width.GetValue(), + Settings::values.custom_portrait_bottom_x.GetValue() + + Settings::values.custom_portrait_bottom_width.GetValue()), + std::max(Settings::values.custom_portrait_top_y.GetValue() + + Settings::values.custom_portrait_top_height.GetValue(), + Settings::values.custom_portrait_bottom_y.GetValue() + + Settings::values.custom_portrait_bottom_height.GetValue()), + Settings::values.swap_screen.GetValue(), is_portrait); + case Settings::PortraitLayoutOption::PortraitTopFullWidth: width = Core::kScreenTopWidth * res_scale; - height = Core::kScreenTopHeight * res_scale; + height = (Core::kScreenTopHeight + Core::kScreenBottomHeight) * res_scale; + return PortraitTopFullFrameLayout(width, height, + Settings::values.swap_screen.GetValue()); } - if (Settings::values.upright_screen.GetValue()) { - std::swap(width, height); - } - return SingleFrameLayout(width, height, swap_screens, - Settings::values.upright_screen.GetValue()); - } + } else { + auto layout_option = Settings::values.layout_option.GetValue(); + switch (layout_option) { + case Settings::LayoutOption::CustomLayout: + return CustomFrameLayout(std::max(Settings::values.custom_top_x.GetValue() + + Settings::values.custom_top_width.GetValue(), + Settings::values.custom_bottom_x.GetValue() + + Settings::values.custom_bottom_width.GetValue()), + std::max(Settings::values.custom_top_y.GetValue() + + Settings::values.custom_top_height.GetValue(), + Settings::values.custom_bottom_y.GetValue() + + Settings::values.custom_bottom_height.GetValue()), + Settings::values.swap_screen.GetValue(), is_portrait); - case Settings::LayoutOption::LargeScreen: - if (Settings::values.swap_screen.GetValue()) { - width = (Core::kScreenBottomWidth + + case Settings::LayoutOption::SingleScreen: +#ifndef ANDROID + case Settings::LayoutOption::SeparateWindows: +#endif + { + const bool swap_screens = is_secondary || Settings::values.swap_screen.GetValue(); + if (swap_screens) { + width = Core::kScreenBottomWidth * res_scale; + height = Core::kScreenBottomHeight * res_scale; + } else { + width = Core::kScreenTopWidth * res_scale; + height = Core::kScreenTopHeight * res_scale; + } + if (Settings::values.upright_screen.GetValue()) { + std::swap(width, height); + } + return SingleFrameLayout(width, height, swap_screens, + Settings::values.upright_screen.GetValue()); + } + + case Settings::LayoutOption::LargeScreen: + if (Settings::values.swap_screen.GetValue()) { + width = + (Core::kScreenBottomWidth + Core::kScreenTopWidth / static_cast(Settings::values.large_screen_proportion.GetValue())) * res_scale; - height = Core::kScreenBottomHeight * res_scale; - } else { - width = (Core::kScreenTopWidth + + height = Core::kScreenBottomHeight * res_scale; + } else { + width = + (Core::kScreenTopWidth + Core::kScreenBottomWidth / static_cast(Settings::values.large_screen_proportion.GetValue())) * res_scale; + height = Core::kScreenTopHeight * res_scale; + } + if (Settings::values.upright_screen.GetValue()) { + std::swap(width, height); + } + return LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(), + Settings::values.upright_screen.GetValue(), + Settings::values.large_screen_proportion.GetValue(), + VerticalAlignment::Bottom); + + case Settings::LayoutOption::SideScreen: + width = (Core::kScreenTopWidth + Core::kScreenBottomWidth) * res_scale; height = Core::kScreenTopHeight * res_scale; + + if (Settings::values.upright_screen.GetValue()) { + std::swap(width, height); + } + return LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(), + Settings::values.upright_screen.GetValue(), 1, + VerticalAlignment::Middle); + + case Settings::LayoutOption::MobileLandscape: { + constexpr float large_screen_proportion = 2.25f; + if (Settings::values.swap_screen.GetValue()) { + width = (Core::kScreenBottomWidth + + static_cast(Core::kScreenTopWidth / large_screen_proportion)) * + res_scale; + height = Core::kScreenBottomHeight * res_scale; + } else { + width = (Core::kScreenTopWidth + + static_cast(Core::kScreenBottomWidth / large_screen_proportion)) * + res_scale; + height = Core::kScreenTopHeight * res_scale; + } + return LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(), false, + large_screen_proportion, VerticalAlignment::Top); } - if (Settings::values.upright_screen.GetValue()) { - std::swap(width, height); + + case Settings::LayoutOption::Default: + default: + width = Core::kScreenTopWidth * res_scale; + height = (Core::kScreenTopHeight + Core::kScreenBottomHeight) * res_scale; + + if (Settings::values.upright_screen.GetValue()) { + std::swap(width, height); + } + return DefaultFrameLayout(width, height, Settings::values.swap_screen.GetValue(), + Settings::values.upright_screen.GetValue()); } - return LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(), - Settings::values.upright_screen.GetValue(), - Settings::values.large_screen_proportion.GetValue(), - VerticalAlignment::Bottom); - - case Settings::LayoutOption::SideScreen: - width = (Core::kScreenTopWidth + Core::kScreenBottomWidth) * res_scale; - height = Core::kScreenTopHeight * res_scale; - - if (Settings::values.upright_screen.GetValue()) { - std::swap(width, height); - } - return LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(), - Settings::values.upright_screen.GetValue(), 1, - VerticalAlignment::Middle); - - case Settings::LayoutOption::MobilePortrait: - width = Core::kScreenTopWidth * res_scale; - height = (Core::kScreenTopHeight + Core::kScreenBottomHeight) * res_scale; - return MobilePortraitFrameLayout(width, height, Settings::values.swap_screen.GetValue()); - - case Settings::LayoutOption::MobileLandscape: { - constexpr float large_screen_proportion = 2.25f; - if (Settings::values.swap_screen.GetValue()) { - width = (Core::kScreenBottomWidth + - static_cast(Core::kScreenTopWidth / large_screen_proportion)) * - res_scale; - height = Core::kScreenBottomHeight * res_scale; - } else { - width = (Core::kScreenTopWidth + - static_cast(Core::kScreenBottomWidth / large_screen_proportion)) * - res_scale; - height = Core::kScreenTopHeight * res_scale; - } - return LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(), false, - large_screen_proportion, VerticalAlignment::Top); - } - - case Settings::LayoutOption::Default: - default: - width = Core::kScreenTopWidth * res_scale; - height = (Core::kScreenTopHeight + Core::kScreenBottomHeight) * res_scale; - - if (Settings::values.upright_screen.GetValue()) { - std::swap(width, height); - } - return DefaultFrameLayout(width, height, Settings::values.swap_screen.GetValue(), - Settings::values.upright_screen.GetValue()); } UNREACHABLE(); } @@ -554,11 +561,27 @@ FramebufferLayout GetCardboardSettings(const FramebufferLayout& layout) { u32 cardboard_screen_width; u32 cardboard_screen_height; - switch (Settings::values.layout_option.GetValue()) { - case Settings::LayoutOption::MobileLandscape: - case Settings::LayoutOption::SideScreen: - // If orientation is portrait, only use MobilePortrait - if (!is_portrait) { + if (is_portrait) { + switch (Settings::values.portrait_layout_option.GetValue()) { + case Settings::PortraitLayoutOption::PortraitTopFullWidth: + cardboard_screen_width = top_screen_width; + cardboard_screen_height = top_screen_height + bottom_screen_height; + bottom_screen_left += (top_screen_width - bottom_screen_width) / 2; + if (is_swapped) + top_screen_top += bottom_screen_height; + else + bottom_screen_top += top_screen_height; + break; + default: + cardboard_screen_width = is_swapped ? bottom_screen_width : top_screen_width; + cardboard_screen_height = is_swapped ? bottom_screen_height : top_screen_height; + } + } else { + switch (Settings::values.layout_option.GetValue()) { + case Settings::LayoutOption::MobileLandscape: + case Settings::LayoutOption::SideScreen: + // If orientation is portrait, only use MobilePortrait + cardboard_screen_width = top_screen_width + bottom_screen_width; cardboard_screen_height = is_swapped ? bottom_screen_height : top_screen_height; if (is_swapped) @@ -566,28 +589,14 @@ FramebufferLayout GetCardboardSettings(const FramebufferLayout& layout) { else bottom_screen_left += top_screen_width; break; - } else { - [[fallthrough]]; - } - case Settings::LayoutOption::SingleScreen: - default: - if (!is_portrait) { - // Default values when using LayoutOption::SingleScreen + + case Settings::LayoutOption::SingleScreen: + default: + cardboard_screen_width = is_swapped ? bottom_screen_width : top_screen_width; cardboard_screen_height = is_swapped ? bottom_screen_height : top_screen_height; break; - } else { - [[fallthrough]]; } - case Settings::LayoutOption::MobilePortrait: - cardboard_screen_width = top_screen_width; - cardboard_screen_height = top_screen_height + bottom_screen_height; - bottom_screen_left += (top_screen_width - bottom_screen_width) / 2; - if (is_swapped) - top_screen_top += bottom_screen_height; - else - bottom_screen_top += top_screen_height; - break; } s32 cardboard_max_x_shift = (layout.width / 2 - cardboard_screen_width) / 2; s32 cardboard_user_x_shift = @@ -620,6 +629,15 @@ FramebufferLayout GetCardboardSettings(const FramebufferLayout& layout) { return new_layout; } +/*f + * TODO: remove this? + */ +std::pair GetMinimumSizeFromPortraitLayout() { + u32 min_width, min_height; + min_width = Core::kScreenTopWidth; + min_height = Core::kScreenTopHeight + Core::kScreenBottomHeight; + return std::make_pair(min_width, min_height); +} std::pair GetMinimumSizeFromLayout(Settings::LayoutOption layout, bool upright_screen) { diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h index 37cbb564c..98565ed7c 100644 --- a/src/core/frontend/framebuffer_layout.h +++ b/src/core/frontend/framebuffer_layout.h @@ -85,13 +85,14 @@ struct FramebufferLayout { FramebufferLayout DefaultFrameLayout(u32 width, u32 height, bool is_swapped, bool upright); /** - * Factory method for constructing a mobile portrait FramebufferLayout + * Factory method for constructing the mobile Full Width Top layout + * Two screens at top, full width, no gap between them * @param width Window framebuffer width in pixels * @param height Window framebuffer height in pixels * @param is_swapped if true, the bottom screen will be displayed above the top screen * @return Newly created FramebufferLayout object with mobile portrait screen regions initialized */ -FramebufferLayout MobilePortraitFrameLayout(u32 width, u32 height, bool is_swapped); +FramebufferLayout PortraitTopFullFrameLayout(u32 width, u32 height, bool is_swapped); /** * Factory method for constructing a FramebufferLayout with only the top or bottom screen @@ -152,8 +153,10 @@ FramebufferLayout CustomFrameLayout(u32 width, u32 height, bool is_swapped, * Convenience method to get frame layout by resolution scale * Read from the current settings to determine which layout to use. * @param res_scale resolution scale factor + * @param is_portrait_mode defaults to false */ -FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale, bool is_secondary = false); +FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale, bool is_secondary = false, + bool is_portrait_mode = false); /** * Convenience method for transforming a frame layout when using Cardboard VR @@ -165,4 +168,6 @@ FramebufferLayout GetCardboardSettings(const FramebufferLayout& layout); std::pair GetMinimumSizeFromLayout(Settings::LayoutOption layout, bool upright_screen); +std::pair GetMinimumSizeFromPortraitLayout(); + } // namespace Layout diff --git a/src/lime/config.cpp b/src/lime/config.cpp index e18de2ad2..49962b1d2 100644 --- a/src/lime/config.cpp +++ b/src/lime/config.cpp @@ -183,7 +183,7 @@ void Config::ReadValues() { ReadSetting("Layout", Settings::values.screen_bottom_leftright_padding); ReadSetting("Layout", Settings::values.screen_bottom_topbottom_padding); - ReadSetting("Layout", Settings::values.custom_portrait_layout); + ReadSetting("Layout", Settings::values.portrait_layout_option); ReadSetting("Layout", Settings::values.custom_portrait_top_x); ReadSetting("Layout", Settings::values.custom_portrait_top_y); ReadSetting("Layout", Settings::values.custom_portrait_top_width); diff --git a/src/lime_qt/configuration/config.cpp b/src/lime_qt/configuration/config.cpp index b26507fb2..ce5c632ac 100644 --- a/src/lime_qt/configuration/config.cpp +++ b/src/lime_qt/configuration/config.cpp @@ -538,7 +538,7 @@ void Config::ReadLayoutValues() { ReadBasicSetting(Settings::values.screen_bottom_stretch); ReadBasicSetting(Settings::values.screen_bottom_leftright_padding); ReadBasicSetting(Settings::values.screen_bottom_topbottom_padding); - ReadBasicSetting(Settings::values.custom_portrait_layout); + ReadBasicSetting(Settings::values.portrait_layout_option); ReadBasicSetting(Settings::values.custom_portrait_top_x); ReadBasicSetting(Settings::values.custom_portrait_top_y); ReadBasicSetting(Settings::values.custom_portrait_top_width); @@ -1099,7 +1099,6 @@ void Config::SaveLayoutValues() { WriteBasicSetting(Settings::values.screen_bottom_stretch); WriteBasicSetting(Settings::values.screen_bottom_leftright_padding); WriteBasicSetting(Settings::values.screen_bottom_topbottom_padding); - WriteBasicSetting(Settings::values.custom_portrait_layout); WriteBasicSetting(Settings::values.custom_portrait_top_x); WriteBasicSetting(Settings::values.custom_portrait_top_y); WriteBasicSetting(Settings::values.custom_portrait_top_width); diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 514ec389b..80cf549e9 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -634,6 +634,10 @@ void RendererOpenGL::DrawSingleScreenStereo(const ScreenInfo& screen_info_l, * Draws the emulated screens to the emulator window. */ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool flipped) { + bool isPortrait = false; +#ifdef ANDROID + isPortrait = layout.height > layout.width; +#endif if (settings.bg_color_update_requested.exchange(false)) { // Update background color before drawing glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(), @@ -675,12 +679,12 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool f if (!Settings::values.swap_screen.GetValue()) { DrawTopScreen(layout, top_screen); glUniform1i(uniform_layer, 0); - ApplySecondLayerOpacity(); + ApplySecondLayerOpacity(isPortrait); DrawBottomScreen(layout, bottom_screen); } else { DrawBottomScreen(layout, bottom_screen); glUniform1i(uniform_layer, 0); - ApplySecondLayerOpacity(); + ApplySecondLayerOpacity(isPortrait); DrawTopScreen(layout, top_screen); } @@ -692,11 +696,17 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool f DrawBottomScreen(layout, additional_screen); } } - ResetSecondLayerOpacity(); + ResetSecondLayerOpacity(isPortrait); } -void RendererOpenGL::ApplySecondLayerOpacity() { -#ifndef ANDROID // TODO: Implement custom layouts on Android +void RendererOpenGL::ApplySecondLayerOpacity(bool isPortrait) { +#ifdef ANDROID + // TODO: Allow for second layer opacity in portrait mode android + if (isPortrait) { + return; + } +#endif + if ((Settings::values.layout_option.GetValue() == Settings::LayoutOption::CustomLayout || Settings::values.custom_layout) && Settings::values.custom_second_layer_opacity.GetValue() < 100) { @@ -706,11 +716,16 @@ void RendererOpenGL::ApplySecondLayerOpacity() { state.blend.dst_rgb_func = GL_ONE_MINUS_CONSTANT_ALPHA; state.blend.color.alpha = Settings::values.custom_second_layer_opacity.GetValue() / 100.0f; } -#endif } -void RendererOpenGL::ResetSecondLayerOpacity() { -#ifndef ANDROID // TODO: Implement custom layouts on Android +void RendererOpenGL::ResetSecondLayerOpacity(bool isPortrait) { +#ifdef ANDROID + // TODO: Allow for second layer opacity in portrait mode android + if (isPortrait) { + return; + } +#endif + if ((Settings::values.layout_option.GetValue() == Settings::LayoutOption::CustomLayout || Settings::values.custom_layout) && Settings::values.custom_second_layer_opacity.GetValue() < 100) { @@ -720,7 +735,6 @@ void RendererOpenGL::ResetSecondLayerOpacity() { state.blend.dst_a_func = GL_ZERO; state.blend.color.alpha = 0.0f; } -#endif } void RendererOpenGL::DrawTopScreen(const Layout::FramebufferLayout& layout, diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index a335f516e..7885a2f5b 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -65,8 +65,8 @@ private: void ConfigureFramebufferTexture(TextureInfo& texture, const Pica::FramebufferConfig& framebuffer); void DrawScreens(const Layout::FramebufferLayout& layout, bool flipped); - void ApplySecondLayerOpacity(); - void ResetSecondLayerOpacity(); + void ApplySecondLayerOpacity(bool isPortrait = false); + void ResetSecondLayerOpacity(bool isPortrait = false); void DrawBottomScreen(const Layout::FramebufferLayout& layout, const Common::Rectangle& bottom_screen); void DrawTopScreen(const Layout::FramebufferLayout& layout,