diff --git a/.codespellrc b/.codespellrc index 786a991ebc..29bcf35026 100644 --- a/.codespellrc +++ b/.codespellrc @@ -3,4 +3,4 @@ [codespell] skip = ./.git,./build,./dist,./Doxyfile,./externals,./LICENSES -ignore-words-list = aci,allright,ba,deques,froms,hda,inout,lod,masia,nam,nax,nd,pullrequests,pullrequest,te,transfered,unstall,uscaled,zink +ignore-words-list = aci,allright,ba,deques,froms,hda,inout,lod,masia,nam,nax,nd,optin,pullrequests,pullrequest,te,transfered,unstall,uscaled,zink diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt index 022dd28fcd..4576c17f3d 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt @@ -223,6 +223,8 @@ object NativeLibrary { external fun getCompany(filename: String): String + external fun isHomebrew(filename: String): Boolean + external fun setAppDirectory(directory: String) external fun initializeGpuDriver( diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt index f4db61cb33..20a0394f54 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt @@ -6,15 +6,12 @@ package org.yuzu.yuzu_emu.activities import android.app.Activity import android.content.Context import android.content.Intent -import android.content.res.Configuration import android.graphics.Rect import android.hardware.Sensor import android.hardware.SensorEvent import android.hardware.SensorEventListener import android.hardware.SensorManager -import android.hardware.display.DisplayManager import android.os.Bundle -import android.view.Display import android.view.InputDevice import android.view.KeyEvent import android.view.MotionEvent @@ -23,7 +20,6 @@ import android.view.View import android.view.inputmethod.InputMethodManager import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity -import androidx.core.content.getSystemService import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsControllerCompat @@ -39,7 +35,6 @@ import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel import org.yuzu.yuzu_emu.fragments.EmulationFragment import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.utils.ControllerMappingHelper -import org.yuzu.yuzu_emu.utils.EmulationMenuSettings import org.yuzu.yuzu_emu.utils.ForegroundService import org.yuzu.yuzu_emu.utils.InputHandler import org.yuzu.yuzu_emu.utils.NfcReader @@ -148,11 +143,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { super.onResume() nfcReader.startScanning() startMotionSensorListener() - - NativeLibrary.notifyOrientationChange( - EmulationMenuSettings.landscapeScreenLayout, - getAdjustedRotation() - ) } override fun onPause() { @@ -258,24 +248,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { override fun onAccuracyChanged(sensor: Sensor, i: Int) {} - private fun getAdjustedRotation():Int { - val rotation = getSystemService()!!.getDisplay(Display.DEFAULT_DISPLAY).rotation - val config: Configuration = resources.configuration - - if ((config.screenLayout and Configuration.SCREENLAYOUT_LONG_YES) != 0 || - (config.screenLayout and Configuration.SCREENLAYOUT_LONG_NO) == 0 || - (config.screenLayout and Configuration.SCREENLAYOUT_SIZE_SMALL) != 0) { - return rotation - } - when (rotation) { - Surface.ROTATION_0 -> return Surface.ROTATION_90 - Surface.ROTATION_90 -> return Surface.ROTATION_0 - Surface.ROTATION_180 -> return Surface.ROTATION_270 - Surface.ROTATION_270 -> return Surface.ROTATION_180 - } - return rotation - } - private fun restoreState(savedInstanceState: Bundle) { game = savedInstanceState.parcelable(EXTRA_SELECTED_GAME)!! } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt index ebc0f164a2..adbe3696b4 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt @@ -127,13 +127,7 @@ class SearchFragment : Fragment() { } } - R.id.chip_homebrew -> { - baseList.filter { - Log.error("Guh - ${it.path}") - FileUtil.hasExtension(it.path, "nro") - || FileUtil.hasExtension(it.path, "nso") - } - } + R.id.chip_homebrew -> baseList.filter { it.isHomebrew } R.id.chip_retail -> baseList.filter { FileUtil.hasExtension(it.path, "xci") diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt index 2a17653b2f..3d6782c499 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt @@ -16,7 +16,8 @@ class Game( val regions: String, val path: String, val gameId: String, - val company: String + val company: String, + val isHomebrew: Boolean ) : Parcelable { val keyAddedToLibraryTime get() = "${gameId}_AddedToLibraryTime" val keyLastPlayedTime get() = "${gameId}_LastPlayed" @@ -31,6 +32,7 @@ class Game( && path == other.path && gameId == other.gameId && company == other.company + && isHomebrew == other.isHomebrew } companion object { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt index 7059856f13..d9b301210d 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt @@ -13,6 +13,8 @@ import androidx.preference.PreferenceManager import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.MissingFieldException import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json import org.yuzu.yuzu_emu.NativeLibrary @@ -20,6 +22,7 @@ import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.utils.GameHelper import java.util.Locale +@OptIn(ExperimentalSerializationApi::class) class GamesViewModel : ViewModel() { private val _games = MutableLiveData>(emptyList()) val games: LiveData> get() = _games @@ -49,7 +52,13 @@ class GamesViewModel : ViewModel() { if (storedGames!!.isNotEmpty()) { val deserializedGames = mutableSetOf() storedGames.forEach { - val game: Game = Json.decodeFromString(it) + val game: Game + try { + game = Json.decodeFromString(it) + } catch (e: MissingFieldException) { + return@forEach + } + val gameExists = DocumentFile.fromSingleUri(YuzuApplication.appContext, Uri.parse(game.path)) ?.exists() diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt index c9f5797acd..aa424c768e 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt @@ -765,18 +765,20 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context // If we have API access, calculate the safe area to draw the overlay var cutoutLeft = 0 var cutoutBottom = 0 - val insets = context.windowManager.currentWindowMetrics.windowInsets.displayCutout - if (insets != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - if (insets.boundingRectTop.bottom != 0 && insets.boundingRectTop.bottom > maxY / 2) - insets.boundingRectTop.bottom.toFloat() else maxY - if (insets.boundingRectRight.left != 0 && insets.boundingRectRight.left > maxX / 2) - insets.boundingRectRight.left.toFloat() else maxX + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + val insets = context.windowManager.currentWindowMetrics.windowInsets.displayCutout + if (insets != null) { + if (insets.boundingRectTop.bottom != 0 && insets.boundingRectTop.bottom > maxY / 2) + insets.boundingRectTop.bottom.toFloat() else maxY + if (insets.boundingRectRight.left != 0 && insets.boundingRectRight.left > maxX / 2) + insets.boundingRectRight.left.toFloat() else maxX - minX = insets.boundingRectLeft.right - insets.boundingRectLeft.left - minY = insets.boundingRectBottom.top - insets.boundingRectBottom.bottom + minX = insets.boundingRectLeft.right - insets.boundingRectLeft.left + minY = insets.boundingRectBottom.top - insets.boundingRectBottom.bottom - cutoutLeft = insets.boundingRectRight.right - insets.boundingRectRight.left - cutoutBottom = insets.boundingRectTop.top - insets.boundingRectTop.bottom + cutoutLeft = insets.boundingRectRight.right - insets.boundingRectRight.left + cutoutBottom = insets.boundingRectTop.top - insets.boundingRectTop.bottom + } } // This makes sure that if we have an inset on one side of the screen, we mirror it on diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt index ba6b5783ec..42b2076184 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt @@ -6,7 +6,6 @@ package org.yuzu.yuzu_emu.utils import android.content.SharedPreferences import android.net.Uri import androidx.preference.PreferenceManager -import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import org.yuzu.yuzu_emu.NativeLibrary @@ -83,7 +82,8 @@ object GameHelper { NativeLibrary.getRegions(filePath), filePath, gameId, - NativeLibrary.getCompany(filePath) + NativeLibrary.getCompany(filePath), + NativeLibrary.isHomebrew(filePath) ) val addedTime = preferences.getLong(newGame.keyAddedToLibraryTime, 0L) diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 8a91c562f7..cdf7622caf 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -13,8 +13,7 @@ #include #include -#include -#include +#include #include "common/detached_tasks.h" #include "common/dynamic_library.h" @@ -59,390 +58,365 @@ namespace { -class EmulationSession final { -public: - EmulationSession() { - m_vfs = std::make_shared(); - } + class EmulationSession final { + public: + EmulationSession() { + m_vfs = std::make_shared(); + } - ~EmulationSession() = default; + ~EmulationSession() = default; - static EmulationSession& GetInstance() { - return s_instance; - } + static EmulationSession& GetInstance() { + return s_instance; + } - const Core::System& System() const { - return m_system; - } + const Core::System& System() const { + return m_system; + } - Core::System& System() { - return m_system; - } + Core::System& System() { + return m_system; + } - const EmuWindow_Android& Window() const { - return *m_window; - } + const EmuWindow_Android& Window() const { + return *m_window; + } - EmuWindow_Android& Window() { - return *m_window; - } + EmuWindow_Android& Window() { + return *m_window; + } - ANativeWindow* NativeWindow() const { - return m_native_window; - } + ANativeWindow* NativeWindow() const { + return m_native_window; + } - void SetNativeWindow(ANativeWindow* native_window) { - m_native_window = native_window; - } + void SetNativeWindow(ANativeWindow* native_window) { + m_native_window = native_window; + } - u32 ScreenRotation() const { - return m_screen_rotation; - } - - void SetScreenRotation(u32 screen_rotation) { - m_screen_rotation = screen_rotation; - } - - void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir, - const std::string& custom_driver_name, - const std::string& file_redirect_dir) { + void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir, + const std::string& custom_driver_name, + const std::string& file_redirect_dir) { #ifdef ARCHITECTURE_arm64 - void* handle{}; - const char* file_redirect_dir_{}; - int featureFlags{}; + void* handle{}; + const char* file_redirect_dir_{}; + int featureFlags{}; - // Enable driver file redirection when renderer debugging is enabled. - if (Settings::values.renderer_debug && file_redirect_dir.size()) { - featureFlags |= ADRENOTOOLS_DRIVER_FILE_REDIRECT; - file_redirect_dir_ = file_redirect_dir.c_str(); - } + // Enable driver file redirection when renderer debugging is enabled. + if (Settings::values.renderer_debug && file_redirect_dir.size()) { + featureFlags |= ADRENOTOOLS_DRIVER_FILE_REDIRECT; + file_redirect_dir_ = file_redirect_dir.c_str(); + } - // Try to load a custom driver. - if (custom_driver_name.size()) { - handle = adrenotools_open_libvulkan( - RTLD_NOW, featureFlags | ADRENOTOOLS_DRIVER_CUSTOM, nullptr, hook_lib_dir.c_str(), - custom_driver_dir.c_str(), custom_driver_name.c_str(), file_redirect_dir_, nullptr); - } + // Try to load a custom driver. + if (custom_driver_name.size()) { + handle = adrenotools_open_libvulkan( + RTLD_NOW, featureFlags | ADRENOTOOLS_DRIVER_CUSTOM, nullptr, hook_lib_dir.c_str(), + custom_driver_dir.c_str(), custom_driver_name.c_str(), file_redirect_dir_, nullptr); + } - // Try to load the system driver. - if (!handle) { - handle = - adrenotools_open_libvulkan(RTLD_NOW, featureFlags, nullptr, hook_lib_dir.c_str(), - nullptr, nullptr, file_redirect_dir_, nullptr); - } + // Try to load the system driver. + if (!handle) { + handle = + adrenotools_open_libvulkan(RTLD_NOW, featureFlags, nullptr, hook_lib_dir.c_str(), + nullptr, nullptr, file_redirect_dir_, nullptr); + } - m_vulkan_library = std::make_shared(handle); + m_vulkan_library = std::make_shared(handle); #endif - } - - bool IsRunning() const { - std::scoped_lock lock(m_mutex); - return m_is_running; - } - - const Core::PerfStatsResults& PerfStats() const { - std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex); - return m_perf_stats; - } - - void SurfaceChanged() { - if (!IsRunning()) { - return; - } - m_window->OnSurfaceChanged(m_native_window); - m_system.Renderer().NotifySurfaceChanged(); - } - - Core::SystemResultStatus InitializeEmulation(const std::string& filepath) { - std::scoped_lock lock(m_mutex); - - // Loads the configuration. - Config{}; - - // Create the render window. - m_window = std::make_unique(&m_input_subsystem, m_native_window, - m_vulkan_library); - - // Initialize system. - auto android_keyboard = std::make_unique(); - m_software_keyboard = android_keyboard.get(); - m_system.SetShuttingDown(false); - m_system.ApplySettings(); - m_system.HIDCore().ReloadInputDevices(); - m_system.SetContentProvider(std::make_unique()); - m_system.SetFilesystem(std::make_shared()); - m_system.SetAppletFrontendSet({ - nullptr, // Amiibo Settings - nullptr, // Controller Selector - nullptr, // Error Display - nullptr, // Mii Editor - nullptr, // Parental Controls - nullptr, // Photo Viewer - nullptr, // Profile Selector - std::move(android_keyboard), // Software Keyboard - nullptr, // Web Browser - }); - m_system.GetFileSystemController().CreateFactories(*m_system.GetFilesystem()); - - // Initialize account manager - m_profile_manager = std::make_unique(); - - // Load the ROM. - m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath); - if (m_load_result != Core::SystemResultStatus::Success) { - return m_load_result; } - // Complete initialization. - m_system.GPU().Start(); - m_system.GetCpuManager().OnGpuReady(); - m_system.RegisterExitCallback([&] { HaltEmulation(); }); - - return Core::SystemResultStatus::Success; - } - - void ShutdownEmulation() { - std::scoped_lock lock(m_mutex); - - m_is_running = false; - - // Unload user input. - m_system.HIDCore().UnloadInputDevices(); - - // Shutdown the main emulated process - if (m_load_result == Core::SystemResultStatus::Success) { - m_system.DetachDebugger(); - m_system.ShutdownMainProcess(); - m_detached_tasks.WaitForAllTasks(); - m_load_result = Core::SystemResultStatus::ErrorNotInitialized; - } - - // Tear down the render window. - m_window.reset(); - } - - void PauseEmulation() { - std::scoped_lock lock(m_mutex); - m_system.Pause(); - } - - void UnPauseEmulation() { - std::scoped_lock lock(m_mutex); - m_system.Run(); - } - - void HaltEmulation() { - std::scoped_lock lock(m_mutex); - m_is_running = false; - m_cv.notify_one(); - } - - void RunEmulation() { - { + bool IsRunning() const { std::scoped_lock lock(m_mutex); - m_is_running = true; + return m_is_running; } - // Load the disk shader cache. - if (Settings::values.use_disk_shader_cache.GetValue()) { - LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); - m_system.Renderer().ReadRasterizer()->LoadDiskResources( - m_system.GetApplicationProcessProgramID(), std::stop_token{}, - LoadDiskCacheProgress); - LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); + const Core::PerfStatsResults& PerfStats() const { + std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex); + return m_perf_stats; } - void(m_system.Run()); - - if (m_system.DebuggerEnabled()) { - m_system.InitializeDebugger(); + void SurfaceChanged() { + if (!IsRunning()) { + return; + } + m_window->OnSurfaceChanged(m_native_window); + m_system.Renderer().NotifySurfaceChanged(); } - while (true) { + Core::SystemResultStatus InitializeEmulation(const std::string& filepath) { + std::scoped_lock lock(m_mutex); + + // Loads the configuration. + Config{}; + + // Create the render window. + m_window = std::make_unique(&m_input_subsystem, m_native_window, + m_vulkan_library); + + // Initialize system. + auto android_keyboard = std::make_unique(); + m_software_keyboard = android_keyboard.get(); + m_system.SetShuttingDown(false); + m_system.ApplySettings(); + m_system.HIDCore().ReloadInputDevices(); + m_system.SetContentProvider(std::make_unique()); + m_system.SetFilesystem(std::make_shared()); + m_system.SetAppletFrontendSet({ + nullptr, // Amiibo Settings + nullptr, // Controller Selector + nullptr, // Error Display + nullptr, // Mii Editor + nullptr, // Parental Controls + nullptr, // Photo Viewer + nullptr, // Profile Selector + std::move(android_keyboard), // Software Keyboard + nullptr, // Web Browser + }); + m_system.GetFileSystemController().CreateFactories(*m_system.GetFilesystem()); + + // Initialize account manager + m_profile_manager = std::make_unique(); + + // Load the ROM. + m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath); + if (m_load_result != Core::SystemResultStatus::Success) { + return m_load_result; + } + + // Complete initialization. + m_system.GPU().Start(); + m_system.GetCpuManager().OnGpuReady(); + m_system.RegisterExitCallback([&] { HaltEmulation(); }); + + return Core::SystemResultStatus::Success; + } + + void ShutdownEmulation() { + std::scoped_lock lock(m_mutex); + + m_is_running = false; + + // Unload user input. + m_system.HIDCore().UnloadInputDevices(); + + // Shutdown the main emulated process + if (m_load_result == Core::SystemResultStatus::Success) { + m_system.DetachDebugger(); + m_system.ShutdownMainProcess(); + m_detached_tasks.WaitForAllTasks(); + m_load_result = Core::SystemResultStatus::ErrorNotInitialized; + } + + // Tear down the render window. + m_window.reset(); + } + + void PauseEmulation() { + std::scoped_lock lock(m_mutex); + m_system.Pause(); + } + + void UnPauseEmulation() { + std::scoped_lock lock(m_mutex); + m_system.Run(); + } + + void HaltEmulation() { + std::scoped_lock lock(m_mutex); + m_is_running = false; + m_cv.notify_one(); + } + + void RunEmulation() { { - std::unique_lock lock(m_mutex); - if (m_cv.wait_for(lock, std::chrono::milliseconds(800), - [&]() { return !m_is_running; })) { - // Emulation halted. - break; + std::scoped_lock lock(m_mutex); + m_is_running = true; + } + + // Load the disk shader cache. + if (Settings::values.use_disk_shader_cache.GetValue()) { + LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); + m_system.Renderer().ReadRasterizer()->LoadDiskResources( + m_system.GetApplicationProcessProgramID(), std::stop_token{}, + LoadDiskCacheProgress); + LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); + } + + void(m_system.Run()); + + if (m_system.DebuggerEnabled()) { + m_system.InitializeDebugger(); + } + + while (true) { + { + std::unique_lock lock(m_mutex); + if (m_cv.wait_for(lock, std::chrono::milliseconds(800), + [&]() { return !m_is_running; })) { + // Emulation halted. + break; + } + } + { + // Refresh performance stats. + std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex); + m_perf_stats = m_system.GetAndResetPerfStats(); } } - { - // Refresh performance stats. - std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex); - m_perf_stats = m_system.GetAndResetPerfStats(); + } + + std::string GetRomTitle(const std::string& path) { + return GetRomMetadata(path).title; + } + + std::vector GetRomIcon(const std::string& path) { + return GetRomMetadata(path).icon; + } + + bool GetIsHomebrew(const std::string& path) { + return GetRomMetadata(path).isHomebrew; + } + + void ResetRomMetadata() { + m_rom_metadata_cache.clear(); + } + + bool IsHandheldOnly() { + const auto npad_style_set = m_system.HIDCore().GetSupportedStyleTag(); + + if (npad_style_set.fullkey == 1) { + return false; } - } - } - std::string GetRomTitle(const std::string& path) { - return GetRomMetadata(path).title; - } + if (npad_style_set.handheld == 0) { + return false; + } - std::vector GetRomIcon(const std::string& path) { - return GetRomMetadata(path).icon; - } - - void ResetRomMetadata() { - m_rom_metadata_cache.clear(); - } - - bool IsHandheldOnly() { - const auto npad_style_set = m_system.HIDCore().GetSupportedStyleTag(); - - if (npad_style_set.fullkey == 1) { - return false; + return !Settings::values.use_docked_mode.GetValue(); } - if (npad_style_set.handheld == 0) { - return false; + void SetDeviceType(int index, int type) { + auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); + controller->SetNpadStyleIndex(static_cast(type)); } - return !Settings::values.use_docked_mode.GetValue(); - } + void OnGamepadConnectEvent(int index) { + auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); - void SetDeviceType(int index, int type) { - auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); - controller->SetNpadStyleIndex(static_cast(type)); - } + // Ensure that player1 is configured correctly and handheld disconnected + if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) { + auto handheld = + m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); - void OnGamepadConnectEvent(int index) { - auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); + if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) { + handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); + controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); + handheld->Disconnect(); + } + } - // Ensure that player1 is configured correctly and handheld disconnected - if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) { - auto handheld = - m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); + // Ensure that handheld is configured correctly and player 1 disconnected + if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) { + auto player1 = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); - if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) { - handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); - controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); - handheld->Disconnect(); + if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) { + player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); + controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); + player1->Disconnect(); + } + } + + if (!controller->IsConnected()) { + controller->Connect(); } } - // Ensure that handheld is configured correctly and player 1 disconnected - if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) { - auto player1 = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); + void OnGamepadDisconnectEvent(int index) { + auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); + controller->Disconnect(); + } - if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) { - player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); - controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); - player1->Disconnect(); + SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard() { + return m_software_keyboard; + } + + private: + struct RomMetadata { + std::string title; + std::vector icon; + bool isHomebrew; + }; + + RomMetadata GetRomMetadata(const std::string& path) { + if (auto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) { + return search->second; } + + return CacheRomMetadata(path); } - if (!controller->IsConnected()) { - controller->Connect(); - } - } + RomMetadata CacheRomMetadata(const std::string& path) { + const auto file = Core::GetGameFileFromPath(m_vfs, path); + auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0); - void OnGamepadDisconnectEvent(int index) { - auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); - controller->Disconnect(); - } - - SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard() { - return m_software_keyboard; - } - - void DirectConnectToRoom(const std::string& nickname, const char* server_addr = "127.0.0.1", - u16 server_port = Network::DefaultRoomPort, - const std::string& password = "") { - auto room_network = m_system.GetRoomNetwork(); - - if (const auto member = room_network.GetRoomMember().lock()) { - // Prevent the user from trying to join a room while they are already joining. - if (member->GetState() == Network::RoomMember::State::Joining || member->IsConnected()) { - return; + RomMetadata entry; + loader->ReadTitle(entry.title); + loader->ReadIcon(entry.icon); + if (loader->GetFileType() == Loader::FileType::NRO) { + auto loader_nro = dynamic_cast(loader.get()); + entry.isHomebrew = loader_nro->IsHomebrew(); } else { - member->Join(nickname, server_addr, server_port); + entry.isHomebrew = false; } - } - } - Network::RoomMember::State GetRoomMemberState() { - if (const auto member = m_system.GetRoomNetwork().GetRoomMember().lock()) { - return member->GetState(); - } else { - return Network::RoomMember::State::Idle; - } - } + m_rom_metadata_cache[path] = entry; -private: - struct RomMetadata { - std::string title; - std::vector icon; + return entry; + } + + private: + static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max) { + JNIEnv* env = IDCache::GetEnvForThread(); + env->CallStaticVoidMethod(IDCache::GetDiskCacheProgressClass(), + IDCache::GetDiskCacheLoadProgress(), static_cast(stage), + static_cast(progress), static_cast(max)); + } + + private: + static EmulationSession s_instance; + + // Frontend management + std::unordered_map m_rom_metadata_cache; + + // Window management + std::unique_ptr m_window; + ANativeWindow* m_native_window{}; + + // Core emulation + Core::System m_system; + InputCommon::InputSubsystem m_input_subsystem; + Common::DetachedTasks m_detached_tasks; + Core::PerfStatsResults m_perf_stats{}; + std::shared_ptr m_vfs; + Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized}; + bool m_is_running{}; + SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{}; + std::unique_ptr m_profile_manager; + + // GPU driver parameters + std::shared_ptr m_vulkan_library; + + // Synchronization + std::condition_variable_any m_cv; + mutable std::mutex m_perf_stats_mutex; + mutable std::mutex m_mutex; }; - RomMetadata GetRomMetadata(const std::string& path) { - if (auto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) { - return search->second; - } - - return CacheRomMetadata(path); - } - - RomMetadata CacheRomMetadata(const std::string& path) { - const auto file = Core::GetGameFileFromPath(m_vfs, path); - const auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0); - - RomMetadata entry; - loader->ReadTitle(entry.title); - loader->ReadIcon(entry.icon); - - m_rom_metadata_cache[path] = entry; - - return entry; - } - -private: - static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max) { - JNIEnv* env = IDCache::GetEnvForThread(); - env->CallStaticVoidMethod(IDCache::GetDiskCacheProgressClass(), - IDCache::GetDiskCacheLoadProgress(), static_cast(stage), - static_cast(progress), static_cast(max)); - } - -private: - static EmulationSession s_instance; - - // Frontend management - std::unordered_map m_rom_metadata_cache; - - // Window management - std::unique_ptr m_window; - ANativeWindow* m_native_window{}; - u32 m_screen_rotation{}; - - // Core emulation - Core::System m_system; - InputCommon::InputSubsystem m_input_subsystem; - Common::DetachedTasks m_detached_tasks; - Core::PerfStatsResults m_perf_stats{}; - std::shared_ptr m_vfs; - Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized}; - bool m_is_running{}; - SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{}; - std::unique_ptr m_profile_manager; - - // GPU driver parameters - std::shared_ptr m_vulkan_library; - - // Synchronization - std::condition_variable_any m_cv; - mutable std::mutex m_perf_stats_mutex; - mutable std::mutex m_mutex; -}; - /*static*/ EmulationSession EmulationSession::s_instance; } // Anonymous namespace -u32 GetAndroidScreenRotation() { - return EmulationSession::GetInstance().ScreenRotation(); -} - static Core::SystemResultStatus RunEmulation(const std::string& filepath) { Common::Log::Initialize(); Common::Log::SetColorConsoleBackendEnabled(true); @@ -486,13 +460,6 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceDestroyed(JNIEnv* env, EmulationSession::GetInstance().SurfaceChanged(); } -void Java_org_yuzu_yuzu_1emu_NativeLibrary_notifyOrientationChange(JNIEnv* env, - [[maybe_unused]] jclass clazz, - jint layout_option, - jint rotation) { - return EmulationSession::GetInstance().SetScreenRotation(static_cast(rotation)); -} - void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, [[maybe_unused]] jclass clazz, jstring j_directory) { @@ -500,11 +467,11 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, } void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver( - JNIEnv* env, [[maybe_unused]] jclass clazz, jstring hook_lib_dir, jstring custom_driver_dir, - jstring custom_driver_name, jstring file_redirect_dir) { + JNIEnv* env, [[maybe_unused]] jclass clazz, jstring hook_lib_dir, jstring custom_driver_dir, + jstring custom_driver_name, jstring file_redirect_dir) { EmulationSession::GetInstance().InitializeGpuDriver( - GetJString(env, hook_lib_dir), GetJString(env, custom_driver_dir), - GetJString(env, custom_driver_name), GetJString(env, file_redirect_dir)); + GetJString(env, hook_lib_dir), GetJString(env, custom_driver_dir), + GetJString(env, custom_driver_name), GetJString(env, file_redirect_dir)); } jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadKeys(JNIEnv* env, @@ -562,7 +529,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent([[maybe_unu } jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent( - [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint j_device) { + [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint j_device) { if (EmulationSession::GetInstance().IsRunning()) { EmulationSession::GetInstance().OnGamepadDisconnectEvent(j_device); } @@ -592,12 +559,12 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadJoystickEvent([[maybe_un } jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMotionEvent( - [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint j_device, - jlong delta_timestamp, jfloat gyro_x, jfloat gyro_y, jfloat gyro_z, jfloat accel_x, - jfloat accel_y, jfloat accel_z) { + [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint j_device, + jlong delta_timestamp, jfloat gyro_x, jfloat gyro_y, jfloat gyro_z, jfloat accel_x, + jfloat accel_y, jfloat accel_z) { if (EmulationSession::GetInstance().IsRunning()) { EmulationSession::GetInstance().Window().OnGamepadMotionEvent( - j_device, delta_timestamp, gyro_x, gyro_y, gyro_z, accel_x, accel_y, accel_z); + j_device, delta_timestamp, gyro_x, gyro_y, gyro_z, accel_x, accel_y, accel_z); } return static_cast(true); } @@ -687,8 +654,14 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany([[maybe_unused]] JNIEnv return env->NewStringUTF(""); } +jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHomebrew([[maybe_unused]] JNIEnv* env, + [[maybe_unused]] jclass clazz, + [[maybe_unused]] jstring j_filename) { + return EmulationSession::GetInstance().GetIsHomebrew(GetJString(env, j_filename)); +} + void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation - [[maybe_unused]] (JNIEnv* env, [[maybe_unused]] jclass clazz) { +[[maybe_unused]] (JNIEnv* env, [[maybe_unused]] jclass clazz) { // Create the default config.ini. Config{}; // Initialize the emulated system. @@ -701,8 +674,8 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore([[maybe_unused]] JNIEn } void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z( - [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, [[maybe_unused]] jstring j_file, - [[maybe_unused]] jstring j_savestate, [[maybe_unused]] jboolean j_delete_savestate) {} + [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, [[maybe_unused]] jstring j_file, + [[maybe_unused]] jstring j_savestate, [[maybe_unused]] jboolean j_delete_savestate) {} void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz) { @@ -765,7 +738,7 @@ jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats([[maybe_unused]] } void Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_setSysDirectory( - [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jstring j_path) {} + [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jstring j_path) {} void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, @@ -796,45 +769,4 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_submitInlineKeyboardInput(JNIEnv* env EmulationSession::GetInstance().SoftwareKeyboard()->SubmitInlineKeyboardInput(j_key_code); } -void Java_org_yuzu_yuzu_1emu_NativeLibrary_connectToRoom(JNIEnv* env, jclass clazz, - jstring nickname, - jstring server_addr, - jint server_port, - jstring password) { - EmulationSession::GetInstance().DirectConnectToRoom( - GetJString(env, nickname), - GetJString(env, server_addr).c_str(), - server_port, - GetJString(env, password) - ); -} - -jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRoomMemberState(JNIEnv* env, jclass clazz) { - auto state = EmulationSession::GetInstance().GetRoomMemberState(); - - std::string state_str{}; - - switch(state) { - using State = Network::RoomMember::State; - - case State::Uninitialized: - state_str = "Uninitialized"; - break; - case State::Idle: - state_str = "Idle"; - break; - case State::Joining: - state_str = "Joining"; - break; - case State::Joined: - state_str = "Joined"; - break; - case State::Moderator: - state_str = "Moderator"; - break; - } - - return env->NewStringUTF(state_str.c_str()); -} - -} // extern "C" +} // extern "C" \ No newline at end of file diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 73d04d7ee5..7be6cf5f35 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -33,7 +33,8 @@ static_assert(sizeof(NroSegmentHeader) == 0x8, "NroSegmentHeader has incorrect s struct NroHeader { INSERT_PADDING_BYTES(0x4); u32_le module_header_offset; - INSERT_PADDING_BYTES(0x8); + u32 magic_ext1; + u32 magic_ext2; u32_le magic; INSERT_PADDING_BYTES(0x4); u32_le file_size; @@ -124,6 +125,16 @@ FileType AppLoader_NRO::IdentifyType(const FileSys::VirtualFile& nro_file) { return FileType::Error; } +bool AppLoader_NRO::IsHomebrew() { + // Read NSO header + NroHeader nro_header{}; + if (sizeof(NroHeader) != file->ReadObject(&nro_header)) { + return false; + } + return nro_header.magic_ext1 == Common::MakeMagic('H', 'O', 'M', 'E') && + nro_header.magic_ext2 == Common::MakeMagic('B', 'R', 'E', 'W'); +} + static constexpr u32 PageAlignSize(u32 size) { return static_cast((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK); } diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h index ccb77b581a..8de6eebc67 100644 --- a/src/core/loader/nro.h +++ b/src/core/loader/nro.h @@ -38,6 +38,8 @@ public: */ static FileType IdentifyType(const FileSys::VirtualFile& nro_file); + bool IsHomebrew(); + FileType GetFileType() const override { return IdentifyType(file); } diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index 7cdde992b9..acb143fc7a 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -37,10 +37,6 @@ #include "video_core/vulkan_common/vulkan_memory_allocator.h" #include "video_core/vulkan_common/vulkan_wrapper.h" -#ifdef ANDROID -extern u32 GetAndroidScreenRotation(); -#endif - namespace Vulkan { namespace { @@ -78,47 +74,6 @@ struct ScreenRectVertex { } }; -#ifdef ANDROID - -std::array MakeOrthographicMatrix(f32 width, f32 height) { - constexpr u32 ROTATION_0 = 0; - constexpr u32 ROTATION_90 = 1; - constexpr u32 ROTATION_180 = 2; - constexpr u32 ROTATION_270 = 3; - - // clang-format off - switch (GetAndroidScreenRotation()) { - case ROTATION_0: - // Desktop - return { 2.f / width, 0.f, 0.f, 0.f, - 0.f, 2.f / height, 0.f, 0.f, - 0.f, 0.f, 1.f, 0.f, - -1.f, -1.f, 0.f, 1.f}; - case ROTATION_180: - // Reverse desktop - return {-2.f / width, 0.f, 0.f, 0.f, - 0.f, -2.f / height, 0.f, 0.f, - 0.f, 0.f, 1.f, 0.f, - 1.f, 1.f, 0.f, 1.f}; - case ROTATION_270: - // Reverse landscape - return { 0.f, -2.f / width, 0.f, 0.f, - 2.f / height, 0.f, 0.f, 0.f, - 0.f, 0.f, 1.f, 0.f, - -1.f, 1.f, 0.f, 1.f}; - case ROTATION_90: - default: - // Landscape - return { 0.f, 2.f / width, 0.f, 0.f, - -2.f / height, 0.f, 0.f, 0.f, - 0.f, 0.f, 1.f, 0.f, - 1.f, -1.f, 0.f, 1.f}; - } - // clang-format on -} - -#else - std::array MakeOrthographicMatrix(f32 width, f32 height) { // clang-format off return { 2.f / width, 0.f, 0.f, 0.f, @@ -128,8 +83,6 @@ std::array MakeOrthographicMatrix(f32 width, f32 height) { // clang-format on } -#endif - u32 GetBytesPerPixel(const Tegra::FramebufferConfig& framebuffer) { using namespace VideoCore::Surface; return BytesPerBlock(PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)); @@ -1159,7 +1112,7 @@ void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) { .pNext = nullptr, .flags = 0, .imageType = VK_IMAGE_TYPE_2D, - .format = GetFormat(framebuffer), + .format = used_on_framebuffer ? VK_FORMAT_R16G16B16A16_SFLOAT : GetFormat(framebuffer), .extent = { .width = (up_scale * framebuffer.width) >> down_shift, @@ -1180,14 +1133,14 @@ void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) { const auto create_commit = [&](vk::Image& image) { return memory_allocator.Commit(image, MemoryUsage::DeviceLocal); }; - const auto create_image_view = [&](vk::Image& image) { + const auto create_image_view = [&](vk::Image& image, bool used_on_framebuffer = false) { return device.GetLogical().CreateImageView(VkImageViewCreateInfo{ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .pNext = nullptr, .flags = 0, .image = *image, .viewType = VK_IMAGE_VIEW_TYPE_2D, - .format = GetFormat(framebuffer), + .format = used_on_framebuffer ? VK_FORMAT_R16G16B16A16_SFLOAT : GetFormat(framebuffer), .components = { .r = VK_COMPONENT_SWIZZLE_IDENTITY, @@ -1217,7 +1170,7 @@ void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) { const u32 down_shift = Settings::values.resolution_info.down_shift; aa_image = create_image(true, up_scale, down_shift); aa_commit = create_commit(aa_image); - aa_image_view = create_image_view(aa_image); + aa_image_view = create_image_view(aa_image, true); VkExtent2D size{ .width = (up_scale * framebuffer.width) >> down_shift, .height = (up_scale * framebuffer.height) >> down_shift, diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index afcf34fba4..d3cddac690 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -231,7 +231,12 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bo .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, +#ifdef ANDROID + // On Android, do not allow surface rotation to deviate from the frontend. + .preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR, +#else .preTransform = capabilities.currentTransform, +#endif .compositeAlpha = alpha_flags, .presentMode = present_mode, .clipped = VK_FALSE, diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index fe12fa8f35..bac9dff90b 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -760,6 +760,7 @@ void Config::ReadRendererValues() { ReadGlobalSetting(Settings::values.use_fast_gpu_time); ReadGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache); ReadGlobalSetting(Settings::values.enable_compute_pipelines); + ReadGlobalSetting(Settings::values.use_video_framerate); ReadGlobalSetting(Settings::values.bg_red); ReadGlobalSetting(Settings::values.bg_green); ReadGlobalSetting(Settings::values.bg_blue); @@ -1415,6 +1416,7 @@ void Config::SaveRendererValues() { WriteGlobalSetting(Settings::values.use_fast_gpu_time); WriteGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache); WriteGlobalSetting(Settings::values.enable_compute_pipelines); + WriteGlobalSetting(Settings::values.use_video_framerate); WriteGlobalSetting(Settings::values.bg_red); WriteGlobalSetting(Settings::values.bg_green); WriteGlobalSetting(Settings::values.bg_blue);