From a2e6891f01e8ba910d43078604419460bdba68de Mon Sep 17 00:00:00 2001 From: David Griswold Date: Fri, 5 Sep 2025 17:05:35 +0300 Subject: [PATCH] android: Re-fixed game termination bug (#1357) * EmulationActivity and EmulationFragment clear only their own hooks * EmulationLifecycleUtil: Rename `remove()` to `removeHook()` * EmulationLifecycleUtil: Removed unused function `clear()` * Corrected somewhat incorrect usage of the word "hook" * Define `onShutdown` and `onPause` hook functions in constructors * Formatting nitpicks * Updated license header * Re-added log messages for attempting to add duplicate hooks --------- Co-authored-by: OpenSauce04 --- .../citra_emu/activities/EmulationActivity.kt | 26 ++++++++++--------- .../citra_emu/fragments/EmulationFragment.kt | 13 ++++++++-- .../citra_emu/utils/EmulationLifecycleUtil.kt | 24 ++++++++++++----- 3 files changed, 43 insertions(+), 20 deletions(-) diff --git a/src/android/app/src/main/java/org/citra/citra_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/citra/citra_emu/activities/EmulationActivity.kt index 3ff594ce9..f23147dd8 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/activities/EmulationActivity.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/activities/EmulationActivity.kt @@ -60,7 +60,15 @@ class EmulationActivity : AppCompatActivity() { private lateinit var binding: ActivityEmulationBinding private lateinit var screenAdjustmentUtil: ScreenAdjustmentUtil private lateinit var hotkeyUtility: HotkeyUtility - private lateinit var secondaryDisplay: SecondaryDisplay; + private lateinit var secondaryDisplay: SecondaryDisplay + + private val onShutdown = Runnable { + if (intent.getBooleanExtra("launched_from_shortcut", false)) { + finishAffinity() + } else { + this.finish() + } + } private val emulationFragment: EmulationFragment get() { @@ -77,8 +85,8 @@ class EmulationActivity : AppCompatActivity() { ThemeUtil.setTheme(this) settingsViewModel.settings.loadSettings() super.onCreate(savedInstanceState) - secondaryDisplay = SecondaryDisplay(this); - secondaryDisplay.updateDisplay(); + secondaryDisplay = SecondaryDisplay(this) + secondaryDisplay.updateDisplay() binding = ActivityEmulationBinding.inflate(layoutInflater) screenAdjustmentUtil = ScreenAdjustmentUtil(this, windowManager, settingsViewModel.settings) @@ -101,13 +109,7 @@ class EmulationActivity : AppCompatActivity() { windowManager.defaultDisplay.rotation ) - EmulationLifecycleUtil.addShutdownHook(hook = { - if (intent.getBooleanExtra("launched_from_shortcut", false)) { - finishAffinity() - } else { - this.finish() - } - }) + EmulationLifecycleUtil.addShutdownHook(onShutdown) isEmulationRunning = true instance = this @@ -165,12 +167,12 @@ class EmulationActivity : AppCompatActivity() { } override fun onDestroy() { - EmulationLifecycleUtil.clear() + EmulationLifecycleUtil.removeHook(onShutdown) NativeLibrary.playTimeManagerStop() isEmulationRunning = false instance = null secondaryDisplay.releasePresentation() - secondaryDisplay.releaseVD(); + secondaryDisplay.releaseVD() super.onDestroy() } diff --git a/src/android/app/src/main/java/org/citra/citra_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/citra/citra_emu/fragments/EmulationFragment.kt index fbf3e5525..d3625693c 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/fragments/EmulationFragment.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/fragments/EmulationFragment.kt @@ -101,6 +101,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram private val emulationViewModel: EmulationViewModel by activityViewModels() private val settingsViewModel: SettingsViewModel by viewModels() + private val onPause = Runnable{ togglePause() } + private val onShutdown = Runnable{ emulationState.stop() } + override fun onAttach(context: Context) { super.onAttach(context) if (context is EmulationActivity) { @@ -156,8 +159,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram emulationState = EmulationState(game.path) emulationActivity = requireActivity() as EmulationActivity screenAdjustmentUtil = ScreenAdjustmentUtil(requireContext(), requireActivity().windowManager, settingsViewModel.settings) - EmulationLifecycleUtil.addShutdownHook(hook = { emulationState.stop() }) - EmulationLifecycleUtil.addPauseResumeHook(hook = { togglePause() }) + EmulationLifecycleUtil.addPauseResumeHook(onPause) + EmulationLifecycleUtil.addShutdownHook(onShutdown) } override fun onCreateView( @@ -507,6 +510,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram super.onDetach() } + override fun onDestroy() { + EmulationLifecycleUtil.removeHook(onPause) + EmulationLifecycleUtil.removeHook(onShutdown) + super.onDestroy() + } + private fun setupCitraDirectoriesThenStartEmulation() { val directoryInitializationState = DirectoryInitialization.start() if (directoryInitializationState === diff --git a/src/android/app/src/main/java/org/citra/citra_emu/utils/EmulationLifecycleUtil.kt b/src/android/app/src/main/java/org/citra/citra_emu/utils/EmulationLifecycleUtil.kt index 8f3b5dc07..5fafb7bed 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/utils/EmulationLifecycleUtil.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/utils/EmulationLifecycleUtil.kt @@ -1,4 +1,4 @@ -// Copyright 2023 Citra Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -18,15 +18,27 @@ object EmulationLifecycleUtil { } fun addShutdownHook(hook: Runnable) { - shutdownHooks.add(hook) + if (shutdownHooks.contains(hook)) { + Log.warning("[EmulationLifecycleUtil] Tried to add shutdown hook for function that already existed. Skipping.") + } else { + shutdownHooks.add(hook) + } } fun addPauseResumeHook(hook: Runnable) { - pauseResumeHooks.add(hook) + if (pauseResumeHooks.contains(hook)) { + Log.warning("[EmulationLifecycleUtil] Tried to add pause resume hook for function that already existed. Skipping.") + } else { + pauseResumeHooks.add(hook) + } } - fun clear() { - pauseResumeHooks.clear() - shutdownHooks.clear() + fun removeHook(hook: Runnable) { + if (pauseResumeHooks.contains(hook)) { + pauseResumeHooks.remove(hook) + } + if (shutdownHooks.contains(hook)) { + shutdownHooks.remove(hook) + } } }