diff --git a/.ci/linux.sh b/.ci/linux.sh index af07c1b05..10219eef3 100755 --- a/.ci/linux.sh +++ b/.ci/linux.sh @@ -19,7 +19,6 @@ cmake .. -G Ninja \ -DCMAKE_C_COMPILER=clang-18 \ "${EXTRA_CMAKE_FLAGS[@]}" \ -DENABLE_QT_TRANSLATION=ON \ - -DCITRA_ENABLE_COMPATIBILITY_REPORTING=ON \ -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \ -DUSE_DISCORD_PRESENCE=ON ninja diff --git a/.ci/macos.sh b/.ci/macos.sh index c4e2a284b..d666e64bb 100755 --- a/.ci/macos.sh +++ b/.ci/macos.sh @@ -7,7 +7,6 @@ cmake .. -GNinja \ -DCMAKE_C_COMPILER_LAUNCHER=ccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ -DENABLE_QT_TRANSLATION=ON \ - -DCITRA_ENABLE_COMPATIBILITY_REPORTING=ON \ -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \ -DUSE_DISCORD_PRESENCE=ON ninja diff --git a/.ci/windows.sh b/.ci/windows.sh index db7ea6255..2762ed43e 100644 --- a/.ci/windows.sh +++ b/.ci/windows.sh @@ -6,7 +6,6 @@ cmake .. -G Ninja \ -DCMAKE_C_COMPILER_LAUNCHER=ccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ -DENABLE_QT_TRANSLATION=ON \ - -DCITRA_ENABLE_COMPATIBILITY_REPORTING=ON \ -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \ -DUSE_DISCORD_PRESENCE=ON ninja diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 563ae0a18..0432d4b6e 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -4,7 +4,7 @@ labels: ["bug"] body: - type: markdown attributes: - value: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with Lime. + value: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with Lime3DS. - type: checkboxes attributes: label: Is there an existing issue for this? diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b39b52128..ee29593f4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -256,4 +256,4 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - asset_paths: '["./**/*.tar.*","./**/*.AppImage","./**/*.zip","./**/*.apk"]' + asset_paths: '["./**/*.tar.*","./**/*.AppImage","./**/*.zip","./**/*.apk","./**/*.aab",]' diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts index 53f8d7549..7561ca055 100644 --- a/src/android/app/build.gradle.kts +++ b/src/android/app/build.gradle.kts @@ -268,6 +268,11 @@ android.applicationVariants.configureEach { include("*.apk") into(layout.buildDirectory.dir("bundle")) } + project.copy { + from(layout.buildDirectory.dir("outputs/bundle/${variant.name}")) + include("*.aab") + into(layout.buildDirectory.dir("bundle")) + } } } tasks.named("bundle${capitalizedName}").configure { finalizedBy(copyTask) } 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..79b77f77c 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 @@ -1,4 +1,4 @@ -// Copyright 2023 Citra Emulator Project +// Copyright Citra Emulator Project / Lime3DS Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -156,9 +156,9 @@ object NativeLibrary { external fun getPerfStats(): DoubleArray /** - * Notifies the core emulation that the orientation has changed. + * Notifies the core emulation that the layout should be updated */ - external fun notifyOrientationChange(layoutOption: Int, rotation: Int) + external fun updateFramebuffer(isPortrait: Boolean) /** * Swaps the top and bottom screens. @@ -262,10 +262,6 @@ object NativeLibrary { get() = LimeApplication.appContext.resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT - @Keep - @JvmStatic - fun landscapeScreenLayout(): Int = EmulationMenuSettings.landscapeScreenLayout - @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/ScreenAdjustmentUtil.kt b/src/android/app/src/main/java/io/github/lime3ds/android/display/ScreenAdjustmentUtil.kt index cd0310047..863b0ba2c 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 @@ -1,9 +1,8 @@ -// Copyright 2023 Citra Emulator Project +// Copyright Citra Emulator Project / Lime3DS Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. package io.github.lime3ds.android.display - import android.view.WindowManager import io.github.lime3ds.android.NativeLibrary import io.github.lime3ds.android.features.settings.model.BooleanSetting @@ -12,8 +11,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 @@ -24,19 +25,35 @@ class ScreenAdjustmentUtil(private val windowManager: WindowManager, BooleanSetting.SWAP_SCREEN.boolean = isEnabled settings.saveSetting(BooleanSetting.SWAP_SCREEN, SettingsFile.FILE_NAME_CONFIG) } - fun cycleLayouts() { - val nextLayout = (EmulationMenuSettings.landscapeScreenLayout + 1) % ScreenLayout.entries.size - changeScreenOrientation(ScreenLayout.from(nextLayout)) + // TODO: figure out how to pull these from R.array + val landscape_values = intArrayOf(6,1,3,4,0,5); + val portrait_values = intArrayOf(0,1); + if (NativeLibrary.isPortraitMode) { + val current_layout = IntSetting.PORTRAIT_SCREEN_LAYOUT.int + val pos = portrait_values.indexOf(current_layout) + val layout_option = portrait_values[(pos + 1) % portrait_values.size] + changePortraitOrientation(layout_option) + } else { + val current_layout = IntSetting.SCREEN_LAYOUT.int + val pos = landscape_values.indexOf(current_layout) + val layout_option = landscape_values[(pos + 1) % landscape_values.size] + changeScreenOrientation(layout_option) + } + } - fun changeScreenOrientation(layoutOption: ScreenLayout) { - EmulationMenuSettings.landscapeScreenLayout = layoutOption.int - NativeLibrary.notifyOrientationChange( - EmulationMenuSettings.landscapeScreenLayout, - windowManager.defaultDisplay.rotation - ) - IntSetting.SCREEN_LAYOUT.int = layoutOption.int + fun changePortraitOrientation(layoutOption: Int) { + IntSetting.PORTRAIT_SCREEN_LAYOUT.int = layoutOption + settings.saveSetting(IntSetting.PORTRAIT_SCREEN_LAYOUT, SettingsFile.FILE_NAME_CONFIG) + NativeLibrary.reloadSettings() + NativeLibrary.updateFramebuffer(NativeLibrary.isPortraitMode) + } + + fun changeScreenOrientation(layoutOption: Int) { + IntSetting.SCREEN_LAYOUT.int = layoutOption settings.saveSetting(IntSetting.SCREEN_LAYOUT, SettingsFile.FILE_NAME_CONFIG) + NativeLibrary.reloadSettings() + NativeLibrary.updateFramebuffer(NativeLibrary.isPortraitMode) } } 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..b7617a85d 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 @@ -1,4 +1,4 @@ -// Copyright 2023 Citra Emulator Project +// Copyright Citra Emulator Project / Lime3DS Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -6,17 +6,30 @@ 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), + ORIGINAL(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 } } } + +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; + } + } +} \ No newline at end of file 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..5c2439683 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 @@ -1,4 +1,4 @@ -// Copyright 2023 Citra Emulator Project +// Copyright Citra Emulator Project / Lime3DS Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -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..25991f002 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 @@ -1,4 +1,4 @@ -// Copyright 2023 Citra Emulator Project +// Copyright Citra Emulator Project / Lime3DS Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -23,6 +23,23 @@ 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), + LANDSCAPE_TOP_X("custom_top_x",Settings.SECTION_LAYOUT,0), + LANDSCAPE_TOP_Y("custom_top_y",Settings.SECTION_LAYOUT,0), + LANDSCAPE_TOP_WIDTH("custom_top_width",Settings.SECTION_LAYOUT,800), + LANDSCAPE_TOP_HEIGHT("custom_top_height",Settings.SECTION_LAYOUT,480), + LANDSCAPE_BOTTOM_X("custom_bottom_x",Settings.SECTION_LAYOUT,80), + LANDSCAPE_BOTTOM_Y("custom_bottom_y",Settings.SECTION_LAYOUT,480), + LANDSCAPE_BOTTOM_WIDTH("custom_bottom_width",Settings.SECTION_LAYOUT,640), + LANDSCAPE_BOTTOM_HEIGHT("custom_bottom_height",Settings.SECTION_LAYOUT,480), + PORTRAIT_SCREEN_LAYOUT("portrait_layout_option",Settings.SECTION_LAYOUT,0), + PORTRAIT_TOP_X("custom_portrait_top_x",Settings.SECTION_LAYOUT,0), + PORTRAIT_TOP_Y("custom_portrait_top_y",Settings.SECTION_LAYOUT,0), + PORTRAIT_TOP_WIDTH("custom_portrait_top_width",Settings.SECTION_LAYOUT,800), + PORTRAIT_TOP_HEIGHT("custom_portrait_top_height",Settings.SECTION_LAYOUT,480), + PORTRAIT_BOTTOM_X("custom_portrait_bottom_x",Settings.SECTION_LAYOUT,80), + PORTRAIT_BOTTOM_Y("custom_portrait_bottom_y",Settings.SECTION_LAYOUT,480), + PORTRAIT_BOTTOM_WIDTH("custom_portrait_bottom_width",Settings.SECTION_LAYOUT,640), + PORTRAIT_BOTTOM_HEIGHT("custom_portrait_bottom_height",Settings.SECTION_LAYOUT,480), 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..f0106d21c 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 @@ -1,4 +1,4 @@ -// Copyright 2023 Citra Emulator Project +// Copyright Citra Emulator Project / Lime3DS Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -109,6 +109,8 @@ class Settings { const val SECTION_AUDIO = "Audio" const val SECTION_DEBUG = "Debugging" const val SECTION_THEME = "Theme" + const val SECTION_CUSTOM_LANDSCAPE = "Custom Landscape Layout" + const val SECTION_CUSTOM_PORTRAIT = "Custom Portrait Layout" const val KEY_BUTTON_A = "button_a" const val KEY_BUTTON_B = "button_b" @@ -131,7 +133,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/features/settings/ui/SettingsActivityPresenter.kt b/src/android/app/src/main/java/io/github/lime3ds/android/features/settings/ui/SettingsActivityPresenter.kt index 65307d0d2..9197081dd 100644 --- a/src/android/app/src/main/java/io/github/lime3ds/android/features/settings/ui/SettingsActivityPresenter.kt +++ b/src/android/app/src/main/java/io/github/lime3ds/android/features/settings/ui/SettingsActivityPresenter.kt @@ -1,4 +1,4 @@ -// Copyright 2023 Citra Emulator Project +// Copyright Citra Emulator Project / Lime3DS Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -7,6 +7,7 @@ package io.github.lime3ds.android.features.settings.ui import android.os.Bundle import android.text.TextUtils import io.github.lime3ds.android.NativeLibrary +import io.github.lime3ds.android.features.settings.model.IntSetting import io.github.lime3ds.android.features.settings.model.Settings import io.github.lime3ds.android.utils.SystemSaveGame import io.github.lime3ds.android.utils.DirectoryInitialization @@ -56,6 +57,9 @@ class SettingsActivityPresenter(private val activityView: SettingsActivityView) Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI...") settings.saveSettings(activityView) SystemSaveGame.save() + //added to ensure that layout changes take effect as soon as settings window closes + NativeLibrary.reloadSettings() + NativeLibrary.updateFramebuffer(NativeLibrary.isPortraitMode) } NativeLibrary.reloadSettings() } diff --git a/src/android/app/src/main/java/io/github/lime3ds/android/features/settings/ui/SettingsAdapter.kt b/src/android/app/src/main/java/io/github/lime3ds/android/features/settings/ui/SettingsAdapter.kt index 6806991a8..0ecfb5384 100644 --- a/src/android/app/src/main/java/io/github/lime3ds/android/features/settings/ui/SettingsAdapter.kt +++ b/src/android/app/src/main/java/io/github/lime3ds/android/features/settings/ui/SettingsAdapter.kt @@ -1,4 +1,4 @@ -// Copyright 2023 Citra Emulator Project +// Copyright Citra Emulator Project / Lime3DS Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -7,13 +7,16 @@ package io.github.lime3ds.android.features.settings.ui import android.annotation.SuppressLint import android.content.Context import android.content.DialogInterface +import android.graphics.Color import android.icu.util.Calendar import android.icu.util.TimeZone +import android.text.Editable import android.text.InputFilter +import android.text.TextWatcher import android.text.format.DateFormat import android.view.LayoutInflater import android.view.ViewGroup -import android.widget.TextView +import android.widget.EditText import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.core.widget.doOnTextChanged @@ -22,6 +25,8 @@ import androidx.recyclerview.widget.RecyclerView import com.google.android.material.datepicker.MaterialDatePicker import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.slider.Slider +import com.google.android.material.textfield.TextInputEditText +import com.google.android.material.textfield.TextInputLayout import com.google.android.material.timepicker.MaterialTimePicker import com.google.android.material.timepicker.TimeFormat import io.github.lime3ds.android.R @@ -73,7 +78,8 @@ class SettingsAdapter( private var clickedPosition: Int private var dialog: AlertDialog? = null private var sliderProgress = 0 - private var textSliderValue: TextView? = null + private var textSliderValue: TextInputEditText? = null + private var textInputLayout: TextInputLayout? = null private var textInputValue: String = "" private var defaultCancelListener = @@ -256,18 +262,35 @@ class SettingsAdapter( val inflater = LayoutInflater.from(context) val sliderBinding = DialogSliderBinding.inflate(inflater) - + textInputLayout = sliderBinding.textInput textSliderValue = sliderBinding.textValue - textSliderValue!!.text = sliderProgress.toString() - sliderBinding.textUnits.text = item.units + textSliderValue!!.setText(sliderProgress.toString()) + textInputLayout!!.suffixText = item.units sliderBinding.slider.apply { valueFrom = item.min.toFloat() valueTo = item.max.toFloat() value = sliderProgress.toFloat() + textSliderValue!!.addTextChangedListener( object : TextWatcher { + override fun afterTextChanged(s: Editable) { + val textValue = s.toString().toIntOrNull(); + if (textValue == null || textValue < valueFrom || textValue > valueTo) { + textInputLayout!!.error ="Inappropriate value" + } else { + textInputLayout!!.error = null + value = textValue.toFloat(); + } + } + override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {} + override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {} + }) + addOnChangeListener { _: Slider, value: Float, _: Boolean -> sliderProgress = value.toInt() - textSliderValue!!.text = sliderProgress.toString() + if (textSliderValue!!.text.toString() != value.toInt().toString()) { + textSliderValue!!.setText(value.toInt().toString()) + textSliderValue!!.setSelection(textSliderValue!!.length()) + } } } diff --git a/src/android/app/src/main/java/io/github/lime3ds/android/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/io/github/lime3ds/android/features/settings/ui/SettingsFragmentPresenter.kt index df0231d4f..68a1cab0f 100644 --- a/src/android/app/src/main/java/io/github/lime3ds/android/features/settings/ui/SettingsFragmentPresenter.kt +++ b/src/android/app/src/main/java/io/github/lime3ds/android/features/settings/ui/SettingsFragmentPresenter.kt @@ -1,4 +1,4 @@ -// Copyright 2023 Citra Emulator Project +// Copyright Citra Emulator Project / Lime3DS Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -6,6 +6,7 @@ package io.github.lime3ds.android.features.settings.ui import android.content.Context import android.content.SharedPreferences +import android.content.res.Resources import android.hardware.camera2.CameraAccessException import android.hardware.camera2.CameraCharacteristics import android.hardware.camera2.CameraManager @@ -42,6 +43,7 @@ import io.github.lime3ds.android.utils.GpuDriverHelper import io.github.lime3ds.android.utils.Log import io.github.lime3ds.android.utils.SystemSaveGame import io.github.lime3ds.android.utils.ThemeUtil +import kotlin.math.min class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) { private var menuTag: String? = null @@ -91,9 +93,12 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) Settings.SECTION_CAMERA -> addCameraSettings(sl) Settings.SECTION_CONTROLS -> addControlsSettings(sl) Settings.SECTION_RENDERER -> addGraphicsSettings(sl) + Settings.SECTION_LAYOUT -> addLayoutSettings(sl) Settings.SECTION_AUDIO -> addAudioSettings(sl) Settings.SECTION_DEBUG -> addDebugSettings(sl) Settings.SECTION_THEME -> addThemeSettings(sl) + Settings.SECTION_CUSTOM_LANDSCAPE -> addCustomLandscapeSettings(sl) + Settings.SECTION_CUSTOM_PORTRAIT -> addCustomPortraitSettings(sl) else -> { fragmentView.showToastMessage("Unimplemented menu", false) return @@ -103,6 +108,23 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) fragmentView.showSettingsList(settingsList!!) } + /** Returns the portrait mode width */ + private fun getWidth(): Int { + val dm = Resources.getSystem().displayMetrics; + return if (dm.widthPixels < dm.heightPixels) + dm.widthPixels + else + dm.heightPixels + } + + private fun getHeight(): Int { + val dm = Resources.getSystem().displayMetrics; + return if (dm.widthPixels < dm.heightPixels) + dm.heightPixels + else + dm.widthPixels + } + private fun addConfigSettings(sl: ArrayList) { settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_settings)) sl.apply { @@ -146,6 +168,14 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) Settings.SECTION_RENDERER ) ) + add( + SubmenuSetting( + R.string.preferences_layout, + 0, + R.drawable.ic_fit_screen, + Settings.SECTION_LAYOUT + ) + ) add( SubmenuSetting( R.string.preferences_audio, @@ -162,6 +192,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) Settings.SECTION_DEBUG ) ) + add( RunnableSetting( R.string.reset_to_default, @@ -873,6 +904,262 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) } } + private fun addLayoutSettings(sl: ArrayList) { + settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_layout)) + sl.apply { + add( + SingleChoiceSetting( + IntSetting.SCREEN_LAYOUT, + R.string.emulation_switch_screen_layout, + 0, + R.array.landscapeLayouts, + R.array.landscapeLayoutValues, + IntSetting.SCREEN_LAYOUT.key, + IntSetting.SCREEN_LAYOUT.defaultValue + ) + ) + add( + SingleChoiceSetting( + IntSetting.PORTRAIT_SCREEN_LAYOUT, + R.string.emulation_switch_portrait_layout, + 0, + R.array.portraitLayouts, + R.array.portraitLayoutValues, + IntSetting.PORTRAIT_SCREEN_LAYOUT.key, + IntSetting.PORTRAIT_SCREEN_LAYOUT.defaultValue + ) + ) + add( + SubmenuSetting( + R.string.emulation_landscape_custom_layout, + 0, + R.drawable.ic_fit_screen, + Settings.SECTION_CUSTOM_LANDSCAPE + ) + ) + add( + SubmenuSetting( + R.string.emulation_portrait_custom_layout, + 0, + R.drawable.ic_portrait_fit_screen, + Settings.SECTION_CUSTOM_PORTRAIT + ) + ) + + + } + } + + private fun addCustomLandscapeSettings(sl: ArrayList) { + settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.emulation_landscape_custom_layout)) + sl.apply { + add(HeaderSetting(R.string.emulation_top_screen)) + add( + SliderSetting( + IntSetting.LANDSCAPE_TOP_X, + R.string.emulation_custom_layout_x, + 0, + 0, + getHeight(), + "px", + IntSetting.LANDSCAPE_TOP_X.key, + IntSetting.LANDSCAPE_TOP_X.defaultValue.toFloat() + ) + ) + add( + SliderSetting( + IntSetting.LANDSCAPE_TOP_Y, + R.string.emulation_custom_layout_y, + 0, + 0, + getWidth(), + "px", + IntSetting.LANDSCAPE_TOP_Y.key, + IntSetting.LANDSCAPE_TOP_Y.defaultValue.toFloat() + ) + ) + add( + SliderSetting( + IntSetting.LANDSCAPE_TOP_WIDTH, + R.string.emulation_custom_layout_width, + 0, + 0, + getHeight(), + "px", + IntSetting.LANDSCAPE_TOP_WIDTH.key, + IntSetting.LANDSCAPE_TOP_WIDTH.defaultValue.toFloat() + ) + ) + add( + SliderSetting( + IntSetting.LANDSCAPE_TOP_HEIGHT, + R.string.emulation_custom_layout_height, + 0, + 0, + getWidth(), + "px", + IntSetting.LANDSCAPE_TOP_HEIGHT.key, + IntSetting.LANDSCAPE_TOP_HEIGHT.defaultValue.toFloat() + ) + ) + add(HeaderSetting(R.string.emulation_bottom_screen)) + add( + SliderSetting( + IntSetting.LANDSCAPE_BOTTOM_X, + R.string.emulation_custom_layout_x, + 0, + 0, + getHeight(), + "px", + IntSetting.LANDSCAPE_BOTTOM_X.key, + IntSetting.LANDSCAPE_BOTTOM_X.defaultValue.toFloat() + ) + ) + add( + SliderSetting( + IntSetting.LANDSCAPE_BOTTOM_Y, + R.string.emulation_custom_layout_y, + 0, + 0, + getWidth(), + "px", + IntSetting.LANDSCAPE_BOTTOM_Y.key, + IntSetting.LANDSCAPE_BOTTOM_Y.defaultValue.toFloat() + ) + ) + add( + SliderSetting( + IntSetting.LANDSCAPE_BOTTOM_WIDTH, + R.string.emulation_custom_layout_width, + 0, + 0, + getHeight(), + "px", + IntSetting.LANDSCAPE_BOTTOM_WIDTH.key, + IntSetting.LANDSCAPE_BOTTOM_WIDTH.defaultValue.toFloat() + ) + ) + add( + SliderSetting( + IntSetting.LANDSCAPE_BOTTOM_HEIGHT, + R.string.emulation_custom_layout_height, + 0, + 0, + getWidth(), + "px", + IntSetting.LANDSCAPE_BOTTOM_HEIGHT.key, + IntSetting.LANDSCAPE_BOTTOM_HEIGHT.defaultValue.toFloat() + ) + ) + } + + } + + private fun addCustomPortraitSettings(sl: ArrayList) { + settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.emulation_portrait_custom_layout)) + sl.apply { + add(HeaderSetting(R.string.emulation_top_screen)) + add( + SliderSetting( + IntSetting.PORTRAIT_TOP_X, + R.string.emulation_custom_layout_x, + 0, + 0, + getWidth(), + "px", + IntSetting.PORTRAIT_TOP_X.key, + IntSetting.PORTRAIT_TOP_X.defaultValue.toFloat() + ) + ) + add( + SliderSetting( + IntSetting.PORTRAIT_TOP_Y, + R.string.emulation_custom_layout_y, + 0, + 0, + getHeight(), + "px", + IntSetting.PORTRAIT_TOP_Y.key, + IntSetting.PORTRAIT_TOP_Y.defaultValue.toFloat() + ) + ) + add( + SliderSetting( + IntSetting.PORTRAIT_TOP_WIDTH, + R.string.emulation_custom_layout_width, + 0, + 0, + getWidth(), + "px", + IntSetting.PORTRAIT_TOP_WIDTH.key, + IntSetting.PORTRAIT_TOP_WIDTH.defaultValue.toFloat() + ) + ) + add( + SliderSetting( + IntSetting.PORTRAIT_TOP_HEIGHT, + R.string.emulation_custom_layout_height, + 0, + 0, + getHeight(), + "px", + IntSetting.PORTRAIT_TOP_HEIGHT.key, + IntSetting.PORTRAIT_TOP_HEIGHT.defaultValue.toFloat() + ) + ) + add(HeaderSetting(R.string.emulation_bottom_screen)) + add( + SliderSetting( + IntSetting.PORTRAIT_BOTTOM_X, + R.string.emulation_custom_layout_x, + 0, + 0, + getWidth(), + "px", + IntSetting.PORTRAIT_BOTTOM_X.key, + IntSetting.PORTRAIT_BOTTOM_X.defaultValue.toFloat() + ) + ) + add( + SliderSetting( + IntSetting.PORTRAIT_BOTTOM_Y, + R.string.emulation_custom_layout_y, + 0, + 0, + getHeight(), + "px", + IntSetting.PORTRAIT_BOTTOM_Y.key, + IntSetting.PORTRAIT_BOTTOM_Y.defaultValue.toFloat() + ) + ) + add( + SliderSetting( + IntSetting.PORTRAIT_BOTTOM_WIDTH, + R.string.emulation_custom_layout_width, + 0, + 0, + getWidth(), + "px", + IntSetting.PORTRAIT_BOTTOM_WIDTH.key, + IntSetting.PORTRAIT_BOTTOM_WIDTH.defaultValue.toFloat() + ) + ) + add( + SliderSetting( + IntSetting.PORTRAIT_BOTTOM_HEIGHT, + R.string.emulation_custom_layout_height, + 0, + 0, + getHeight(), + "px", + IntSetting.PORTRAIT_BOTTOM_HEIGHT.key, + IntSetting.PORTRAIT_BOTTOM_HEIGHT.defaultValue.toFloat() + ) + ) + } + + } + private fun addAudioSettings(sl: ArrayList) { settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_audio)) sl.apply { 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..3d7407436 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 @@ -1,4 +1,4 @@ -// Copyright 2023 Citra Emulator Project +// Copyright Citra Emulator Project / Lime3DS Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -13,6 +13,8 @@ import android.os.Bundle import android.os.Handler import android.os.Looper import android.os.SystemClock +import android.text.Editable +import android.text.TextWatcher import android.view.Choreographer import android.view.LayoutInflater import android.view.MotionEvent @@ -51,8 +53,10 @@ 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.IntSetting import io.github.lime3ds.android.features.settings.model.SettingsViewModel import io.github.lime3ds.android.features.settings.ui.SettingsActivity import io.github.lime3ds.android.features.settings.utils.SettingsFile @@ -142,7 +146,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 +212,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 +274,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 } @@ -315,6 +327,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram SettingsFile.FILE_NAME_CONFIG, "" ) + true } @@ -423,7 +436,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram } private fun togglePause() { - if(emulationState.isPaused) { + if (emulationState.isPaused) { emulationState.unpause() } else { emulationState.pause() @@ -769,7 +782,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) @@ -777,19 +790,22 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram popupMenu.menuInflater.inflate(R.menu.menu_landscape_screen_layout, popupMenu.menu) - val layoutOptionMenuItem = when (EmulationMenuSettings.landscapeScreenLayout) { + val layoutOptionMenuItem = when (IntSetting.SCREEN_LAYOUT.int) { + ScreenLayout.ORIGINAL.int -> + R.id.menu_screen_layout_original + ScreenLayout.SINGLE_SCREEN.int -> R.id.menu_screen_layout_single 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 +813,83 @@ 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_original -> { + screenAdjustmentUtil.changeScreenOrientation(ScreenLayout.ORIGINAL.int) + true + } + + R.id.menu_screen_layout_custom -> { + Toast.makeText( + requireContext(), + R.string.emulation_adjust_custom_layout, + Toast.LENGTH_LONG + ).show() + 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 (IntSetting.PORTRAIT_SCREEN_LAYOUT.int) { + 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 -> { + Toast.makeText( + requireContext(), + R.string.emulation_adjust_custom_layout, + Toast.LENGTH_LONG + ).show() + screenAdjustmentUtil.changePortraitOrientation(PortraitScreenLayout.CUSTOM_PORTRAIT_LAYOUT.int) true } @@ -869,14 +941,32 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram sliderBinding.apply { slider.valueTo = 150f + slider.valueFrom = 0f slider.value = preferences.getInt(target, 50).toFloat() + textValue.setText((slider.value + 50).toInt().toString()) + textValue.addTextChangedListener( object : TextWatcher { + override fun afterTextChanged(s: Editable) { + val value = s.toString().toIntOrNull() + if (value == null || value < 50 || value > 150) { + textInput.error = "Inappropriate Value" + } else { + textInput.error = null + slider.value = value.toFloat() - 50 + } + } + override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {} + override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {} + }) slider.addOnChangeListener( Slider.OnChangeListener { slider: Slider, progress: Float, _: Boolean -> - textValue.text = (progress.toInt() + 50).toString() - setControlScale(slider.value.toInt(), target) + if (textValue.text.toString() != (slider.value + 50).toInt().toString()) { + textValue.setText((slider.value + 50).toInt().toString()) + textValue.setSelection(textValue.length()) + setControlScale(slider.value.toInt(), target) + } + }) - textValue.text = (sliderBinding.slider.value.toInt() + 50).toString() - textUnits.text = "%" + textInput.suffixText = "%" } val previousProgress = sliderBinding.slider.value.toInt() @@ -899,15 +989,36 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram val sliderBinding = DialogSliderBinding.inflate(layoutInflater) sliderBinding.apply { + slider.valueFrom = 0f slider.valueTo = 100f slider.value = preferences.getInt("controlOpacity", 50).toFloat() - slider.addOnChangeListener( - Slider.OnChangeListener { slider: Slider, progress: Float, _: Boolean -> - textValue.text = (progress.toInt()).toString() - setControlOpacity(slider.value.toInt()) - }) - textValue.text = (sliderBinding.slider.value.toInt()).toString() - textUnits.text = "%" + textValue.setText(slider.value.toInt().toString()) + + textValue.addTextChangedListener( object : TextWatcher { + override fun afterTextChanged(s: Editable) { + val value = s.toString().toIntOrNull() + if (value == null || value < slider.valueFrom || value > slider.valueTo) { + textInput.error = "Inappropriate Value" + } else { + textInput.error = null + slider.value = value.toFloat() + } + } + override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {} + override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {} + }) + + + slider.addOnChangeListener { _: Slider, value: Float, _: Boolean -> + + if (textValue.text.toString() != slider.value.toInt().toString()) { + textValue.setText(slider.value.toInt().toString()) + textValue.setSelection(textValue.length()) + setControlOpacity(slider.value.toInt()) + } + } + + textInput.suffixText = "%" } val previousProgress = sliderBinding.slider.value.toInt() @@ -959,7 +1070,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..8932da0f1 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 @@ -1,4 +1,4 @@ -// Copyright 2023 Citra Emulator Project +// Copyright Citra Emulator Project / Lime3DS Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -7,7 +7,6 @@ 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.ScreenLayout object EmulationMenuSettings { private val preferences = @@ -27,16 +26,7 @@ object EmulationMenuSettings { .putBoolean("EmulationMenuSettings_DpadSlideEnable", value) .apply() } - var landscapeScreenLayout: Int - get() = preferences.getInt( - "EmulationMenuSettings_LandscapeScreenLayout", - ScreenLayout.MOBILE_LANDSCAPE.int - ) - set(value) { - preferences.edit() - .putInt("EmulationMenuSettings_LandscapeScreenLayout", 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 136671bff..fb3549b0d 100644 --- a/src/android/app/src/main/jni/config.cpp +++ b/src/android/app/src/main/jni/config.cpp @@ -1,4 +1,4 @@ -// Copyright 2014 Citra Emulator Project +// Copyright Citra Emulator Project / Lime3DS Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -174,8 +174,7 @@ void Config::ReadValues() { // Layout Settings::values.layout_option = static_cast(sdl2_config->GetInteger( - "Layout", "layout_option", static_cast(Settings::LayoutOption::MobileLandscape))); - ReadSetting("Layout", Settings::values.custom_layout); + "Layout", "layout_option", static_cast(Settings::LayoutOption::LargeScreen))); ReadSetting("Layout", Settings::values.custom_top_x); ReadSetting("Layout", Settings::values.custom_top_y); ReadSetting("Layout", Settings::values.custom_top_width); @@ -188,6 +187,19 @@ void Config::ReadValues() { ReadSetting("Layout", Settings::values.cardboard_x_shift); ReadSetting("Layout", Settings::values.cardboard_y_shift); + 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); + ReadSetting("Layout", Settings::values.custom_portrait_top_height); + ReadSetting("Layout", Settings::values.custom_portrait_bottom_x); + ReadSetting("Layout", Settings::values.custom_portrait_bottom_y); + ReadSetting("Layout", Settings::values.custom_portrait_bottom_width); + ReadSetting("Layout", Settings::values.custom_portrait_bottom_height); + // Utility ReadSetting("Utility", Settings::values.dump_textures); ReadSetting("Utility", Settings::values.custom_textures); diff --git a/src/android/app/src/main/jni/default_ini.h b/src/android/app/src/main/jni/default_ini.h index b96999da9..1e2d6c8fe 100644 --- a/src/android/app/src/main/jni/default_ini.h +++ b/src/android/app/src/main/jni/default_ini.h @@ -1,4 +1,4 @@ -// Copyright 2014 Citra Emulator Project +// Copyright Citra Emulator Project / Lime3DS Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -179,16 +179,20 @@ 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: Top/Bottom *currently unsupported on android* +# 1: Single Screen Only, +# 2: *currently unsupported on android* +# 3: Side by Side +# 4: Hybrid +# 5: Custom Layout +# 6: (default) Large screen / small screen layout_option = -# Toggle custom layout (using the settings below) on or off. -# 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. +# suggested aspect ratio for top screen is 5:3 +# suggested aspect ratio for bottom screen is 4:3 custom_top_x = custom_top_y = custom_top_width = @@ -198,6 +202,22 @@ custom_bottom_y = custom_bottom_width = custom_bottom_height = +# 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 Portrait Custom layout option +# 0x, 0y is the top left corner of the render window. +custom_portrait_top_x = +custom_portrait_top_y = +custom_portrait_top_width = +custom_portrait_top_height = +custom_portrait_bottom_x = +custom_portrait_bottom_y = +custom_portrait_bottom_width = +custom_portrait_bottom_height = + # Swaps the prominent screen with the other screen. # For example, if Single Screen is chosen, setting this to 1 will display the bottom screen instead of the top screen. # 0 (default): Top Screen is prominent, 1: Bottom Screen is prominent 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..677de440b 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 @@ -1,4 +1,4 @@ -// Copyright 2019 Citra Emulator Project +// Copyright Citra Emulator Project / Lime3DS Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -21,12 +21,6 @@ static bool IsPortraitMode() { IDCache::GetNativeLibraryClass(), IDCache::GetIsPortraitMode()); } -static void UpdateLandscapeScreenLayout() { - Settings::values.layout_option = - static_cast(IDCache::GetEnvForThread()->CallStaticIntMethod( - IDCache::GetNativeLibraryClass(), IDCache::GetLandscapeScreenLayout())); -} - bool EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) { if (render_window == surface) { return false; @@ -57,7 +51,6 @@ void EmuWindow_Android::OnTouchMoved(int x, int y) { } void EmuWindow_Android::OnFramebufferSizeChanged() { - UpdateLandscapeScreenLayout(); 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..30d5a8b7b 100644 --- a/src/android/app/src/main/jni/id_cache.cpp +++ b/src/android/app/src/main/jni/id_cache.cpp @@ -1,4 +1,4 @@ -// Copyright 2019 Citra Emulator Project +// Copyright Citra Emulator Project / Lime3DS Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -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; } @@ -172,8 +177,6 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) { s_native_library_class, "onCoreError", "(Lio/github/lime3ds/android/NativeLibrary$CoreError;Ljava/lang/String;)Z"); 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_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..5b083814f 100644 --- a/src/android/app/src/main/jni/id_cache.h +++ b/src/android/app/src/main/jni/id_cache.h @@ -1,4 +1,4 @@ -// Copyright 2019 Citra Emulator Project +// Copyright Citra Emulator Project / Lime3DS Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -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..a421119de 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -1,4 +1,4 @@ -// Copyright 2019 Citra Emulator Project +// Copyright Citra Emulator Project / Lime3DS Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -346,15 +346,13 @@ void JNICALL Java_io_github_lime3ds_android_NativeLibrary_enableAdrenoTurboMode( EnableAdrenoTurboMode(enable); } -void Java_io_github_lime3ds_android_NativeLibrary_notifyOrientationChange( - [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jobject obj, jint layout_option, jint rotation) { - Settings::values.layout_option = static_cast(layout_option); +void Java_io_github_lime3ds_android_NativeLibrary_updateFramebuffer([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jobject obj, + jboolean is_portrait_mode) { auto& system = Core::System::GetInstance(); if (system.IsPoweredOn()) { - system.GPU().Renderer().UpdateCurrentFramebufferLayout(!(rotation % 2)); + system.GPU().Renderer().UpdateCurrentFramebufferLayout(is_portrait_mode); } - InputManager::screen_rotation = rotation; - Camera::NDK::g_rotation = rotation; } void Java_io_github_lime3ds_android_NativeLibrary_swapScreens([[maybe_unused]] JNIEnv* env, diff --git a/src/android/app/src/main/res/drawable/ic_portrait_fit_screen.xml b/src/android/app/src/main/res/drawable/ic_portrait_fit_screen.xml new file mode 100644 index 000000000..299572089 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_portrait_fit_screen.xml @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/src/android/app/src/main/res/layout/dialog_slider.xml b/src/android/app/src/main/res/layout/dialog_slider.xml index 59719e013..cb0cb2207 100644 --- a/src/android/app/src/main/res/layout/dialog_slider.xml +++ b/src/android/app/src/main/res/layout/dialog_slider.xml @@ -1,6 +1,7 @@ @@ -13,25 +14,30 @@ android:layout_marginRight="@dimen/spacing_large" android:layout_alignParentEnd="true" android:layout_alignParentStart="true" - android:layout_below="@+id/text_value" + android:layout_below="@+id/text_input" android:layout_marginBottom="@dimen/spacing_medlarge" /> - + + + android:id="@+id/text_value" + android:layout_alignParentTop="true" + android:layout_centerHorizontal="true" + android:layout_marginTop="@dimen/spacing_medlarge" + android:layout_marginBottom="@dimen/spacing_medlarge" /> - + diff --git a/src/android/app/src/main/res/menu/menu_emulation.xml b/src/android/app/src/main/res/menu/menu_emulation.xml deleted file mode 100644 index a0dec63be..000000000 --- a/src/android/app/src/main/res/menu/menu_emulation.xml +++ /dev/null @@ -1,127 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/android/app/src/main/res/menu/menu_in_game.xml b/src/android/app/src/main/res/menu/menu_in_game.xml index 56bb99ed2..d42d59264 100644 --- a/src/android/app/src/main/res/menu/menu_in_game.xml +++ b/src/android/app/src/main/res/menu/menu_in_game.xml @@ -27,9 +27,15 @@ android:icon="@drawable/ic_fit_screen" android:title="@string/emulation_switch_screen_layout" /> + + - - @@ -23,6 +19,14 @@ 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..afa9f27e0 --- /dev/null +++ b/src/android/app/src/main/res/menu/menu_portrait_screen_layout.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index 0cb329253..3439f727b 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml @@ -11,6 +11,37 @@ 1 + + @string/emulation_screen_layout_landscape + @string/emulation_screen_layout_single + @string/emulation_screen_layout_sidebyside + @string/emulation_screen_layout_hybrid + @string/emulation_screen_layout_original + @string/emulation_screen_layout_custom + + + + 6 + 1 + 3 + 4 + 0 + 5 + + + + @string/emulation_portrait_layout_top_full + @string/emulation_screen_layout_custom + + + + 0 + 1 + + @string/auto_select @string/system_region_jpn diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 72d63373e..21a39d58e 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -330,7 +330,7 @@ Audio Debug Theme and Color - + Layout Your ROM is Encrypted Invalid ROM format @@ -359,12 +359,25 @@ Open Settings Open Cheats Landscape Screen Layout + Portrait Screen Layout Default Portrait Single Screen Side by Side Screens Hybrid Screens - Cycle Landscape Layouts + Original + Default + Custom Layout + Adjust Custom Layout in Settings + Landscape Custom Layout + Portrait Custom Layout + Top Screen + Bottom Screen + X-Position + Y-Position + Width + Height + Cycle Layouts Swap Screens Reset Overlay Show Overlay diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 08bafc37c..c9fe12002 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -1,7 +1,10 @@ -// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project +// Copyright Citra Emulator Project / Lime3DS Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +// Copyright Dolphin Emulator Project +// Licensed under GPLv2 or any later version + #include #include #include @@ -707,8 +710,8 @@ std::string AppDataRoamingDirectory() { /** * @return The user’s home directory on POSIX systems */ -static const std::string& GetHomeDirectory() { - static std::string home_path; +const std::string GetHomeDirectory() { + std::string home_path; if (home_path.empty()) { const char* envvar = getenv("HOME"); if (envvar) { diff --git a/src/common/file_util.h b/src/common/file_util.h index c0fa49d7d..f0c53c15f 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h @@ -1,7 +1,10 @@ -// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project +// Copyright Citra Emulator Project / Lime3DS Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +// Copyright Dolphin Emulator Project +// Licensed under GPLv2 or any later version + #pragma once #include @@ -207,6 +210,7 @@ void UpdateUserPath(UserPath path, const std::string& filename); [[nodiscard]] const std::string& GetExeDirectory(); [[nodiscard]] std::string AppDataRoamingDirectory(); #else +[[nodiscard]] const std::string GetHomeDirectory(); [[nodiscard]] const std::string GetUserDirectory(const std::string& envvar); #endif diff --git a/src/common/settings.cpp b/src/common/settings.cpp index dbf85e710..6431e9301 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -1,4 +1,4 @@ -// Copyright 2014 Citra Emulator Project +// Copyright Citra Emulator Project / Lime3DS Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -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 580c0b65c..256f6858a 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -1,4 +1,4 @@ -// Copyright 2014 Citra Emulator Project +// Copyright Citra Emulator Project / Lime3DS Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -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,19 @@ 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"}; - 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,6 +507,17 @@ struct Values { Setting screen_bottom_leftright_padding{0, "screen_bottom_leftright_padding"}; Setting screen_bottom_topbottom_padding{0, "screen_bottom_topbottom_padding"}; + 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{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"}; SwitchableSetting bg_green{0.f, "bg_green"}; SwitchableSetting bg_blue{0.f, "bg_blue"}; diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index 872cce7d1..4962c76f3 100644 --- a/src/core/frontend/emu_window.cpp +++ b/src/core/frontend/emu_window.cpp @@ -1,4 +1,4 @@ -// Copyright 2014 Citra Emulator Project +// Copyright Citra Emulator Project / Lime3DS Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -176,20 +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) { - layout = Layout::CustomFrameLayout(width, height, Settings::values.swap_screen.GetValue()); + 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(), @@ -219,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 = @@ -241,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 fbb4667fc..c03926cbf 100644 --- a/src/core/frontend/framebuffer_layout.cpp +++ b/src/core/frontend/framebuffer_layout.cpp @@ -1,4 +1,4 @@ -// Copyright 2016 Citra Emulator Project +// Copyright Citra Emulator Project / Lime3DS Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -117,11 +117,11 @@ 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); - FramebufferLayout res{width, height, true, true, {}, {}}; + FramebufferLayout res{width, height, true, true, {}, {}, true, true}; // Default layout gives equal screen sizes to the top and bottom screen Common::Rectangle screen_window_area{0, 0, width, height / 2}; Common::Rectangle top_screen = MaxRectangle(screen_window_area, TOP_SCREEN_ASPECT_RATIO); @@ -305,7 +305,7 @@ FramebufferLayout HybridScreenLayout(u32 width, u32 height, bool swapped, bool u ASSERT(width > 0); ASSERT(height > 0); - FramebufferLayout res{width, height, true, true, {}, {}, !upright, true, {}}; + FramebufferLayout res{width, height, true, true, {}, {}, !upright, false, true, {}}; // Split the window into two parts. Give 2.25x width to the main screen, // and make a bar on the right side with 1x width top screen and 1.25x width bottom screen @@ -382,24 +382,33 @@ FramebufferLayout SeparateWindowsLayout(u32 width, u32 height, bool is_secondary return SingleFrameLayout(width, height, is_secondary, upright); } -FramebufferLayout CustomFrameLayout(u32 width, u32 height, bool is_swapped) { +FramebufferLayout CustomFrameLayout(u32 width, u32 height, bool is_swapped, bool is_portrait_mode) { ASSERT(width > 0); ASSERT(height > 0); - FramebufferLayout res{width, height, true, true, {}, {}, !Settings::values.upright_screen}; + FramebufferLayout res{ + width, height, true, true, {}, {}, !Settings::values.upright_screen, is_portrait_mode}; + u16 top_x = is_portrait_mode ? Settings::values.custom_portrait_top_x.GetValue() + : Settings::values.custom_top_x.GetValue(); + u16 top_width = is_portrait_mode ? Settings::values.custom_portrait_top_width.GetValue() + : Settings::values.custom_top_width.GetValue(); + u16 top_y = is_portrait_mode ? Settings::values.custom_portrait_top_y.GetValue() + : Settings::values.custom_top_y.GetValue(); + u16 top_height = is_portrait_mode ? Settings::values.custom_portrait_top_height.GetValue() + : Settings::values.custom_top_height.GetValue(); + u16 bottom_x = is_portrait_mode ? Settings::values.custom_portrait_bottom_x.GetValue() + : Settings::values.custom_bottom_x.GetValue(); + u16 bottom_width = is_portrait_mode ? Settings::values.custom_portrait_bottom_width.GetValue() + : Settings::values.custom_bottom_width.GetValue(); + u16 bottom_y = is_portrait_mode ? Settings::values.custom_portrait_bottom_y.GetValue() + : Settings::values.custom_bottom_y.GetValue(); + u16 bottom_height = is_portrait_mode ? Settings::values.custom_portrait_bottom_height.GetValue() + : Settings::values.custom_bottom_height.GetValue(); - Common::Rectangle top_screen{Settings::values.custom_top_x.GetValue(), - Settings::values.custom_top_y.GetValue(), - (u32)(Settings::values.custom_top_x.GetValue() + - Settings::values.custom_top_width.GetValue()), - (u32)(Settings::values.custom_top_y.GetValue() + - Settings::values.custom_top_height.GetValue())}; - Common::Rectangle bot_screen{Settings::values.custom_bottom_x.GetValue(), - Settings::values.custom_bottom_y.GetValue(), - (u32)(Settings::values.custom_bottom_x.GetValue() + - Settings::values.custom_bottom_width.GetValue()), - (u32)(Settings::values.custom_bottom_y.GetValue() + - Settings::values.custom_bottom_height.GetValue())}; + Common::Rectangle top_screen{top_x, top_y, (u32)(top_x + top_width), + (u32)(top_y + top_height)}; + Common::Rectangle bot_screen{bottom_x, bottom_y, (u32)(bottom_x + bottom_width), + (u32)(bottom_y + bottom_height)}; if (is_swapped) { res.top_screen = bot_screen; @@ -411,102 +420,126 @@ FramebufferLayout CustomFrameLayout(u32 width, u32 height, bool is_swapped) { return res; } -FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale, bool is_secondary) { - if (Settings::values.custom_layout.GetValue() == true) { - return CustomFrameLayout(std::max(Settings::values.custom_top_width.GetValue(), - Settings::values.custom_bottom_width.GetValue()), - std::max(Settings::values.custom_top_height.GetValue(), - Settings::values.custom_bottom_height.GetValue()), - Settings::values.swap_screen.GetValue()); - } - +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(); } @@ -529,11 +562,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) @@ -541,28 +590,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 = @@ -596,6 +631,13 @@ FramebufferLayout GetCardboardSettings(const FramebufferLayout& layout) { return new_layout; } +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) { u32 min_width, min_height; diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h index f2ed553fd..510035dc8 100644 --- a/src/core/frontend/framebuffer_layout.h +++ b/src/core/frontend/framebuffer_layout.h @@ -1,4 +1,4 @@ -// Copyright 2016 Citra Emulator Project +// Copyright Citra Emulator Project / Lime3DS Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -61,7 +61,7 @@ struct FramebufferLayout { Common::Rectangle top_screen; Common::Rectangle bottom_screen; bool is_rotated = true; - + bool is_portrait = false; bool additional_screen_enabled; Common::Rectangle additional_screen; @@ -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 @@ -145,14 +146,17 @@ FramebufferLayout SeparateWindowsLayout(u32 width, u32 height, bool is_secondary * @param height Window framebuffer height in pixels * @return Newly created FramebufferLayout object with default screen regions initialized */ -FramebufferLayout CustomFrameLayout(u32 width, u32 height, bool is_swapped); +FramebufferLayout CustomFrameLayout(u32 width, u32 height, bool is_swapped, + bool is_portrait_mode = false); /** * 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 @@ -164,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 e67b7ed9f..4bc59945c 100644 --- a/src/lime/config.cpp +++ b/src/lime/config.cpp @@ -1,4 +1,4 @@ -// Copyright 2014 Citra Emulator Project +// Copyright Citra Emulator Project / Lime3DS Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -164,8 +164,6 @@ void Config::ReadValues() { ReadSetting("Layout", Settings::values.swap_screen); ReadSetting("Layout", Settings::values.upright_screen); ReadSetting("Layout", Settings::values.large_screen_proportion); - - ReadSetting("Layout", Settings::values.custom_layout); ReadSetting("Layout", Settings::values.custom_top_x); ReadSetting("Layout", Settings::values.custom_top_y); ReadSetting("Layout", Settings::values.custom_top_width); @@ -183,6 +181,16 @@ void Config::ReadValues() { ReadSetting("Layout", Settings::values.screen_bottom_leftright_padding); ReadSetting("Layout", Settings::values.screen_bottom_topbottom_padding); + 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); + ReadSetting("Layout", Settings::values.custom_portrait_top_height); + ReadSetting("Layout", Settings::values.custom_portrait_bottom_x); + ReadSetting("Layout", Settings::values.custom_portrait_bottom_y); + ReadSetting("Layout", Settings::values.custom_portrait_bottom_width); + ReadSetting("Layout", Settings::values.custom_portrait_bottom_height); + // Utility ReadSetting("Utility", Settings::values.dump_textures); ReadSetting("Utility", Settings::values.custom_textures); diff --git a/src/lime/default_ini.h b/src/lime/default_ini.h index fa1911971..731e0127c 100644 --- a/src/lime/default_ini.h +++ b/src/lime/default_ini.h @@ -1,4 +1,4 @@ -// Copyright 2014 Citra Emulator Project +// Copyright Citra Emulator Project / Lime3DS Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -182,7 +182,7 @@ filter_mode = [Layout] # Layout for the screen inside the render window. -# 0 (default): Default Top Bottom Screen +# 0 (default): Default Above/Below Screen # 1: Single Screen Only # 2: Large Screen Small Screen # 3: Side by Side @@ -191,10 +191,6 @@ filter_mode = # 6: Custom Layout layout_option = -# Toggle custom layout (using the settings below) on or off. -# 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 = diff --git a/src/lime/lime.cpp b/src/lime/lime.cpp index e04eaef7a..f7a41cdd5 100644 --- a/src/lime/lime.cpp +++ b/src/lime/lime.cpp @@ -415,7 +415,7 @@ int main(int argc, char** argv) { return -1; case Core::System::ResultStatus::ErrorLoader_ErrorEncrypted: LOG_CRITICAL(Frontend, "The game that you are trying to load must be decrypted before " - "being used with Lime. \n\n For more information on dumping and " + "being used with Lime3DS. \n\n For more information on dumping and " "decrypting games, please refer to: " "https://web.archive.org/web/20240304210021/https://citra-emu.org/" "wiki/dumping-game-cartridges/"); diff --git a/src/lime_qt/CMakeLists.txt b/src/lime_qt/CMakeLists.txt index e508736c5..a2f14f40a 100644 --- a/src/lime_qt/CMakeLists.txt +++ b/src/lime_qt/CMakeLists.txt @@ -28,9 +28,6 @@ add_executable(lime-qt camera/qt_multimedia_camera.cpp camera/qt_multimedia_camera.h lime-qt.rc - compatdb.cpp - compatdb.h - compatdb.ui configuration/config.cpp configuration/config.h configuration/configure.ui @@ -334,10 +331,6 @@ target_compile_definitions(lime-qt PRIVATE -DQT_NO_CAST_TO_ASCII ) -if (CITRA_ENABLE_COMPATIBILITY_REPORTING) - target_compile_definitions(lime-qt PRIVATE -DCITRA_ENABLE_COMPATIBILITY_REPORTING) -endif() - if (USE_DISCORD_PRESENCE) target_sources(lime-qt PUBLIC discord_impl.cpp diff --git a/src/lime_qt/compatdb.cpp b/src/lime_qt/compatdb.cpp deleted file mode 100644 index 506706fe9..000000000 --- a/src/lime_qt/compatdb.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2017 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include -#include -#include -#include -#include "core/core.h" -#include "lime_qt/compatdb.h" -#include "ui_compatdb.h" - -CompatDB::CompatDB(QWidget* parent) - : QWizard(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), - ui{std::make_unique()} { - ui->setupUi(this); - connect(ui->radioButton_Perfect, &QRadioButton::clicked, this, &CompatDB::EnableNext); - connect(ui->radioButton_Great, &QRadioButton::clicked, this, &CompatDB::EnableNext); - connect(ui->radioButton_Okay, &QRadioButton::clicked, this, &CompatDB::EnableNext); - connect(ui->radioButton_Bad, &QRadioButton::clicked, this, &CompatDB::EnableNext); - connect(ui->radioButton_IntroMenu, &QRadioButton::clicked, this, &CompatDB::EnableNext); - connect(ui->radioButton_WontBoot, &QRadioButton::clicked, this, &CompatDB::EnableNext); - connect(button(NextButton), &QPushButton::clicked, this, &CompatDB::Submit); - connect(&testcase_watcher, &QFutureWatcher::finished, this, - &CompatDB::OnTestcaseSubmitted); -} - -CompatDB::~CompatDB() = default; - -enum class CompatDBPage { - Intro = 0, - Selection = 1, - Final = 2, -}; - -void CompatDB::Submit() { - QButtonGroup* compatibility = new QButtonGroup(this); - compatibility->addButton(ui->radioButton_Perfect, 0); - compatibility->addButton(ui->radioButton_Great, 1); - compatibility->addButton(ui->radioButton_Okay, 2); - compatibility->addButton(ui->radioButton_Bad, 3); - compatibility->addButton(ui->radioButton_IntroMenu, 4); - compatibility->addButton(ui->radioButton_WontBoot, 5); - switch ((static_cast(currentId()))) { - case CompatDBPage::Selection: - if (compatibility->checkedId() == -1) { - button(NextButton)->setEnabled(false); - } - break; - case CompatDBPage::Final: - back(); - LOG_DEBUG(Frontend, "Compatibility Rating: {}", compatibility->checkedId()); - - button(NextButton)->setEnabled(false); - button(NextButton)->setText(tr("Submitting")); - button(CancelButton)->setVisible(false); - - break; - default: - LOG_ERROR(Frontend, "Unexpected page: {}", currentId()); - } -} - -void CompatDB::OnTestcaseSubmitted() { - if (!testcase_watcher.result()) { - QMessageBox::critical(this, tr("Communication error"), - tr("An error occurred while sending the Testcase")); - button(NextButton)->setEnabled(true); - button(NextButton)->setText(tr("Next")); - button(CancelButton)->setVisible(true); - } else { - next(); - // older versions of QT don't support the "NoCancelButtonOnLastPage" option, this is a - // workaround - button(CancelButton)->setVisible(false); - } -} - -void CompatDB::EnableNext() { - button(NextButton)->setEnabled(true); -} diff --git a/src/lime_qt/compatdb.h b/src/lime_qt/compatdb.h deleted file mode 100644 index 5381f67f7..000000000 --- a/src/lime_qt/compatdb.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2017 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include -#include -#include - -namespace Ui { -class CompatDB; -} - -class CompatDB : public QWizard { - Q_OBJECT - -public: - explicit CompatDB(QWidget* parent = nullptr); - ~CompatDB(); - -private: - QFutureWatcher testcase_watcher; - - std::unique_ptr ui; - - void Submit(); - void OnTestcaseSubmitted(); - void EnableNext(); -}; diff --git a/src/lime_qt/compatdb.ui b/src/lime_qt/compatdb.ui deleted file mode 100644 index 12e409352..000000000 --- a/src/lime_qt/compatdb.ui +++ /dev/null @@ -1,215 +0,0 @@ - - - CompatDB - - - - 0 - 0 - 600 - 482 - - - - - 500 - 410 - - - - Report Compatibility - - - QWizard::DisabledBackButtonOnLastPage|QWizard::HelpButtonOnRight|QWizard::NoBackButtonOnStartPage - - - - Report Game Compatibility - - - 0 - - - - - - <html><head/><body><p><span style=" font-size:10pt;">Should you choose to submit a test case to the </span><a href="https://citra-emu.org/game/"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">Lime3DS Compatibility List</span></a><span style=" font-size:10pt;">, The following information will be collected and displayed on the site:</span></p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Hardware Information (CPU / GPU / Operating System)</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Which version of Lime3DS you are running</li><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The connected Citra account</li></ul></body></html> - - - true - - - true - - - - - - - Qt::Vertical - - - - 20 - 0 - - - - - - - - - Report Game Compatibility - - - 1 - - - - - - Perfect - - - - - - - <html><head/><body><p>Game functions flawlessly with no audio or graphical glitches.</p></body></html> - - - true - - - - - - - Great - - - - - - - <html><head/><body><p>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.</p></body></html> - - - true - - - - - - - Okay - - - - - - - <html><head/><body><p>Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.</p></body></html> - - - true - - - - - - - Bad - - - - - - - <html><head/><body><p>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.</p></body></html> - - - true - - - - - - - Intro/Menu - - - - - - - <html><head/><body><p>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.</p></body></html> - - - true - - - - - - - Won't Boot - - - true - - - false - - - - - - - <html><head/><body><p>The game crashes when attempting to startup.</p></body></html> - - - - - - - - 10 - - - - <html><head/><body><p>Independent of speed or performance, how well does this game play from start to finish on this version of Lime?</p></body></html> - - - true - - - - - - - Qt::Vertical - - - - 20 - 0 - - - - - - - - - Thank you for your submission! - - - 2 - - - - - - diff --git a/src/lime_qt/configuration/config.cpp b/src/lime_qt/configuration/config.cpp index 3919fe430..547174f0f 100644 --- a/src/lime_qt/configuration/config.cpp +++ b/src/lime_qt/configuration/config.cpp @@ -1,4 +1,4 @@ -// Copyright 2014 Citra Emulator Project +// Copyright Citra Emulator Project / Lime3DS Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -520,8 +520,6 @@ void Config::ReadLayoutValues() { if (global) { ReadBasicSetting(Settings::values.mono_render_option); - - ReadBasicSetting(Settings::values.custom_layout); ReadBasicSetting(Settings::values.custom_top_x); ReadBasicSetting(Settings::values.custom_top_y); ReadBasicSetting(Settings::values.custom_top_width); @@ -538,6 +536,15 @@ 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.portrait_layout_option); + ReadBasicSetting(Settings::values.custom_portrait_top_x); + ReadBasicSetting(Settings::values.custom_portrait_top_y); + ReadBasicSetting(Settings::values.custom_portrait_top_width); + ReadBasicSetting(Settings::values.custom_portrait_top_height); + ReadBasicSetting(Settings::values.custom_portrait_bottom_x); + ReadBasicSetting(Settings::values.custom_portrait_bottom_y); + ReadBasicSetting(Settings::values.custom_portrait_bottom_width); + ReadBasicSetting(Settings::values.custom_portrait_bottom_height); } qt_config->endGroup(); @@ -1072,8 +1079,6 @@ void Config::SaveLayoutValues() { if (global) { WriteBasicSetting(Settings::values.mono_render_option); - - WriteBasicSetting(Settings::values.custom_layout); WriteBasicSetting(Settings::values.custom_top_x); WriteBasicSetting(Settings::values.custom_top_y); WriteBasicSetting(Settings::values.custom_top_width); @@ -1090,6 +1095,14 @@ 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_top_x); + WriteBasicSetting(Settings::values.custom_portrait_top_y); + WriteBasicSetting(Settings::values.custom_portrait_top_width); + WriteBasicSetting(Settings::values.custom_portrait_top_height); + WriteBasicSetting(Settings::values.custom_portrait_bottom_x); + WriteBasicSetting(Settings::values.custom_portrait_bottom_y); + WriteBasicSetting(Settings::values.custom_portrait_bottom_width); + WriteBasicSetting(Settings::values.custom_portrait_bottom_height); } qt_config->endGroup(); diff --git a/src/lime_qt/main.cpp b/src/lime_qt/main.cpp index 4bd97e1dc..c9a74c1b7 100644 --- a/src/lime_qt/main.cpp +++ b/src/lime_qt/main.cpp @@ -46,7 +46,6 @@ #include "lime_qt/bootmanager.h" #include "lime_qt/camera/qt_multimedia_camera.h" #include "lime_qt/camera/still_image_camera.h" -#include "lime_qt/compatdb.h" #include "lime_qt/compatibility_list.h" #include "lime_qt/configuration/config.h" #include "lime_qt/configuration/configure_dialog.h" @@ -97,7 +96,6 @@ #include "core/savestate.h" #include "core/system_titles.h" #include "input_common/main.h" -#include "network/network_settings.h" #include "ui_main.h" #include "video_core/gpu.h" #include "video_core/renderer_base.h" @@ -339,9 +337,6 @@ GMainWindow::~GMainWindow() { } void GMainWindow::InitializeWidgets() { -#ifdef CITRA_ENABLE_COMPATIBILITY_REPORTING - ui->action_Report_Compatibility->setVisible(true); -#endif render_window = new GRenderWindow(this, emu_thread.get(), system, false); secondary_window = new GRenderWindow(this, emu_thread.get(), system, true); render_window->hide(); @@ -640,8 +635,6 @@ void GMainWindow::InitializeHotkeys() { link_action_shortcut(ui->action_Screen_Layout_Swap_Screens, QStringLiteral("Swap Screens")); link_action_shortcut(ui->action_Screen_Layout_Upright_Screens, QStringLiteral("Rotate Screens Upright")); - link_action_shortcut(ui->action_Enable_Frame_Advancing, - QStringLiteral("Toggle Frame Advancing")); link_action_shortcut(ui->action_Advance_Frame, QStringLiteral("Advance Frame")); link_action_shortcut(ui->action_Load_from_Newest_Slot, QStringLiteral("Load from Newest Slot")); link_action_shortcut(ui->action_Save_to_Oldest_Slot, QStringLiteral("Save to Oldest Slot")); @@ -897,7 +890,10 @@ void GMainWindow::ConnectMenuEvents() { connect_menu(ui->action_Pause, &GMainWindow::OnPauseContinueGame); connect_menu(ui->action_Stop, &GMainWindow::OnStopGame); connect_menu(ui->action_Restart, [this] { BootGame(QString(game_path)); }); - connect_menu(ui->action_Report_Compatibility, &GMainWindow::OnMenuReportCompatibility); + connect_menu(ui->action_Report_Compatibility, []() { + QDesktopServices::openUrl(QUrl(QStringLiteral( + "https://github.com/Lime3DS/compatibility-list/blob/master/CONTRIBUTING.md"))); + }); connect_menu(ui->action_Configure, &GMainWindow::OnConfigure); connect_menu(ui->action_Configure_Current_Game, &GMainWindow::OnConfigurePerGame); @@ -937,16 +933,8 @@ void GMainWindow::ConnectMenuEvents() { connect_menu(ui->action_Save_Movie, &GMainWindow::OnSaveMovie); connect_menu(ui->action_Movie_Read_Only_Mode, [this](bool checked) { movie.SetReadOnly(checked); }); - connect_menu(ui->action_Enable_Frame_Advancing, [this] { - if (emulation_running) { - system.frame_limiter.SetFrameAdvancing(ui->action_Enable_Frame_Advancing->isChecked()); - ui->action_Advance_Frame->setEnabled(ui->action_Enable_Frame_Advancing->isChecked()); - } - }); connect_menu(ui->action_Advance_Frame, [this] { if (emulation_running && system.frame_limiter.IsFrameAdvancing()) { - ui->action_Enable_Frame_Advancing->setChecked(true); - ui->action_Advance_Frame->setEnabled(true); system.frame_limiter.AdvanceFrame(); } }); @@ -971,7 +959,8 @@ void GMainWindow::ConnectMenuEvents() { } void GMainWindow::UpdateMenuState() { - const bool is_paused = !emu_thread || !emu_thread->IsRunning(); + const bool is_paused = + !emu_thread || !emu_thread->IsRunning() || system.frame_limiter.IsFrameAdvancing(); const std::array running_actions{ ui->action_Stop, @@ -989,6 +978,7 @@ void GMainWindow::UpdateMenuState() { } ui->action_Capture_Screenshot->setEnabled(emulation_running && !is_paused); + ui->action_Advance_Frame->setEnabled(emulation_running && is_paused); if (emulation_running && is_paused) { ui->action_Pause->setText(tr("&Continue")); @@ -1408,12 +1398,7 @@ void GMainWindow::BootGame(const QString& filename) { movie_playback_path.clear(); } - if (ui->action_Enable_Frame_Advancing->isChecked()) { - ui->action_Advance_Frame->setEnabled(true); - system.frame_limiter.SetFrameAdvancing(true); - } else { - ui->action_Advance_Frame->setEnabled(false); - } + ui->action_Advance_Frame->setEnabled(false); if (video_dumping_on_start) { StartVideoDumping(video_dumping_path); @@ -1770,9 +1755,9 @@ void GMainWindow::OnGameListRemovePlayTimeData(u64 program_id) { bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, const std::string& comment, const std::filesystem::path& icon_path, - const std::filesystem::path& command, - const std::string& arguments, const std::string& categories, - const std::string& keywords, const std::string& name) try { + const std::string& command, const std::string& arguments, + const std::string& categories, const std::string& keywords, + const std::string& name, const bool& skip_tryexec) try { #if defined(__linux__) || defined(__FreeBSD__) // Linux and FreeBSD std::filesystem::path shortcut_path_full = shortcut_path / (name + ".desktop"); std::ofstream shortcut_stream(shortcut_path_full, std::ios::binary | std::ios::trunc); @@ -1791,8 +1776,10 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, if (std::filesystem::is_regular_file(icon_path)) { fmt::print(shortcut_stream, "Icon={}\n", icon_path.string()); } - fmt::print(shortcut_stream, "TryExec={}\n", command.string()); - fmt::print(shortcut_stream, "Exec={} {}\n", command.string(), arguments); + if (!skip_tryexec) { + fmt::print(shortcut_stream, "TryExec={}\n", command); + } + fmt::print(shortcut_stream, "Exec={} {}\n", command, arguments); if (!categories.empty()) { fmt::print(shortcut_stream, "Categories={}\n", categories); } @@ -1823,7 +1810,7 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, LOG_ERROR(Frontend, "Failed to create IShellLinkW instance"); return false; } - hres = ps1->SetPath(command.c_str()); + hres = ps1->SetPath(Common::UTF8ToUTF16W(command).data()); if (FAILED(hres)) { LOG_ERROR(Frontend, "Failed to set path"); return false; @@ -1902,16 +1889,16 @@ bool GMainWindow::CreateShortcutMessagesGUI(QWidget* parent, int message, bool GMainWindow::MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name, std::filesystem::path& out_icon_path) { - // Get path to Citra icons directory & icon extension + // Get path to Lime3DS icons directory & icon extension std::string ico_extension = "png"; #if defined(_WIN32) out_icon_path = FileUtil::GetUserPath(FileUtil::UserPath::IconsDir); ico_extension = "ico"; #elif defined(__linux__) || defined(__FreeBSD__) - out_icon_path = FileUtil::GetUserDirectory("XDG_DATA_HOME") + "/icons/hicolor/256x256"; + out_icon_path = FileUtil::GetUserDirectory("XDG_DATA_HOME") + "/icons/hicolor/256x256/"; #endif // Create icons directory if it doesn't exist - if (!FileUtil::CreateDir(out_icon_path.string())) { + if (!FileUtil::CreateFullPath(out_icon_path.string())) { QMessageBox::critical( this, tr("Create Icon"), tr("Cannot create icon file. Path \"%1\" does not exist and cannot be created.") @@ -1922,19 +1909,27 @@ bool GMainWindow::MakeShortcutIcoPath(const u64 program_id, const std::string_vi } // Create icon file path - out_icon_path /= (program_id == 0 ? fmt::format("citra-{}.{}", game_file_name, ico_extension) - : fmt::format("citra-{:016X}.{}", program_id, ico_extension)); + out_icon_path /= (program_id == 0 ? fmt::format("lime-{}.{}", game_file_name, ico_extension) + : fmt::format("lime-{:016X}.{}", program_id, ico_extension)); return true; } void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path, GameListShortcutTarget target) { - // Get path to citra executable - const QStringList args = QApplication::arguments(); - std::filesystem::path citra_command = args[0].toStdString(); - // If relative path, make it an absolute path - if (citra_command.c_str()[0] == '.') { - citra_command = FileUtil::GetCurrentDir().value_or("") + DIR_SEP + citra_command.string(); + std::string lime_command{}; + bool skip_tryexec = false; + const char* env_flatpak_id = getenv("FLATPAK_ID"); + if (env_flatpak_id) { + lime_command = fmt::format("flatpak run {}", env_flatpak_id); + skip_tryexec = true; + } else { + // Get path to Lime3DS executable + const QStringList args = QApplication::arguments(); + lime_command = args[0].toStdString(); + // If relative path, make it an absolute path + if (lime_command.c_str()[0] == '.') { + lime_command = FileUtil::GetCurrentDir().value_or("") + DIR_SEP + lime_command; + } } // Shortcut path @@ -1943,8 +1938,7 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga shortcut_path = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).toStdString(); } else if (target == GameListShortcutTarget::Applications) { - shortcut_path = - QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation).toStdString(); + shortcut_path = GetApplicationsDirectory(); } // Icon path and title @@ -1990,8 +1984,7 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga // Warn once if we are making a shortcut to a volatile AppImage const std::string appimage_ending = std::string(Common::g_scm_rev).substr(0, 9).append(".AppImage"); - if (citra_command.string().ends_with(appimage_ending) && - !UISettings::values.shortcut_already_warned) { + if (lime_command.ends_with(appimage_ending) && !UISettings::values.shortcut_already_warned) { if (CreateShortcutMessagesGUI(this, CREATE_SHORTCUT_MSGBOX_APPIMAGE_VOLATILE_WARNING, qt_game_title)) { return; @@ -2004,12 +1997,12 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga if (CreateShortcutMessagesGUI(this, CREATE_SHORTCUT_MSGBOX_FULLSCREEN_PROMPT, qt_game_title)) { arguments = "-f " + arguments; } - const std::string comment = fmt::format("Start {:s} with the Citra Emulator", game_title); + const std::string comment = fmt::format("Start {:s} with the Lime3DS Emulator", game_title); const std::string categories = "Game;Emulator;Qt;"; const std::string keywords = "3ds;Nintendo;"; - if (CreateShortcutLink(shortcut_path, comment, out_icon_path, citra_command, arguments, - categories, keywords, game_title)) { + if (CreateShortcutLink(shortcut_path, comment, out_icon_path, lime_command, arguments, + categories, keywords, game_title, skip_tryexec)) { CreateShortcutMessagesGUI(this, CREATE_SHORTCUT_MSGBOX_SUCCESS, qt_game_title); return; } @@ -2200,7 +2193,7 @@ void GMainWindow::OnCIAInstallReport(Service::AM::InstallStatus status, QString case Service::AM::InstallStatus::ErrorEncrypted: QMessageBox::critical(this, tr("Encrypted File"), tr("%1 must be decrypted " - "before being used with Lime. A real 3DS is required.") + "before being used with Lime3DS. A real 3DS is required.") .arg(filename)); break; case Service::AM::InstallStatus::ErrorFileNotFound: @@ -2293,6 +2286,7 @@ void GMainWindow::OnStartGame() { PreventOSSleep(); emu_thread->SetRunning(true); + system.frame_limiter.SetFrameAdvancing(false); graphics_api_button->setEnabled(false); qRegisterMetaType("Core::System::ResultStatus"); qRegisterMetaType("std::string"); @@ -2322,7 +2316,7 @@ void GMainWindow::OnRestartGame() { } void GMainWindow::OnPauseGame() { - emu_thread->SetRunning(false); + system.frame_limiter.SetFrameAdvancing(true); qt_cameras->PauseCameras(); play_time_manager->Stop(); @@ -2337,7 +2331,7 @@ void GMainWindow::OnPauseGame() { void GMainWindow::OnPauseContinueGame() { if (emulation_running) { - if (emu_thread->IsRunning()) { + if (emu_thread->IsRunning() && !system.frame_limiter.IsFrameAdvancing()) { OnPauseGame(); } else { OnStartGame(); @@ -2361,17 +2355,6 @@ void GMainWindow::OnLoadComplete() { UpdateSecondaryWindowVisibility(); } -void GMainWindow::OnMenuReportCompatibility() { - if (!NetSettings::values.citra_token.empty() && !NetSettings::values.citra_username.empty()) { - CompatDB compatdb{this}; - compatdb.exec(); - } else { - QMessageBox::critical(this, tr("Missing Citra Account"), - tr("You must link your Citra account to submit test cases." - "
Go to Emulation > Configure... > Web to do so.")); - } -} - void GMainWindow::ToggleFullscreen() { if (!emulation_running) { return; diff --git a/src/lime_qt/main.h b/src/lime_qt/main.h index 774668bfa..ccdf0ce9a 100644 --- a/src/lime_qt/main.h +++ b/src/lime_qt/main.h @@ -1,4 +1,4 @@ -// Copyright 2014 Citra Emulator Project +// Copyright Citra Emulator Project / Lime3DS Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -217,10 +217,10 @@ private: bool MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name, std::filesystem::path& out_icon_path); bool CreateShortcutLink(const std::filesystem::path& shortcut_path, const std::string& comment, - const std::filesystem::path& icon_path, - const std::filesystem::path& command, const std::string& arguments, - const std::string& categories, const std::string& keywords, - const std::string& name); + const std::filesystem::path& icon_path, const std::string& command, + const std::string& arguments, const std::string& categories, + const std::string& keywords, const std::string& name, + const bool& skip_tryexec); private slots: void OnStartGame(); @@ -230,7 +230,6 @@ private slots: void OnStopGame(); void OnSaveState(); void OnLoadState(); - void OnMenuReportCompatibility(); /// Called whenever a user selects a game in the game list widget. void OnGameListLoadFile(QString game_path); void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target); diff --git a/src/lime_qt/main.ui b/src/lime_qt/main.ui index 8bff4a659..cfa6b2e69 100644 --- a/src/lime_qt/main.ui +++ b/src/lime_qt/main.ui @@ -182,15 +182,8 @@ - - - Frame Advance - - - - + - @@ -309,7 +302,7 @@ - About Lime + About Lime3DS QAction::AboutRole @@ -404,14 +397,6 @@ Read-Only Mode - - - true - - - Enable Frame Advancing - - false @@ -575,15 +560,9 @@ - - false - Report Compatibility - - false - diff --git a/src/lime_qt/util/util.cpp b/src/lime_qt/util/util.cpp index cb14d376b..874b81012 100644 --- a/src/lime_qt/util/util.cpp +++ b/src/lime_qt/util/util.cpp @@ -1,10 +1,13 @@ -// Copyright 2015 Citra Emulator Project +// Copyright Citra Emulator Project / Lime3DS Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include #include #include +#include +#include "common/common_paths.h" +#include "common/file_util.h" #include "common/logging/log.h" #include "core/loader/smdh.h" #include "lime_qt/util/util.h" @@ -160,3 +163,15 @@ bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image) return false; #endif } + +const std::string GetApplicationsDirectory() { +// This alternate method is required for Flatpak compatibility as +// QStandardPaths::ApplicationsLocation returns a path inside the Flatpak data directory instead of +// $HOME/.local/share +#if defined(__linux__) || defined(__FreeBSD__) + return FileUtil::GetHomeDirectory() + DIR_SEP + ".local" + DIR_SEP + "share" + DIR_SEP + + "applications"; +#else + return QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation).toStdString(); +#endif +} \ No newline at end of file diff --git a/src/lime_qt/util/util.h b/src/lime_qt/util/util.h index 67bd51a90..428d69ffd 100644 --- a/src/lime_qt/util/util.h +++ b/src/lime_qt/util/util.h @@ -1,4 +1,4 @@ -// Copyright 2015 Citra Emulator Project +// Copyright Citra Emulator Project / Lime3DS Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -36,3 +36,8 @@ QPixmap GetQPixmapFromSMDH(const std::vector& smdh_data); * @return bool If the operation succeeded */ [[nodiscard]] bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image); + +/** + * @return The user’s applications directory + */ +[[nodiscard]] const std::string GetApplicationsDirectory(); diff --git a/src/video_core/rasterizer_cache/rasterizer_cache.h b/src/video_core/rasterizer_cache/rasterizer_cache.h index cd9c080ef..0144cbd61 100644 --- a/src/video_core/rasterizer_cache/rasterizer_cache.h +++ b/src/video_core/rasterizer_cache/rasterizer_cache.h @@ -647,10 +647,11 @@ typename T::Surface& RasterizerCache::GetTextureCube(const TextureCubeConfig& Surface& cube_surface = slot_surfaces[cube.surface_id]; for (u32 i = 0; i < addresses.size(); i++) { - if (!addresses[i]) { + const SurfaceId& face_id = cube.face_ids[i]; + if (!addresses[i] || !face_id) { continue; } - Surface& surface = slot_surfaces[cube.face_ids[i]]; + Surface& surface = slot_surfaces[face_id]; if (cube.ticks[i] == surface.modification_tick) { continue; } diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 514ec389b..b30e1f75e 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -1,4 +1,4 @@ -// Copyright 2022 Citra Emulator Project +// Copyright Citra Emulator Project / Lime3DS Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -675,12 +675,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(layout.is_portrait); DrawBottomScreen(layout, bottom_screen); } else { DrawBottomScreen(layout, bottom_screen); glUniform1i(uniform_layer, 0); - ApplySecondLayerOpacity(); + ApplySecondLayerOpacity(layout.is_portrait); DrawTopScreen(layout, top_screen); } @@ -692,13 +692,14 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool f DrawBottomScreen(layout, additional_screen); } } - ResetSecondLayerOpacity(); + ResetSecondLayerOpacity(layout.is_portrait); } -void RendererOpenGL::ApplySecondLayerOpacity() { -#ifndef ANDROID // TODO: Implement custom layouts on Android - if ((Settings::values.layout_option.GetValue() == Settings::LayoutOption::CustomLayout || - Settings::values.custom_layout) && +void RendererOpenGL::ApplySecondLayerOpacity(bool isPortrait) { + // TODO: Allow for second layer opacity in portrait mode android + + if (!isPortrait && + (Settings::values.layout_option.GetValue() == Settings::LayoutOption::CustomLayout) && Settings::values.custom_second_layer_opacity.GetValue() < 100) { state.blend.src_rgb_func = GL_CONSTANT_ALPHA; state.blend.src_a_func = GL_CONSTANT_ALPHA; @@ -706,13 +707,11 @@ 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 - if ((Settings::values.layout_option.GetValue() == Settings::LayoutOption::CustomLayout || - Settings::values.custom_layout) && +void RendererOpenGL::ResetSecondLayerOpacity(bool isPortrait) { + if (!isPortrait && + (Settings::values.layout_option.GetValue() == Settings::LayoutOption::CustomLayout) && Settings::values.custom_second_layer_opacity.GetValue() < 100) { state.blend.src_rgb_func = GL_ONE; state.blend.dst_rgb_func = GL_ZERO; @@ -720,7 +719,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..c589ed68d 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -1,4 +1,4 @@ -// Copyright 2022 Citra Emulator Project +// Copyright Citra Emulator Project / Lime3DS Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -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,