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.
This commit is contained in:
David Griswold 2026-05-02 11:46:13 +03:00 committed by OpenSauce
parent e3d1da145e
commit 41b4e77206
15 changed files with 57 additions and 23 deletions

View file

@ -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}\",")

View file

@ -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

View file

@ -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),

View file

@ -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

View file

@ -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
}

View file

@ -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

View file

@ -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),

View file

@ -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(

View file

@ -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
}

View file

@ -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

View file

@ -3,7 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_secondary_layout_none"
android:id="@+id/menu_enable_secondary_layout"
android:title="@string/emulation_secondary_display_enable"
android:checkable="true"
android:checked="true"/>
@ -24,8 +24,8 @@
android:checkableBehavior="single"
android:id="@+id/menu_secondary_layout_group">
<item
android:id="@+id/menu_secondary_layout_opposite"
android:title="@string/emulation_secondary_display_opposite" />
android:id="@+id/menu_secondary_layout_reverse_primary"
android:title="@string/emulation_secondary_display_reverse_primary" />
<item
android:id="@+id/menu_secondary_layout_top"

View file

@ -36,8 +36,7 @@
</string-array>
<string-array name="secondaryLayouts">
<item>@string/emulation_secondary_display_default</item>
<item>@string/emulation_secondary_display_opposite</item>
<item>@string/emulation_secondary_display_reverse_primary</item>
<item>@string/emulation_top_screen</item>
<item>@string/emulation_bottom_screen</item>
<item>@string/emulation_screen_layout_sidebyside</item>
@ -54,7 +53,6 @@
</integer-array>
<integer-array name="secondaryLayoutValues">
<item>0</item>
<item>4</item>
<item>1</item>
<item>2</item>

View file

@ -501,6 +501,7 @@
<string name="emulation_switch_screen_layout">Landscape Screen Layout</string>
<string name="emulation_switch_portrait_layout">Portrait Screen Layout</string>
<string name="emulation_secondary_display_enable">Enable Secondary Display</string>
<string name="emulation_secondary_display_enable_description">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</string>
<string name="emulation_switch_secondary_layout">Secondary Display Layout</string>
<string name="emulation_secondary_display_management">Secondary Display</string>
<string name="emulation_select_secondary_display">Choose Display</string>
@ -513,7 +514,7 @@
<string name="emulation_screen_layout_original">Original</string>
<string name="emulation_portrait_layout_top_full">Default</string>
<string name="emulation_secondary_display_default">None (system default)</string>
<string name="emulation_secondary_display_opposite">Opposite Screen</string>
<string name="emulation_secondary_display_reverse_primary">Opposite Screen</string>
<string name="emulation_screen_layout_custom">Custom Layout</string>
<string name="bg_color">Background Color</string>
<string name="bg_color_description">The color which appears behind the screens during emulation, represented as an RGB value.</string>

View file

@ -62,7 +62,7 @@ enum class SecondaryDisplayLayout : u32 {
TopScreenOnly,
BottomScreenOnly,
SideBySide,
OppositeScreenOnly,
ReversePrimaryScreen,
Original,
Hybrid,
LargeScreen
@ -558,7 +558,7 @@ struct Values {
SwitchableSetting<bool> swap_screen{false, Keys::swap_screen};
SwitchableSetting<bool> upright_screen{false, Keys::upright_screen};
SwitchableSetting<SecondaryDisplayLayout> secondary_display_layout{
SecondaryDisplayLayout::None, Keys::secondary_display_layout};
SecondaryDisplayLayout::ReversePrimaryScreen, Keys::secondary_display_layout};
SwitchableSetting<std::vector<LayoutOption>> layouts_to_cycle{
{LayoutOption::Default, LayoutOption::SingleScreen, LayoutOption::LargeScreen,
LayoutOption::SideScreen,

View file

@ -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());