diff --git a/app/src/main/java/app/gamenative/ui/PluviaMain.kt b/app/src/main/java/app/gamenative/ui/PluviaMain.kt index 0035c72aa9..d397e53b6e 100644 --- a/app/src/main/java/app/gamenative/ui/PluviaMain.kt +++ b/app/src/main/java/app/gamenative/ui/PluviaMain.kt @@ -2,6 +2,7 @@ package app.gamenative.ui import android.content.Context import android.content.Intent +import androidx.activity.compose.BackHandler import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.WindowInsets @@ -1096,6 +1097,10 @@ fun PluviaMain( } } + BackHandler(enabled = state.loadingDialogVisible && !SteamService.keepAlive) { + // TODO: Make prelaunch/loading operations cancellable so Back can exit safely. + } + PluviaTheme( isDark = when (state.appTheme) { AppTheme.AUTO -> isSystemInDarkTheme() diff --git a/app/src/main/java/app/gamenative/ui/screen/xserver/XServerScreen.kt b/app/src/main/java/app/gamenative/ui/screen/xserver/XServerScreen.kt index d1b76c799a..e81376a65a 100644 --- a/app/src/main/java/app/gamenative/ui/screen/xserver/XServerScreen.kt +++ b/app/src/main/java/app/gamenative/ui/screen/xserver/XServerScreen.kt @@ -2533,6 +2533,43 @@ private fun shiftXEnvironmentToContext( return environment } + +private fun hardKillStaleWineProcessesBeforeLaunch() { + val deadlineMs = System.currentTimeMillis() + 5_000 + val staleWinePids = ProcessHelper.listRunningWineProcesses() + .mapNotNull { it.toIntOrNull() } + .distinct() + + if (staleWinePids.isEmpty()) { + return + } + + Timber.w( + "Found %d stale Wine process(es) before launch; hard-killing them: %s", + staleWinePids.size, + staleWinePids.joinToString(), + ) + ProcessHelper.killAllWineProcesses() + + var remainingWinePids: List + do { + Thread.sleep(100) + remainingWinePids = ProcessHelper.listRunningWineProcesses() + .mapNotNull { it.toIntOrNull() } + .distinct() + } while (remainingWinePids.isNotEmpty() && System.currentTimeMillis() < deadlineMs) + + if (remainingWinePids.isNotEmpty()) { + Timber.w( + "Wine processes still present after hard-kill attempt: %s", + remainingWinePids.joinToString(), + ) + throw IllegalStateException( + "Wine processes still present after hard-kill attempt: ${remainingWinePids.joinToString()}" + ) + } +} + private fun setupXEnvironment( context: Context, appId: String, @@ -2550,6 +2587,8 @@ private fun setupXEnvironment( onGameLaunchError: ((String) -> Unit)? = null, navigateBack: () -> Unit, ): XEnvironment { + hardKillStaleWineProcessesBeforeLaunch() + val gameSource = ContainerUtils.extractGameSourceFromContainerId(appId) val lc_all = container!!.lC_ALL val imageFs = ImageFs.find(context) diff --git a/app/src/main/java/com/winlator/core/ProcessHelper.java b/app/src/main/java/com/winlator/core/ProcessHelper.java index 0363ea2a84..e203f6064c 100644 --- a/app/src/main/java/com/winlator/core/ProcessHelper.java +++ b/app/src/main/java/com/winlator/core/ProcessHelper.java @@ -37,12 +37,22 @@ public static void terminateProcess(int pid) { // Log.d("ProcessHelper", "Process terminated with pid: " + pid); } + public static void killProcess(int pid) { + Process.sendSignal(pid, SIGKILL); + } + public static void terminateAllWineProcesses() { for (String process : listRunningWineProcesses()) { terminateProcess(Integer.parseInt(process)); } } + public static void killAllWineProcesses() { + for (String process : listRunningWineProcesses()) { + killProcess(Integer.parseInt(process)); + } + } + public static void pauseAllWineProcesses() { for (String process : listRunningWineProcesses()) { suspendProcess(Integer.parseInt(process));