From 41b4e772060873066068bf67ea0c063963e3609b Mon Sep 17 00:00:00 2001 From: David Griswold Date: Sat, 2 May 2026 11:46:13 +0300 Subject: [PATCH] Added android-side enable_secondary_display boolean setting, replacing secondary_display_layout = none on the android side. Exposed it in Layout settings, and secondary display layout is now only selectable if it is enabled. Support for the old option is still in the code, but should no longer be selectable. Also renamed opposite to reverse_primary in a few other places. --- CMakeModules/GenerateSettingKeys.cmake | 1 + .../citra_emu/display/ScreenAdjustmentUtil.kt | 11 +++++++++++ .../citra/citra_emu/display/ScreenLayout.kt | 2 ++ .../citra_emu/display/SecondaryDisplay.kt | 6 +++++- .../features/settings/SettingKeys.kt | 1 + .../features/settings/model/BooleanSetting.kt | 1 + .../features/settings/model/IntSetting.kt | 2 +- .../settings/ui/SettingsFragmentPresenter.kt | 12 +++++++++++- .../citra_emu/fragments/EmulationFragment.kt | 19 ++++++++++--------- src/android/app/src/main/jni/default_ini.h | 4 ++++ .../res/menu/menu_secondary_screen_layout.xml | 6 +++--- .../app/src/main/res/values/arrays.xml | 4 +--- .../app/src/main/res/values/strings.xml | 3 ++- src/common/settings.h | 4 ++-- src/core/frontend/framebuffer_layout.cpp | 4 ++-- 15 files changed, 57 insertions(+), 23 deletions(-) diff --git a/CMakeModules/GenerateSettingKeys.cmake b/CMakeModules/GenerateSettingKeys.cmake index 7aff65db5..8054e9afc 100644 --- a/CMakeModules/GenerateSettingKeys.cmake +++ b/CMakeModules/GenerateSettingKeys.cmake @@ -238,6 +238,7 @@ if (ANDROID) "android_hide_images" "screen_orientation" "performance_overlay_position" + "enable_secondary_display" ) string(REPLACE "_" "_1" KEY_JNI_ESCAPED ${KEY}) set(SETTING_KEY_LIST "${SETTING_KEY_LIST}\n\"${KEY}\",") diff --git a/src/android/app/src/main/java/org/citra/citra_emu/display/ScreenAdjustmentUtil.kt b/src/android/app/src/main/java/org/citra/citra_emu/display/ScreenAdjustmentUtil.kt index cf18a175c..d717e87e6 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/display/ScreenAdjustmentUtil.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/display/ScreenAdjustmentUtil.kt @@ -78,6 +78,17 @@ class ScreenAdjustmentUtil( NativeLibrary.updateFramebuffer(NativeLibrary.isPortraitMode) } + fun enableSecondaryDisplay(layoutOption: Int) { + BooleanSetting.ENABLE_SECONDARY_DISPLAY.boolean = true + settings.saveSetting(BooleanSetting.ENABLE_SECONDARY_DISPLAY, SettingsFile.FILE_NAME_CONFIG) + changeSecondaryOrientation(layoutOption) + } + + fun disableSecondaryDisplay() { + BooleanSetting.ENABLE_SECONDARY_DISPLAY.boolean = false + settings.saveSetting(BooleanSetting.ENABLE_SECONDARY_DISPLAY, SettingsFile.FILE_NAME_CONFIG) + } + fun changeActivityOrientation(orientationOption: Int) { val activity = context as? Activity ?: return IntSetting.ORIENTATION_OPTION.int = orientationOption diff --git a/src/android/app/src/main/java/org/citra/citra_emu/display/ScreenLayout.kt b/src/android/app/src/main/java/org/citra/citra_emu/display/ScreenLayout.kt index c705c2104..9e72f3894 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/display/ScreenLayout.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/display/ScreenLayout.kt @@ -53,6 +53,8 @@ enum class PortraitScreenLayout(val int: Int) { enum class SecondaryDisplayLayout(val int: Int) { // These must match what is defined in src/common/settings.h + // NONE is no longer selectable in the interface, having been replaced with + // the boolean ENABLE_SECONDARY_DISPLAY setting, but is left here for backwards compatibility NONE(0), TOP_SCREEN(1), BOTTOM_SCREEN(2), diff --git a/src/android/app/src/main/java/org/citra/citra_emu/display/SecondaryDisplay.kt b/src/android/app/src/main/java/org/citra/citra_emu/display/SecondaryDisplay.kt index 5796cb4b0..0a3eee316 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/display/SecondaryDisplay.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/display/SecondaryDisplay.kt @@ -17,6 +17,7 @@ import android.view.SurfaceView import android.view.WindowManager import org.citra.citra_emu.features.settings.model.IntSetting import org.citra.citra_emu.NativeLibrary +import org.citra.citra_emu.features.settings.model.BooleanSetting import org.citra.citra_emu.utils.Log class SecondaryDisplay(val context: Context) : DisplayManager.DisplayListener { @@ -84,7 +85,10 @@ class SecondaryDisplay(val context: Context) : DisplayManager.DisplayListener { } val displayToUse = if (availableDisplays.isEmpty() || - IntSetting.SECONDARY_DISPLAY_LAYOUT.int == SecondaryDisplayLayout.NONE.int + // Theoretically, the NONE option is no longer selectable, but + // I am leaving this in for backwards compatibility + IntSetting.SECONDARY_DISPLAY_LAYOUT.int == SecondaryDisplayLayout.NONE.int || + ! BooleanSetting.ENABLE_SECONDARY_DISPLAY.boolean ) { currentDisplayId = -1 vd.display diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/SettingKeys.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/SettingKeys.kt index 56ffb6789..f9ff306f8 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/SettingKeys.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/SettingKeys.kt @@ -141,4 +141,5 @@ object SettingKeys { external fun android_hide_images(): String external fun screen_orientation(): String external fun performance_overlay_position(): String + external fun enable_secondary_display(): String } \ No newline at end of file diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/BooleanSetting.kt index 0e88dacf3..cda0a5f2f 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/BooleanSetting.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/BooleanSetting.kt @@ -59,6 +59,7 @@ enum class BooleanSetting( ANDROID_HIDE_IMAGES(SettingKeys.android_hide_images(), Settings.SECTION_MISC, false), APPLY_REGION_FREE_PATCH(SettingKeys.apply_region_free_patch(), Settings.SECTION_SYSTEM, true), USE_INTEGER_SCALING(SettingKeys.use_integer_scaling(), Settings.SECTION_RENDERER, false), + ENABLE_SECONDARY_DISPLAY(SettingKeys.enable_secondary_display(), Settings.SECTION_LAYOUT, true), SIMULATE_3DS_GPU_TIMINGS(SettingKeys.simulate_3ds_gpu_timings(), Settings.SECTION_RENDERER, true); override var boolean: Boolean = defaultValue diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/IntSetting.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/IntSetting.kt index eb1a880a5..5eb8944d5 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/IntSetting.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/IntSetting.kt @@ -37,7 +37,7 @@ enum class IntSetting( LANDSCAPE_BOTTOM_HEIGHT(SettingKeys.custom_bottom_height(),Settings.SECTION_LAYOUT,480), SCREEN_GAP(SettingKeys.screen_gap(),Settings.SECTION_LAYOUT,0), PORTRAIT_SCREEN_LAYOUT(SettingKeys.portrait_layout_option(),Settings.SECTION_LAYOUT,0), - SECONDARY_DISPLAY_LAYOUT(SettingKeys.secondary_display_layout(),Settings.SECTION_LAYOUT,0), + SECONDARY_DISPLAY_LAYOUT(SettingKeys.secondary_display_layout(),Settings.SECTION_LAYOUT,4), PORTRAIT_TOP_X(SettingKeys.custom_portrait_top_x(),Settings.SECTION_LAYOUT,0), PORTRAIT_TOP_Y(SettingKeys.custom_portrait_top_y(),Settings.SECTION_LAYOUT,0), PORTRAIT_TOP_WIDTH(SettingKeys.custom_portrait_top_width(),Settings.SECTION_LAYOUT,800), diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt index 94bfe78a9..ad3f9cb8c 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt @@ -1201,6 +1201,15 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) IntSetting.PORTRAIT_SCREEN_LAYOUT.defaultValue ) ) + add ( + SwitchSetting( + BooleanSetting.ENABLE_SECONDARY_DISPLAY, + R.string.emulation_secondary_display_enable, + R.string.emulation_secondary_display_enable_description, + BooleanSetting.ENABLE_SECONDARY_DISPLAY.key, + BooleanSetting.ENABLE_SECONDARY_DISPLAY.defaultValue + ) + ) add( SingleChoiceSetting( IntSetting.SECONDARY_DISPLAY_LAYOUT, @@ -1209,7 +1218,8 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) R.array.secondaryLayouts, R.array.secondaryLayoutValues, IntSetting.SECONDARY_DISPLAY_LAYOUT.key, - IntSetting.SECONDARY_DISPLAY_LAYOUT.defaultValue + IntSetting.SECONDARY_DISPLAY_LAYOUT.defaultValue, + BooleanSetting.ENABLE_SECONDARY_DISPLAY.boolean ) ) add( diff --git a/src/android/app/src/main/java/org/citra/citra_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/citra/citra_emu/fragments/EmulationFragment.kt index d3e6546fc..979e5131b 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/fragments/EmulationFragment.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/fragments/EmulationFragment.kt @@ -1065,26 +1065,27 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram var selectedLayout = IntSetting.SECONDARY_DISPLAY_LAYOUT.int val chooserMenu = popupMenu.menu.findItem(R.id.menu_secondary_choose) - val enableSecondaryCheckbox = popupMenu.menu.findItem(R.id.menu_secondary_layout_none) + val enableSecondaryCheckbox = popupMenu.menu.findItem(R.id.menu_enable_secondary_layout) chooserMenu?.subMenu?.removeGroup(R.id.menu_secondary_management_display_group) val displays = emulationActivity.secondaryDisplayManager.availableDisplays - if (selectedLayout == SecondaryDisplayLayout.NONE.int) { + if (selectedLayout == SecondaryDisplayLayout.NONE.int || !BooleanSetting.ENABLE_SECONDARY_DISPLAY.boolean) { + BooleanSetting.ENABLE_SECONDARY_DISPLAY.boolean = false enableSecondaryCheckbox.isChecked = false chooserMenu.isVisible = false popupMenu.menu.setGroupEnabled(R.id.menu_secondary_layout_group, false) - selectedLayout = SecondaryDisplayLayout.REVERSE_PRIMARY.int + } else { popupMenu.menu.setGroupEnabled(R.id.menu_secondary_layout_group, true) chooserMenu.isVisible = (displays.size > 1) } val layoutOptionMenuItem = when (selectedLayout) { SecondaryDisplayLayout.NONE.int -> - R.id.menu_secondary_layout_opposite + R.id.menu_secondary_layout_reverse_primary SecondaryDisplayLayout.REVERSE_PRIMARY.int -> - R.id.menu_secondary_layout_opposite + R.id.menu_secondary_layout_reverse_primary SecondaryDisplayLayout.TOP_SCREEN.int -> R.id.menu_secondary_layout_top @@ -1130,18 +1131,18 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram popupMenu.setOnMenuItemClickListener { when (it.itemId) { - R.id.menu_secondary_layout_none -> { + R.id.menu_enable_secondary_layout -> { if (!it.isChecked) { - screenAdjustmentUtil.changeSecondaryOrientation(selectedLayout) + screenAdjustmentUtil.enableSecondaryDisplay(selectedLayout) } else { - screenAdjustmentUtil.changeSecondaryOrientation(SecondaryDisplayLayout.NONE.int) + screenAdjustmentUtil.disableSecondaryDisplay() } emulationActivity.secondaryDisplayManager.updateDisplay() showSecondaryScreenLayoutMenu() // reopen menu to get new behaviors true } - R.id.menu_secondary_layout_opposite -> { + R.id.menu_secondary_layout_reverse_primary -> { screenAdjustmentUtil.changeSecondaryOrientation(SecondaryDisplayLayout.REVERSE_PRIMARY.int) true } diff --git a/src/android/app/src/main/jni/default_ini.h b/src/android/app/src/main/jni/default_ini.h index 6864d04b0..cc3d91ec5 100644 --- a/src/android/app/src/main/jni/default_ini.h +++ b/src/android/app/src/main/jni/default_ini.h @@ -354,6 +354,10 @@ static const char* android_config_default_file_content = (BOOST_HANA_STRING(R"( # 0 (default): Off, 1: On )") DECLARE_KEY(expand_to_cutout_area) BOOST_HANA_STRING(R"( +# Allows Azahar to use externally connected displays +# 0: Off, 1: On (default) +)") DECLARE_KEY(enable_secondary_display) BOOST_HANA_STRING(R"( + # Secondary Display Layout # What the game should do if a secondary display is connected physically or using # Miracast / Chromecast screen mirroring diff --git a/src/android/app/src/main/res/menu/menu_secondary_screen_layout.xml b/src/android/app/src/main/res/menu/menu_secondary_screen_layout.xml index 148a1405d..0ed57b51d 100644 --- a/src/android/app/src/main/res/menu/menu_secondary_screen_layout.xml +++ b/src/android/app/src/main/res/menu/menu_secondary_screen_layout.xml @@ -3,7 +3,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto"> @@ -24,8 +24,8 @@ android:checkableBehavior="single" android:id="@+id/menu_secondary_layout_group"> + android:id="@+id/menu_secondary_layout_reverse_primary" + android:title="@string/emulation_secondary_display_reverse_primary" /> - @string/emulation_secondary_display_default - @string/emulation_secondary_display_opposite + @string/emulation_secondary_display_reverse_primary @string/emulation_top_screen @string/emulation_bottom_screen @string/emulation_screen_layout_sidebyside @@ -54,7 +53,6 @@ - 0 4 1 2 diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 7c35ed749..c326fdb39 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -501,6 +501,7 @@ Landscape Screen Layout Portrait Screen Layout Enable Secondary Display + If disabled, Azahar will let Android manage connected displays. If this is enabled and multiple displays are connected, you can select which one Azahar will use in the Emulation Quick Menu Secondary Display Layout Secondary Display Choose Display @@ -513,7 +514,7 @@ Original Default None (system default) - Opposite Screen + Opposite Screen Custom Layout Background Color The color which appears behind the screens during emulation, represented as an RGB value. diff --git a/src/common/settings.h b/src/common/settings.h index f4b29ee10..8de6ea457 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -62,7 +62,7 @@ enum class SecondaryDisplayLayout : u32 { TopScreenOnly, BottomScreenOnly, SideBySide, - OppositeScreenOnly, + ReversePrimaryScreen, Original, Hybrid, LargeScreen @@ -558,7 +558,7 @@ struct Values { SwitchableSetting swap_screen{false, Keys::swap_screen}; SwitchableSetting upright_screen{false, Keys::upright_screen}; SwitchableSetting secondary_display_layout{ - SecondaryDisplayLayout::None, Keys::secondary_display_layout}; + SecondaryDisplayLayout::ReversePrimaryScreen, Keys::secondary_display_layout}; SwitchableSetting> layouts_to_cycle{ {LayoutOption::Default, LayoutOption::SingleScreen, LayoutOption::LargeScreen, LayoutOption::SideScreen, diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp index 52ac1cfd9..948886aff 100644 --- a/src/core/frontend/framebuffer_layout.cpp +++ b/src/core/frontend/framebuffer_layout.cpp @@ -324,8 +324,8 @@ FramebufferLayout AndroidSecondaryLayout(u32 width, u32 height) { return HybridScreenLayout(width, height, false, Settings::values.upright_screen.GetValue()); case Settings::SecondaryDisplayLayout::None: // this should never happen - if "none" is set this method shouldn't run - but if it does, - // somehow, use OppositeScreenOnly - case Settings::SecondaryDisplayLayout::OppositeScreenOnly: + // somehow, use ReversePrimaryScreen + case Settings::SecondaryDisplayLayout::ReversePrimaryScreen: default: return SingleFrameLayout(width, height, !Settings::values.swap_screen.GetValue(), Settings::values.upright_screen.GetValue());