From da3322bbb20760c1fd432af09d59a51c92e1447a Mon Sep 17 00:00:00 2001 From: moi952 <52632400+moi952@users.noreply.github.com> Date: Sun, 19 Jul 2026 18:07:01 +0200 Subject: [PATCH 1/7] feat:Add support for installing native Android game builds (Steam Frame) Steam now lets some games be installed as a native Android app instead of the usual Windows build run through Wine (ahead of Valve's Steam Frame headset). This adds a per-game Version choice in Edit Container: picking Android downloads, installs, and launches the game as a real Android app, bypassing Wine entirely for that game. --- app/src/main/AndroidManifest.xml | 1 + .../java/app/gamenative/data/DepotInfo.kt | 8 +- app/src/main/java/app/gamenative/enums/OS.kt | 1 + .../app/gamenative/service/DownloadService.kt | 2 +- .../app/gamenative/service/SteamService.kt | 74 ++++++--- .../main/java/app/gamenative/ui/PluviaMain.kt | 8 + .../component/dialog/ContainerConfigDialog.kt | 11 ++ .../component/dialog/ContainerConfigState.kt | 2 + .../ui/component/dialog/GameManagerDialog.kt | 2 +- .../ui/component/dialog/GeneralTab.kt | 24 ++- .../app/gamenative/ui/model/MainViewModel.kt | 25 +++- .../screen/library/appscreen/BaseAppScreen.kt | 40 +++++ .../library/appscreen/SteamAppScreen.kt | 57 ++++++- .../gamenative/utils/AndroidGameLauncher.kt | 141 ++++++++++++++++++ .../app/gamenative/utils/ContainerUtils.kt | 2 + .../app/gamenative/utils/UpdateInstaller.kt | 2 +- .../com/winlator/container/Container.java | 18 +++ .../com/winlator/container/ContainerData.kt | 4 + app/src/main/res/values-fr/strings.xml | 7 + app/src/main/res/values/strings.xml | 7 + app/src/main/res/xml/file_provider_paths.xml | 4 + app/src/modern/AndroidManifest.xml | 1 + app/src/modernXr/AndroidManifest.xml | 1 + 23 files changed, 407 insertions(+), 35 deletions(-) create mode 100644 app/src/main/java/app/gamenative/utils/AndroidGameLauncher.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 62ff02d129..29f6f4110f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -16,6 +16,7 @@ + diff --git a/app/src/main/java/app/gamenative/data/DepotInfo.kt b/app/src/main/java/app/gamenative/data/DepotInfo.kt index 0d5084ade0..4b38565871 100644 --- a/app/src/main/java/app/gamenative/data/DepotInfo.kt +++ b/app/src/main/java/app/gamenative/data/DepotInfo.kt @@ -27,8 +27,12 @@ data class DepotInfo( val systemDefined: Boolean = false, val steamDeck: Boolean = false, ) { - /** Windows or OS-untagged (neither Linux nor macOS) */ + /** Windows or OS-untagged (neither Linux, macOS nor Android) */ val isWindowsCompatible: Boolean get() = osList.contains(OS.windows) || - (!osList.contains(OS.linux) && !osList.contains(OS.macos)) + (!osList.contains(OS.linux) && !osList.contains(OS.macos) && !osList.contains(OS.android)) + + /** Explicitly tagged for Android (Steam Frame / Lepton depots) */ + val isAndroidCompatible: Boolean + get() = osList.contains(OS.android) } diff --git a/app/src/main/java/app/gamenative/enums/OS.kt b/app/src/main/java/app/gamenative/enums/OS.kt index c62c0203a0..f506986537 100644 --- a/app/src/main/java/app/gamenative/enums/OS.kt +++ b/app/src/main/java/app/gamenative/enums/OS.kt @@ -8,6 +8,7 @@ enum class OS(val code: Int) { windows(0x01), macos(0x02), linux(0x04), + android(0x08), ; companion object { diff --git a/app/src/main/java/app/gamenative/service/DownloadService.kt b/app/src/main/java/app/gamenative/service/DownloadService.kt index 511f9e55f4..0d02635995 100644 --- a/app/src/main/java/app/gamenative/service/DownloadService.kt +++ b/app/src/main/java/app/gamenative/service/DownloadService.kt @@ -80,7 +80,7 @@ object DownloadService { } fun getSizeFromStoreDisplay (appId: Int, branch: String = "public"): String { - val depots = SteamService.getDownloadableDepots(appId) + val depots = SteamService.getDownloadableDepots(appId, wantAndroid = SteamService.isAndroidPlatform(appId)) val installBytes = depots.values.sumOf { (it.manifests[branch] ?: it.manifests["public"])?.size ?: 0L } return StorageUtils.formatBinarySize(installBytes) } diff --git a/app/src/main/java/app/gamenative/service/SteamService.kt b/app/src/main/java/app/gamenative/service/SteamService.kt index 62872563cc..3143e2ea98 100644 --- a/app/src/main/java/app/gamenative/service/SteamService.kt +++ b/app/src/main/java/app/gamenative/service/SteamService.kt @@ -50,6 +50,7 @@ import app.gamenative.enums.SaveLocation import app.gamenative.enums.SyncResult import app.gamenative.events.AndroidEvent import app.gamenative.events.SteamEvent +import app.gamenative.utils.AndroidGameLauncher import app.gamenative.utils.CaseInsensitiveFileSystem import app.gamenative.utils.ContainerUtils import app.gamenative.utils.FileUtils @@ -841,6 +842,7 @@ class SteamService : Service(), IChallengeUrlChanged { licensedDepotIds: Set? = null, hasSteamUnlockedBranch: Boolean = false, dlcAppIdsWithSingleDepots: Set? = null, + wantAndroid: Boolean = false, ): Boolean { if (depot.manifests.isEmpty() && depot.encryptedManifests.isNotEmpty() && !hasSteamUnlockedBranch) return false @@ -850,9 +852,14 @@ class SteamService : Service(), IChallengeUrlChanged { depot.sharedInstall if (!hasContent) return false - // 2. Supported OS - if (!depot.isWindowsCompatible) + // 2. Supported OS (Windows/untagged by default, or the Android depot when the + // user opted into the Android platform for this container) + if (wantAndroid) { + if (!depot.isAndroidCompatible) + return false + } else if (!depot.isWindowsCompatible) { return false + } // 3. 64-bit or indeterminate // Arch selection: allow 64-bit and Unknown always. // Allow 32-bit only when no 64-bit depot exists. @@ -915,12 +922,14 @@ class SteamService : Service(), IChallengeUrlChanged { preferredLanguage: String, ownedDlc: Map?, licensedDepotIds: Set?, + wantAndroid: Boolean = false, ): Collection { val dlcAppIdsWithSingleDepots = getDlcAppIdsWithSingleDepot(depots) return depots.values.filter { depot -> filterForDownloadableDepots(depot, prefer64Bit = false, preferNonDeckWindows = false, preferredLanguage, ownedDlc, licensedDepotIds, - dlcAppIdsWithSingleDepots = dlcAppIdsWithSingleDepots + dlcAppIdsWithSingleDepots = dlcAppIdsWithSingleDepots, + wantAndroid = wantAndroid, ) } } @@ -935,23 +944,25 @@ class SteamService : Service(), IChallengeUrlChanged { ownedDlc: Map?, licensedDepotIds: Set?, hasSteamUnlockedBranch: Boolean = false, + wantAndroid: Boolean = false, ): Map { val dlcAppIdsWithSingleDepots = getDlcAppIdsWithSingleDepot(depots) val effectiveLanguage = SteamUtils.effectiveDepotLanguage( depots, preferredLanguage, ownedDlc, licensedDepotIds, hasSteamUnlockedBranch, ) - val eligible = eligibleDepots(depots, effectiveLanguage, ownedDlc, licensedDepotIds) + val eligible = eligibleDepots(depots, effectiveLanguage, ownedDlc, licensedDepotIds, wantAndroid) val has64Bit = eligible.any { it.osArch == OSArch.Arch64 } - val hasNonDeckWin = eligible.any { !it.steamDeck && it.isWindowsCompatible } + val hasNonDeckWin = eligible.any { !it.steamDeck && (if (wantAndroid) it.isAndroidCompatible else it.isWindowsCompatible) } return depots.filter { (_, depot) -> filterForDownloadableDepots(depot, has64Bit, hasNonDeckWin, effectiveLanguage, ownedDlc, licensedDepotIds, - dlcAppIdsWithSingleDepots = dlcAppIdsWithSingleDepots + dlcAppIdsWithSingleDepots = dlcAppIdsWithSingleDepots, + wantAndroid = wantAndroid, ) } } - fun getMainAppDepots(appId: Int, containerLanguage: String): Map { + fun getMainAppDepots(appId: Int, containerLanguage: String, wantAndroid: Boolean = false): Map { val appInfo = getAppInfoOf(appId) ?: return emptyMap() val ownedDlc = runBlocking { getOwnedAppDlc(appId) } val hasSteamUnlockedBranch = runBlocking { getSteamUnlockedBranches(appId).isNotEmpty() } @@ -974,7 +985,7 @@ class SteamService : Service(), IChallengeUrlChanged { } } - val baseDepots = resolveDownloadableDepots(appInfo.depots, containerLanguage, ownedDlc, licensedDepots, hasSteamUnlockedBranch) + val baseDepots = resolveDownloadableDepots(appInfo.depots, containerLanguage, ownedDlc, licensedDepots, hasSteamUnlockedBranch, wantAndroid) // Find in the depots of mainApp, that if any of the depotID is actually belongs to another steam_app entry // override the dlcAppId to the corresponding app id @@ -995,28 +1006,28 @@ class SteamService : Service(), IChallengeUrlChanged { * Get downloadable depots for a given app (default language), including all DLCs * @return Map of app ID to depot ID to depot info */ - fun getDownloadableDepots(appId: Int): Map { + fun getDownloadableDepots(appId: Int, wantAndroid: Boolean = false): Map { val preferredLanguage = PrefManager.containerLanguage - return getDownloadableDepots(appId, preferredLanguage) + return getDownloadableDepots(appId, preferredLanguage, wantAndroid) } /** * Get downloadable depots for a given app (container language), including all DLCs * @return Map of app ID to depot ID to depot info */ - fun getDownloadableDepots(appId: Int, preferredLanguage: String): Map { + fun getDownloadableDepots(appId: Int, preferredLanguage: String, wantAndroid: Boolean = false): Map { val appInfo = getAppInfoOf(appId) ?: return emptyMap() val ownedDlc = runBlocking { getOwnedAppDlc(appId) } val hasSteamUnlockedBranch = runBlocking { getSteamUnlockedBranches(appId).isNotEmpty() } val licensedDepots = getLicensedDepotIds(appId).orEmpty().toMutableSet() - val map = getMainAppDepots(appId, preferredLanguage).toMutableMap() + val map = getMainAppDepots(appId, preferredLanguage, wantAndroid).toMutableMap() // parent app's arch applies to DLC arch selection val mainLanguage = SteamUtils.effectiveDepotLanguage( appInfo.depots, preferredLanguage, ownedDlc, licensedDepots, hasSteamUnlockedBranch, ) - val has64Bit = eligibleDepots(appInfo.depots, mainLanguage, ownedDlc, licensedDepots) + val has64Bit = eligibleDepots(appInfo.depots, mainLanguage, ownedDlc, licensedDepots, wantAndroid) .any { it.osArch == OSArch.Arch64 } val indirectDlcApps = getDownloadableDlcAppsOf(appId).orEmpty() @@ -1027,13 +1038,14 @@ class SteamService : Service(), IChallengeUrlChanged { val dlcLanguage = SteamUtils.effectiveDepotLanguage( dlcApp.depots, preferredLanguage, null, dlcLicensedDepots, hasSteamUnlockedBranch, ) - val dlcEligible = eligibleDepots(dlcApp.depots, dlcLanguage, null, dlcLicensedDepots) - val dlcHasNonDeckWin = dlcEligible.any { !it.steamDeck && it.isWindowsCompatible } + val dlcEligible = eligibleDepots(dlcApp.depots, dlcLanguage, null, dlcLicensedDepots, wantAndroid) + val dlcHasNonDeckWin = dlcEligible.any { !it.steamDeck && (if (wantAndroid) it.isAndroidCompatible else it.isWindowsCompatible) } dlcApp.depots .filter { (_, depot) -> filterForDownloadableDepots(depot, has64Bit, dlcHasNonDeckWin, dlcLanguage, null, dlcLicensedDepots, hasSteamUnlockedBranch, - dlcAppIdsWithSingleDepots = dlcAppIdsWithSingleDepots + dlcAppIdsWithSingleDepots = dlcAppIdsWithSingleDepots, + wantAndroid = wantAndroid, ) } .forEach { (depotId, depot) -> @@ -1407,6 +1419,12 @@ class SteamService : Service(), IChallengeUrlChanged { } } + /** Whether the container for [appId] has the game's Android (Steam Frame / Lepton) depot selected. */ + fun isAndroidPlatform(appId: Int): Boolean { + val container = ContainerManager(instance!!.applicationContext).getContainerById("STEAM_${appId}") + return container?.platform.equals(Container.PLATFORM_ANDROID, ignoreCase = true) + } + fun downloadApp(appId: Int, dlcAppIds: List, branch: String = "public", isUpdateOrVerify: Boolean): DownloadInfo? { if (!checkWifiOrNotify()) return null return getAppInfoOf(appId)?.let { appInfo -> @@ -1416,17 +1434,17 @@ class SteamService : Service(), IChallengeUrlChanged { } else { PrefManager.containerLanguage } + val wantAndroid = container?.platform.equals(Container.PLATFORM_ANDROID, ignoreCase = true) - Timber.tag("SteamService").d("downloadApp: downloading app $appId with language $containerLanguage, branch $branch") - - val depots = getDownloadableDepots(appId = appId, preferredLanguage = containerLanguage) + val depots = getDownloadableDepots(appId = appId, preferredLanguage = containerLanguage, wantAndroid = wantAndroid) downloadApp( appId = appId, downloadableDepots = depots, userSelectedDlcAppIds = dlcAppIds, branch = branch, containerLanguage = containerLanguage, - isUpdateOrVerify = isUpdateOrVerify) + isUpdateOrVerify = isUpdateOrVerify, + wantAndroid = wantAndroid) } } @@ -1726,6 +1744,7 @@ class SteamService : Service(), IChallengeUrlChanged { branch: String, containerLanguage: String, isUpdateOrVerify: Boolean, + wantAndroid: Boolean = false, ): DownloadInfo? { val appDirPath = getAppDirPath(appId) @@ -1741,7 +1760,7 @@ class SteamService : Service(), IChallengeUrlChanged { } // Depots from Main game - val mainDepots = getMainAppDepots(appId, containerLanguage) + val mainDepots = getMainAppDepots(appId, containerLanguage, wantAndroid) var mainAppDepots = mainDepots.filter { (_, depot) -> depot.dlcAppId == INVALID_APP_ID } + mainDepots.filter { (_, depot) -> @@ -2221,6 +2240,19 @@ class SteamService : Service(), IChallengeUrlChanged { PluviaApp.events.emit(AndroidEvent.PostInstallSyncStatusChanged(appId, false)) } } + + // Android platform: the download itself doesn't put the game on the device — + // kick off the system install prompt right away instead of waiting for a + // manual Play click. + if (isAndroidPlatform(appId)) { + when (AndroidGameLauncher.installAndLaunch(svc.applicationContext, appId)) { + AndroidGameLauncher.Result.InstallStarted -> + SnackbarManager.show(svc.getString(R.string.android_game_install_started)) + AndroidGameLauncher.Result.Failed -> + SnackbarManager.show(svc.getString(R.string.android_game_launch_failed)) + AndroidGameLauncher.Result.Launched -> Unit + } + } } } } diff --git a/app/src/main/java/app/gamenative/ui/PluviaMain.kt b/app/src/main/java/app/gamenative/ui/PluviaMain.kt index 1db8e407da..9b59354b12 100644 --- a/app/src/main/java/app/gamenative/ui/PluviaMain.kt +++ b/app/src/main/java/app/gamenative/ui/PluviaMain.kt @@ -1620,6 +1620,14 @@ fun preLaunchApp( ContainerUtils.getOrCreateContainer(context, appId) } + // Native Android (Steam Frame / Lepton) build: no Wine/Proton container, no manifest + // components to resolve — skip straight to launch. + if (container.platform.equals(Container.PLATFORM_ANDROID, ignoreCase = true)) { + setLoadingDialogVisible(false) + onSuccess(context, appId) + return@launch + } + // Clear session metadata on every launch to ensure fresh values container.clearSessionMetadata() diff --git a/app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt b/app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt index 2384978ad3..57fb3052c1 100644 --- a/app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt +++ b/app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt @@ -279,6 +279,7 @@ fun ContainerConfigDialog( default: Boolean = false, title: String, initialConfig: ContainerData = ContainerData(), + hasAndroidVersion: Boolean = false, onDismissRequest: () -> Unit, onSave: (ContainerData) -> Unit, ) { @@ -378,6 +379,7 @@ fun ContainerConfigDialog( val installedLists = availability?.installed val isBionicVariant = config.containerVariant.equals(Container.BIONIC, ignoreCase = true) + val isAndroidPlatform = config.platform.equals(Container.PLATFORM_ANDROID, ignoreCase = true) val manifestDownloadMessage = if (manifestDownloadLabel.isNotEmpty()) { stringResource(R.string.manifest_downloading_item, manifestDownloadLabel) } else { @@ -1175,6 +1177,8 @@ fun ContainerConfigDialog( gpuExtensions = gpuExtensions, inspectionMode = inspectionMode, isBionicVariant = isBionicVariant, + isAndroidPlatform = isAndroidPlatform, + hasAndroidVersion = hasAndroidVersion, nonDeletableDriveLetters = nonDeletableDriveLetters, availableDriveLetters = availableDriveLetters, launchManifestInstall = { entry, label, isDriver, expectedType, onInstalled -> @@ -1252,6 +1256,12 @@ fun ContainerConfigDialog( }, ) { paddingValues -> var selectedTab by rememberSaveable { mutableIntStateOf(0) } + // Android platform doesn't use Wine/Box64 at all: force the user back to + // General (where the platform picker lives) so they can't get stuck on a + // now-disabled tab. + LaunchedEffect(isAndroidPlatform) { + if (isAndroidPlatform) selectedTab = 0 + } val tabs = listOf( stringResource(R.string.container_config_tab_general), stringResource(R.string.container_config_tab_graphics), @@ -1302,6 +1312,7 @@ fun ContainerConfigDialog( tabs.forEachIndexed { index, label -> Tab( selected = selectedTab == index, + enabled = index == 0 || !isAndroidPlatform, onClick = { selectedTab = index }, text = { Text(text = label) }, modifier = if (index == 0) { diff --git a/app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigState.kt b/app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigState.kt index c8d34010b3..df067886b0 100644 --- a/app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigState.kt +++ b/app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigState.kt @@ -127,6 +127,8 @@ class ContainerConfigState( val gpuExtensions: List, val inspectionMode: Boolean, val isBionicVariant: Boolean, + val isAndroidPlatform: Boolean, + val hasAndroidVersion: Boolean, val nonDeletableDriveLetters: Set, val availableDriveLetters: List, val launchManifestInstall: (ManifestEntry, String, Boolean, ContentProfile.ContentType?, () -> Unit) -> Unit, diff --git a/app/src/main/java/app/gamenative/ui/component/dialog/GameManagerDialog.kt b/app/src/main/java/app/gamenative/ui/component/dialog/GameManagerDialog.kt index 679ad64420..49ef66474d 100644 --- a/app/src/main/java/app/gamenative/ui/component/dialog/GameManagerDialog.kt +++ b/app/src/main/java/app/gamenative/ui/component/dialog/GameManagerDialog.kt @@ -116,7 +116,7 @@ fun GameManagerDialog( allDownloadableApps.clear() // Get Downloadable Depots - val allPossibleDownloadableDepots = SteamService.getDownloadableDepots(gameId) + val allPossibleDownloadableDepots = SteamService.getDownloadableDepots(gameId, wantAndroid = SteamService.isAndroidPlatform(gameId)) downloadableDepots.putAll(allPossibleDownloadableDepots) // Get Optional DLC IDs diff --git a/app/src/main/java/app/gamenative/ui/component/dialog/GeneralTab.kt b/app/src/main/java/app/gamenative/ui/component/dialog/GeneralTab.kt index 66653ed568..abbd8dc0ae 100644 --- a/app/src/main/java/app/gamenative/ui/component/dialog/GeneralTab.kt +++ b/app/src/main/java/app/gamenative/ui/component/dialog/GeneralTab.kt @@ -137,7 +137,28 @@ fun GeneralTabContent( ) } - SettingsGroup() { + SettingsGroup { + if (state.hasAndroidVersion) { + val platformItems = listOf( + stringResource(R.string.container_platform_normal), + stringResource(R.string.container_platform_android), + ) + val platformIndex = if (config.platform.equals(Container.PLATFORM_ANDROID, ignoreCase = true)) 1 else 0 + SettingsListDropdown( + colors = settingsTileColors(), + title = { Text(text = stringResource(R.string.container_platform)) }, + subtitle = { Text(text = stringResource(R.string.container_platform_description)) }, + value = platformIndex, + items = platformItems, + enabled = true, + onItemSelected = { idx -> + val newPlatform = if (idx == 1) Container.PLATFORM_ANDROID else Container.PLATFORM_WINDOWS + state.config.value = config.copy(platform = newPlatform) + }, + ) + } + } + SettingsGroup(enabled = !state.isAndroidPlatform) { run { val variantIndex = rememberSaveable { mutableIntStateOf( @@ -282,7 +303,6 @@ fun GeneralTabContent( } } SettingsListDropdown( - enabled = true, value = state.languageIndex.value, items = state.languages.map(displayNameForLanguage), fallbackDisplay = displayNameForLanguage("english"), diff --git a/app/src/main/java/app/gamenative/ui/model/MainViewModel.kt b/app/src/main/java/app/gamenative/ui/model/MainViewModel.kt index 4b7fb79617..75d28c4070 100644 --- a/app/src/main/java/app/gamenative/ui/model/MainViewModel.kt +++ b/app/src/main/java/app/gamenative/ui/model/MainViewModel.kt @@ -7,6 +7,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import app.gamenative.PluviaApp import app.gamenative.PrefManager +import app.gamenative.R import app.gamenative.data.GameProcessInfo import app.gamenative.data.GameSource import app.gamenative.data.LibraryPlayHistory @@ -29,11 +30,14 @@ import app.gamenative.utils.CustomGameScanner import app.gamenative.ui.data.MainState import app.gamenative.ui.enums.ConnectionState import app.gamenative.ui.screen.PluviaScreen +import app.gamenative.ui.util.SnackbarManager +import app.gamenative.utils.AndroidGameLauncher import app.gamenative.utils.ContainerUtils import app.gamenative.utils.IntentLaunchManager import app.gamenative.utils.SteamUtils import app.gamenative.utils.UpdateInfo import com.materialkolor.PaletteStyle +import com.winlator.container.Container import com.winlator.xserver.Window import dagger.hilt.android.lifecycle.HiltViewModel import `in`.dragonbra.javasteam.steam.handlers.steamapps.AppProcessInfo @@ -462,8 +466,27 @@ class MainViewModel @Inject constructor( } fun launchApp(context: Context, appId: String) { - // Show booting splash before launching the app viewModelScope.launch { + val container = withContext(Dispatchers.IO) { ContainerUtils.getOrCreateContainer(context, appId) } + if (container.platform.equals(Container.PLATFORM_ANDROID, ignoreCase = true)) { + // Native Android (Steam Frame / Lepton) build: no Wine container involved at all. + withContext(Dispatchers.IO) { + libraryPlayHistoryDao.upsert( + LibraryPlayHistory(appId = appId, lastPlayed = System.currentTimeMillis()), + ) + } + val gameId = ContainerUtils.extractGameIdFromContainerId(appId) + when (withContext(Dispatchers.IO) { AndroidGameLauncher.installAndLaunch(context, gameId) }) { + AndroidGameLauncher.Result.InstallStarted -> + SnackbarManager.show(context.getString(R.string.android_game_install_started)) + AndroidGameLauncher.Result.Failed -> + SnackbarManager.show(context.getString(R.string.android_game_launch_failed)) + AndroidGameLauncher.Result.Launched -> Unit + } + return@launch + } + + // Show booting splash before launching the app viewModelScope.launch(Dispatchers.IO) { libraryPlayHistoryDao.upsert( LibraryPlayHistory( diff --git a/app/src/main/java/app/gamenative/ui/screen/library/appscreen/BaseAppScreen.kt b/app/src/main/java/app/gamenative/ui/screen/library/appscreen/BaseAppScreen.kt index 03d6f40f59..a9217dce9e 100644 --- a/app/src/main/java/app/gamenative/ui/screen/library/appscreen/BaseAppScreen.kt +++ b/app/src/main/java/app/gamenative/ui/screen/library/appscreen/BaseAppScreen.kt @@ -606,6 +606,12 @@ abstract class BaseAppScreen { protected open fun supportsSaveTransfer(libraryItem: LibraryItem): Boolean = false + /** Whether this game also has a native Android (Steam Frame / Lepton) depot available. */ + open fun hasAndroidVersion(libraryItem: LibraryItem): Boolean = false + + /** Package name of the Android app installed/installing for this game, or null if not applicable. */ + open fun getAndroidPackageName(context: Context, libraryItem: LibraryItem): String? = null + protected open suspend fun exportSaves( context: Context, libraryItem: LibraryItem, @@ -1070,6 +1076,39 @@ abstract class BaseAppScreen { performStateRefresh(true) } + // Android platform games: the actual install happens in the system's own installer UI, + // outside our control, so listen for its completion broadcast instead of requiring the + // user to leave and come back to this screen for the Play button to appear. + DisposableEffect(libraryItem.appId) { + val androidPackageName = getAndroidPackageName(context, libraryItem) + if (androidPackageName == null) { + onDispose { } + } else { + val receiver = object : android.content.BroadcastReceiver() { + override fun onReceive(ctx: Context, intent: Intent) { + if (intent.data?.schemeSpecificPart == androidPackageName) { + requestStateRefresh(true) + } + } + } + val filter = android.content.IntentFilter().apply { + addAction(Intent.ACTION_PACKAGE_ADDED) + addAction(Intent.ACTION_PACKAGE_REPLACED) + addAction(Intent.ACTION_PACKAGE_REMOVED) + addDataScheme("package") + } + androidx.core.content.ContextCompat.registerReceiver( + context, + receiver, + filter, + androidx.core.content.ContextCompat.RECEIVER_NOT_EXPORTED, + ) + onDispose { + context.unregisterReceiver(receiver) + } + } + } + var showConfigDialog by androidx.compose.runtime.remember { androidx.compose.runtime.mutableStateOf(false) } @@ -1380,6 +1419,7 @@ abstract class BaseAppScreen { ContainerConfigDialog( title = "${displayInfo.name} Config", initialConfig = containerData, + hasAndroidVersion = remember(libraryItem.appId) { hasAndroidVersion(libraryItem) }, onDismissRequest = { showConfigDialog = false }, onSave = { saveContainerConfig(context, libraryItem, it) diff --git a/app/src/main/java/app/gamenative/ui/screen/library/appscreen/SteamAppScreen.kt b/app/src/main/java/app/gamenative/ui/screen/library/appscreen/SteamAppScreen.kt index 0d5e3c987e..66285b1bb4 100644 --- a/app/src/main/java/app/gamenative/ui/screen/library/appscreen/SteamAppScreen.kt +++ b/app/src/main/java/app/gamenative/ui/screen/library/appscreen/SteamAppScreen.kt @@ -57,6 +57,7 @@ import app.gamenative.ui.data.AppMenuOption import app.gamenative.ui.data.GameDisplayInfo import app.gamenative.ui.enums.AppOptionMenuType import app.gamenative.ui.enums.DialogType +import app.gamenative.utils.AndroidGameLauncher import app.gamenative.utils.ContainerUtils import app.gamenative.utils.MarkerUtils import app.gamenative.utils.SteamUtils @@ -367,7 +368,14 @@ class SteamAppScreen : BaseAppScreen() { } override fun isInstalled(context: Context, libraryItem: LibraryItem): Boolean { - return SteamService.isAppInstalled(libraryItem.gameId) + val gameId = libraryItem.gameId + if (!SteamService.isAppInstalled(gameId)) return false + // Android platform: the depot being downloaded isn't the same as the app actually + // being installed on the system — only show Play once it really is. + if (SteamService.isAndroidPlatform(gameId)) { + return AndroidGameLauncher.isGameInstalled(context, gameId) + } + return true } override fun isValidToDownload(context: Context, libraryItem: LibraryItem): Boolean { @@ -863,17 +871,43 @@ class SteamAppScreen : BaseAppScreen() { override fun saveContainerConfig(context: Context, libraryItem: LibraryItem, config: ContainerData) { val container = getContainer(context, libraryItem.appId) + val platformChanged = !container.platform.equals(config.platform, ignoreCase = true) + val languageChanged = container.language != config.language ContainerUtils.applyToContainer(context, libraryItem.appId, config) - if (container.language != config.language) { - CoroutineScope(Dispatchers.IO).launch { - SteamService.downloadApp(libraryItem.gameId) + val gameId = libraryItem.gameId + when { + // Switching platform on an already-installed game: drop the old platform's files + // and fetch the newly selected one. Not installed yet -> just remember the choice, + // the Install/Play button will pick it up when the user actually installs. + platformChanged && SteamService.isAppInstalled(gameId) -> { + CoroutineScope(Dispatchers.IO).launch { + SnackbarManager.show(context.getString(R.string.container_platform_switch_reinstalling)) + SteamService.deleteApp(gameId) + DownloadService.invalidateCache() + SteamService.downloadApp(gameId) + } + } + languageChanged -> { + CoroutineScope(Dispatchers.IO).launch { + SteamService.downloadApp(gameId) + } } } } override fun supportsContainerConfig(): Boolean = true + override fun hasAndroidVersion(libraryItem: LibraryItem): Boolean { + return SteamService.getAppInfoOf(libraryItem.gameId)?.depots?.values?.any { it.isAndroidCompatible } == true + } + + override fun getAndroidPackageName(context: Context, libraryItem: LibraryItem): String? { + val gameId = libraryItem.gameId + if (!SteamService.isAndroidPlatform(gameId)) return null + return AndroidGameLauncher.resolvePackageName(context, gameId) + } + override fun getExportFileExtension(): String = ".steam" @Composable @@ -1008,7 +1042,7 @@ class SteamAppScreen : BaseAppScreen() { } } - LaunchedEffect(gameId, hasStoragePermission) { + LaunchedEffect(gameId, hasStoragePermission, installDialogState.visible) { if (!hasStoragePermission) { installSizeInfo = null return@LaunchedEffect @@ -1017,7 +1051,8 @@ class SteamAppScreen : BaseAppScreen() { val info = withContext(Dispatchers.IO) { val container = ContainerManager(context).getContainerById("STEAM_$gameId") val language = container?.language ?: PrefManager.containerLanguage - val depots = SteamService.getDownloadableDepots(gameId, language) + val wantAndroid = container?.platform.equals(Container.PLATFORM_ANDROID, ignoreCase = true) + val depots = SteamService.getDownloadableDepots(gameId, language, wantAndroid) Timber.i("There are ${depots.size} depots belonging to ${libraryItem.appId}") val branch = SteamService.getInstalledApp(gameId)?.branch ?: "public" val availableBytes = StorageUtils.getAvailableSpaceForUncreatedPath(SteamService.getAppDirPath(gameId)) @@ -1262,6 +1297,16 @@ class SteamAppScreen : BaseAppScreen() { val installedAppInfo = getInstalledApp(libraryItem.gameId) val gameRootDir = getInstallPath(context, libraryItem)?.let(::File) + // The installed Android app (Steam Frame / Lepton games) is a + // separate system entity from GameNative's own downloaded copy — + // prompt its uninstall before we delete the .apk we need to + // resolve the package name from. Checked by looking for an actual + // .apk on disk rather than the container's current platform + // setting, which may have since been switched back. + withContext(Dispatchers.Main) { + AndroidGameLauncher.requestUninstall(context, gameId) + } + val success = SteamService.deleteApp(gameId) DownloadService.invalidateCache() if (success) { diff --git a/app/src/main/java/app/gamenative/utils/AndroidGameLauncher.kt b/app/src/main/java/app/gamenative/utils/AndroidGameLauncher.kt new file mode 100644 index 0000000000..d71ba11bb3 --- /dev/null +++ b/app/src/main/java/app/gamenative/utils/AndroidGameLauncher.kt @@ -0,0 +1,141 @@ +package app.gamenative.utils + +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.net.Uri +import android.os.Environment +import app.gamenative.service.SteamService +import timber.log.Timber +import java.io.File + +/** + * Installs/launches the native Android build of a game (Steam Frame / Lepton depot), + * bypassing the Wine/Winlator container entirely. + */ +object AndroidGameLauncher { + + private const val TAG = "AndroidGameLauncher" + + private fun findApkFile(gameId: Int): File? { + val dir = File(SteamService.getAppDirPath(gameId)) + if (!dir.exists()) return null + return dir.walkTopDown().firstOrNull { it.isFile && it.extension.equals("apk", ignoreCase = true) } + } + + private fun findObbFiles(gameId: Int): List { + val dir = File(SteamService.getAppDirPath(gameId)) + if (!dir.exists()) return emptyList() + return dir.walkTopDown().filter { it.isFile && it.extension.equals("obb", ignoreCase = true) }.toList() + } + + private fun getApkPackageName(context: Context, apk: File): String? { + return try { + context.packageManager.getPackageArchiveInfo(apk.absolutePath, 0)?.packageName + } catch (e: Exception) { + Timber.tag(TAG).e(e, "Could not read package info from ${apk.absolutePath}") + null + } + } + + private fun isPackageInstalled(context: Context, packageName: String): Boolean { + return try { + context.packageManager.getPackageInfo(packageName, 0) + true + } catch (e: PackageManager.NameNotFoundException) { + false + } + } + + /** + * Best-effort OBB placement under /Android/obb//. On Android 11+ this requires + * broad ("All files access") storage permission GameNative doesn't currently request, so + * this silently no-ops there — games that need their OBB will fail to start until that's added. + */ + private fun copyObbFiles(gameId: Int, packageName: String) { + val obbFiles = findObbFiles(gameId) + if (obbFiles.isEmpty()) return + try { + val obbRoot = File(Environment.getExternalStorageDirectory(), "Android/obb/$packageName") + obbRoot.mkdirs() + obbFiles.forEach { src -> src.copyTo(File(obbRoot, src.name), overwrite = true) } + Timber.tag(TAG).i("Copied ${obbFiles.size} OBB file(s) to ${obbRoot.absolutePath}") + } catch (e: Exception) { + Timber.tag(TAG).w(e, "Could not place OBB files for $packageName (needs broader storage access on Android 11+)") + } + } + + /** Package name of this game's downloaded .apk, or null if none is on disk / unreadable. */ + fun resolvePackageName(context: Context, gameId: Int): String? { + val apk = findApkFile(gameId) ?: return null + return getApkPackageName(context, apk) + } + + /** Whether the Android app for this game is actually installed on the system (not just downloaded). */ + fun isGameInstalled(context: Context, gameId: Int): Boolean { + val packageName = resolvePackageName(context, gameId) ?: return false + return isPackageInstalled(context, packageName) + } + + sealed class Result { + data object Launched : Result() + data object InstallStarted : Result() + data object Failed : Result() + } + + /** + * If the downloaded APK is already installed, launches it directly. Otherwise kicks off + * the system install flow (user confirmation required) — the caller must ask the user to + * press Play again once that finishes; there is no reliable synchronous "wait for install". + */ + fun installAndLaunch(context: Context, gameId: Int): Result { + val apk = findApkFile(gameId) + if (apk == null) { + Timber.tag(TAG).e("No APK found on disk for game $gameId") + return Result.Failed + } + val packageName = getApkPackageName(context, apk) + if (packageName == null) { + Timber.tag(TAG).e("Could not resolve package name from ${apk.absolutePath}") + return Result.Failed + } + if (!isPackageInstalled(context, packageName)) { + copyObbFiles(gameId, packageName) + UpdateInstaller.installApk(context, apk) + return Result.InstallStarted + } + val intent = context.packageManager.getLaunchIntentForPackage(packageName) + ?: return Result.Failed + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + context.startActivity(intent) + return Result.Launched + } + + /** + * Prompts the system uninstall dialog for the Android package matching this game, if one + * is actually installed. Must be called before the downloaded .apk is deleted from disk + * (needed to resolve the package name). This is a separate step from GameNative forgetting + * the game — the installed app is a distinct entity on the system that Android requires + * explicit user confirmation to remove. + */ + fun requestUninstall(context: Context, gameId: Int) { + val apk = findApkFile(gameId) + if (apk == null) { + Timber.tag(TAG).w("requestUninstall: no APK found on disk for game $gameId, skipping system uninstall") + return + } + val packageName = getApkPackageName(context, apk) + if (packageName == null) { + Timber.tag(TAG).w("requestUninstall: could not resolve package name from ${apk.absolutePath}") + return + } + if (!isPackageInstalled(context, packageName)) { + Timber.tag(TAG).w("requestUninstall: package $packageName is not currently installed, skipping") + return + } + Timber.tag(TAG).i("requestUninstall: prompting system uninstall for $packageName") + val intent = Intent(Intent.ACTION_DELETE, Uri.parse("package:$packageName")) + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + context.startActivity(intent) + } +} diff --git a/app/src/main/java/app/gamenative/utils/ContainerUtils.kt b/app/src/main/java/app/gamenative/utils/ContainerUtils.kt index 476fdbb062..7cf53904fe 100644 --- a/app/src/main/java/app/gamenative/utils/ContainerUtils.kt +++ b/app/src/main/java/app/gamenative/utils/ContainerUtils.kt @@ -328,6 +328,7 @@ object ContainerUtils { box64Preset = container.box64Preset, desktopTheme = container.desktopTheme, containerVariant = container.containerVariant, + platform = container.platform, wineVersion = container.wineVersion, emulator = container.emulator, fexcoreVersion = container.fexCoreVersion, @@ -521,6 +522,7 @@ object ContainerUtils { container.desktopTheme = containerData.desktopTheme container.graphicsDriverVersion = containerData.graphicsDriverVersion container.containerVariant = containerData.containerVariant + container.platform = containerData.platform container.wineVersion = containerData.wineVersion container.emulator = containerData.emulator container.fexCoreVersion = containerData.fexcoreVersion diff --git a/app/src/main/java/app/gamenative/utils/UpdateInstaller.kt b/app/src/main/java/app/gamenative/utils/UpdateInstaller.kt index 7b06e222fe..70277f8602 100644 --- a/app/src/main/java/app/gamenative/utils/UpdateInstaller.kt +++ b/app/src/main/java/app/gamenative/utils/UpdateInstaller.kt @@ -64,7 +64,7 @@ object UpdateInstaller { } } - private fun installApk(context: Context, apkFile: File) { + fun installApk(context: Context, apkFile: File) { try { // Verify file exists before attempting installation if (!apkFile.exists()) { diff --git a/app/src/main/java/com/winlator/container/Container.java b/app/src/main/java/com/winlator/container/Container.java index 86d3b1778f..d49a22ce7b 100644 --- a/app/src/main/java/com/winlator/container/Container.java +++ b/app/src/main/java/com/winlator/container/Container.java @@ -72,6 +72,10 @@ public enum XrControllerMapping { public static final String STEAM_TYPE_ULTRALIGHT = "ultralight"; public static final String GLIBC = "glibc"; public static final String BIONIC = "bionic"; + + // Which Steam depot platform to download/install for this game. + public static final String PLATFORM_WINDOWS = "windows"; + public static final String PLATFORM_ANDROID = "android"; public static final byte MAX_DRIVE_LETTERS = 8; public final String id; private String name; @@ -168,6 +172,8 @@ public enum XrControllerMapping { private String containerVariant = DEFAULT_VARIANT; + private String platform = PLATFORM_WINDOWS; + public String getGraphicsDriverVersion() { return graphicsDriverVersion; } @@ -512,6 +518,14 @@ public String getContainerVariant() { return this.containerVariant; } + public void setPlatform(String platform) { + this.platform = platform != null && !platform.isEmpty() ? platform : PLATFORM_WINDOWS; + } + + public String getPlatform() { + return this.platform; + } + public String getExtra(String name) { return getExtra(name, ""); } @@ -739,6 +753,7 @@ public void saveData() { data.put("steamType", steamType); data.put("language", language); data.put("containerVariant", containerVariant); + data.put("platform", platform); data.put("emulator", emulator); data.put("fexcoreVersion", fexcoreVersion); @@ -846,6 +861,9 @@ public void loadData(JSONObject data) throws JSONException { case "containerVariant" : setContainerVariant(data.getString(key)); break; + case "platform" : + setPlatform(data.getString(key)); + break; case "inputType" : setInputType(data.getInt(key)); break; diff --git a/app/src/main/java/com/winlator/container/ContainerData.kt b/app/src/main/java/com/winlator/container/ContainerData.kt index 9c8e339c09..e689b21e96 100644 --- a/app/src/main/java/com/winlator/container/ContainerData.kt +++ b/app/src/main/java/com/winlator/container/ContainerData.kt @@ -44,6 +44,8 @@ data class ContainerData( val desktopTheme: String = WineThemeManager.DEFAULT_DESKTOP_THEME, // container runtime variant (glibc or bionic) val containerVariant: String = Container.DEFAULT_VARIANT, + // which Steam depot platform to download/install (windows or android) + val platform: String = Container.PLATFORM_WINDOWS, // wine version identifier (used for bionic variant), defaults to main wine val wineVersion: String = WineInfo.MAIN_WINE_VERSION.identifier(), // selected 32-bit emulator for WoW64 processes (FEXCore/Box64) @@ -144,6 +146,7 @@ data class ContainerData( "box64Preset" to state.box64Preset, "desktopTheme" to state.desktopTheme, "containerVariant" to state.containerVariant, + "platform" to state.platform, "wineVersion" to state.wineVersion, "emulator" to state.emulator, "fexcoreVersion" to state.fexcoreVersion, @@ -214,6 +217,7 @@ data class ContainerData( box64Preset = savedMap["box64Preset"] as String, desktopTheme = savedMap["desktopTheme"] as String, containerVariant = (savedMap["containerVariant"] as? String) ?: Container.DEFAULT_VARIANT, + platform = (savedMap["platform"] as? String) ?: Container.PLATFORM_WINDOWS, wineVersion = (savedMap["wineVersion"] as? String) ?: WineInfo.MAIN_WINE_VERSION.identifier(), emulator = (savedMap["emulator"] as? String) ?: Container.DEFAULT_EMULATOR, fexcoreVersion = (savedMap["fexcoreVersion"] as? String) ?: DefaultVersion.FEXCORE, diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 159a162b9c..6920e18c5a 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -646,6 +646,13 @@ Valeur Plus de variables connues Variante du conteneur + Version + Choisissez entre télécharger la version Windows (via Wine) ou la version Android native, lorsque le jeu la propose. + Normal (Windows) + Android + Changement de version — suppression de l\'installation actuelle et récupération de la nouvelle… + Installation de la version Android — appuyez de nouveau sur Jouer une fois l\'installation terminée. + Impossible d\'installer ou de lancer la version Android de ce jeu. Version Wine Arguments d\'exécution Exemple : -dx11 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2305d3b723..0d460a4514 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -767,6 +767,13 @@ Value No more known variables Container Variant + Version + Choose whether to download the Windows version (runs via Wine) or the native Android version, when the game offers one. + Normal (Windows) + Android + Switching version — removing the current install and fetching the new one… + Installing the Android version — press Play again once it\'s installed. + Couldn\'t install or launch the Android version of this game. Wine Version Exec Arguments Example: -dx11 diff --git a/app/src/main/res/xml/file_provider_paths.xml b/app/src/main/res/xml/file_provider_paths.xml index 90f2ff36a3..efd6879abf 100644 --- a/app/src/main/res/xml/file_provider_paths.xml +++ b/app/src/main/res/xml/file_provider_paths.xml @@ -3,5 +3,9 @@ + + diff --git a/app/src/modern/AndroidManifest.xml b/app/src/modern/AndroidManifest.xml index 8a762a288f..35a5e688ab 100644 --- a/app/src/modern/AndroidManifest.xml +++ b/app/src/modern/AndroidManifest.xml @@ -3,5 +3,6 @@ xmlns:tools="http://schemas.android.com/tools"> + diff --git a/app/src/modernXr/AndroidManifest.xml b/app/src/modernXr/AndroidManifest.xml index 279d940ef9..afa30249d0 100644 --- a/app/src/modernXr/AndroidManifest.xml +++ b/app/src/modernXr/AndroidManifest.xml @@ -3,6 +3,7 @@ xmlns:tools="http://schemas.android.com/tools"> + From 8d3424d1692790798c27727b9d22343d3394f75a Mon Sep 17 00:00:00 2001 From: moi952 <52632400+moi952@users.noreply.github.com> Date: Sun, 19 Jul 2026 18:32:29 +0200 Subject: [PATCH 2/7] fix: address CodeRabbit review comments --- .../main/java/app/gamenative/service/SteamService.kt | 3 ++- .../ui/component/dialog/ContainerConfigDialog.kt | 4 ++-- .../ui/screen/library/appscreen/SteamAppScreen.kt | 10 ++++++++++ app/src/main/res/xml/file_provider_paths.xml | 12 +++++++++--- 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/app/gamenative/service/SteamService.kt b/app/src/main/java/app/gamenative/service/SteamService.kt index 3143e2ea98..fae8d6e4e9 100644 --- a/app/src/main/java/app/gamenative/service/SteamService.kt +++ b/app/src/main/java/app/gamenative/service/SteamService.kt @@ -1421,7 +1421,8 @@ class SteamService : Service(), IChallengeUrlChanged { /** Whether the container for [appId] has the game's Android (Steam Frame / Lepton) depot selected. */ fun isAndroidPlatform(appId: Int): Boolean { - val container = ContainerManager(instance!!.applicationContext).getContainerById("STEAM_${appId}") + val context = instance?.applicationContext ?: return false + val container = ContainerManager(context).getContainerById("STEAM_${appId}") return container?.platform.equals(Container.PLATFORM_ANDROID, ignoreCase = true) } diff --git a/app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt b/app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt index 57fb3052c1..bf13bf817c 100644 --- a/app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt +++ b/app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt @@ -1290,11 +1290,11 @@ fun ContainerConfigDialog( if (event.type != KeyEventType.KeyDown) return@onPreviewKeyEvent false when (event.key) { Key.ButtonR1, Key.ButtonR2 -> { - selectedTab = (selectedTab + 1) % tabs.size + if (!isAndroidPlatform) selectedTab = (selectedTab + 1) % tabs.size true } Key.ButtonL1, Key.ButtonL2 -> { - selectedTab = (selectedTab - 1 + tabs.size) % tabs.size + if (!isAndroidPlatform) selectedTab = (selectedTab - 1 + tabs.size) % tabs.size true } else -> false diff --git a/app/src/main/java/app/gamenative/ui/screen/library/appscreen/SteamAppScreen.kt b/app/src/main/java/app/gamenative/ui/screen/library/appscreen/SteamAppScreen.kt index 66285b1bb4..508fc03e9b 100644 --- a/app/src/main/java/app/gamenative/ui/screen/library/appscreen/SteamAppScreen.kt +++ b/app/src/main/java/app/gamenative/ui/screen/library/appscreen/SteamAppScreen.kt @@ -872,6 +872,7 @@ class SteamAppScreen : BaseAppScreen() { override fun saveContainerConfig(context: Context, libraryItem: LibraryItem, config: ContainerData) { val container = getContainer(context, libraryItem.appId) val platformChanged = !container.platform.equals(config.platform, ignoreCase = true) + val wasAndroid = container.platform.equals(Container.PLATFORM_ANDROID, ignoreCase = true) val languageChanged = container.language != config.language ContainerUtils.applyToContainer(context, libraryItem.appId, config) @@ -883,6 +884,15 @@ class SteamAppScreen : BaseAppScreen() { platformChanged && SteamService.isAppInstalled(gameId) -> { CoroutineScope(Dispatchers.IO).launch { SnackbarManager.show(context.getString(R.string.container_platform_switch_reinstalling)) + // Resolve and prompt removal of the installed Android app BEFORE its .apk + // is deleted below — that's the only place the package name can still be + // read from, and once the platform is switched away there's no other path + // back to it. + if (wasAndroid) { + withContext(Dispatchers.Main) { + AndroidGameLauncher.requestUninstall(context, gameId) + } + } SteamService.deleteApp(gameId) DownloadService.invalidateCache() SteamService.downloadApp(gameId) diff --git a/app/src/main/res/xml/file_provider_paths.xml b/app/src/main/res/xml/file_provider_paths.xml index efd6879abf..a1ec971da2 100644 --- a/app/src/main/res/xml/file_provider_paths.xml +++ b/app/src/main/res/xml/file_provider_paths.xml @@ -3,9 +3,15 @@ - + + + From ee35f6ce2a9f367dbd8142d7528d84cce677960e Mon Sep 17 00:00:00 2001 From: moi952 <52632400+moi952@users.noreply.github.com> Date: Sun, 19 Jul 2026 19:33:04 +0200 Subject: [PATCH 3/7] fix: address automated review findings for Android game builds (edge cases, staleness bugs, uninstall freeze) --- .../app/gamenative/service/SteamService.kt | 6 +- .../main/java/app/gamenative/ui/PluviaMain.kt | 14 +++ .../component/dialog/ContainerConfigDialog.kt | 8 +- .../ui/component/dialog/GeneralTab.kt | 2 + .../app/gamenative/ui/model/MainViewModel.kt | 31 +++--- .../screen/library/appscreen/BaseAppScreen.kt | 5 +- .../library/appscreen/SteamAppScreen.kt | 76 +++++++++++---- .../gamenative/utils/AndroidGameLauncher.kt | 96 +++++++++++++++---- .../java/app/gamenative/utils/SteamUtils.kt | 4 +- .../app/gamenative/utils/UpdateInstaller.kt | 9 +- .../winlator/container/ContainerManager.java | 1 + app/src/main/res/values-fr/strings.xml | 5 +- app/src/main/res/values/strings.xml | 3 +- app/src/main/res/xml/file_provider_paths.xml | 13 +-- .../test/java/app/gamenative/enums/OSTest.kt | 58 +++++++++++ 15 files changed, 256 insertions(+), 75 deletions(-) create mode 100644 app/src/test/java/app/gamenative/enums/OSTest.kt diff --git a/app/src/main/java/app/gamenative/service/SteamService.kt b/app/src/main/java/app/gamenative/service/SteamService.kt index fae8d6e4e9..af631daa46 100644 --- a/app/src/main/java/app/gamenative/service/SteamService.kt +++ b/app/src/main/java/app/gamenative/service/SteamService.kt @@ -948,7 +948,7 @@ class SteamService : Service(), IChallengeUrlChanged { ): Map { val dlcAppIdsWithSingleDepots = getDlcAppIdsWithSingleDepot(depots) val effectiveLanguage = SteamUtils.effectiveDepotLanguage( - depots, preferredLanguage, ownedDlc, licensedDepotIds, hasSteamUnlockedBranch, + depots, preferredLanguage, ownedDlc, licensedDepotIds, hasSteamUnlockedBranch, wantAndroid, ) val eligible = eligibleDepots(depots, effectiveLanguage, ownedDlc, licensedDepotIds, wantAndroid) val has64Bit = eligible.any { it.osArch == OSArch.Arch64 } @@ -1025,7 +1025,7 @@ class SteamService : Service(), IChallengeUrlChanged { // parent app's arch applies to DLC arch selection val mainLanguage = SteamUtils.effectiveDepotLanguage( - appInfo.depots, preferredLanguage, ownedDlc, licensedDepots, hasSteamUnlockedBranch, + appInfo.depots, preferredLanguage, ownedDlc, licensedDepots, hasSteamUnlockedBranch, wantAndroid, ) val has64Bit = eligibleDepots(appInfo.depots, mainLanguage, ownedDlc, licensedDepots, wantAndroid) .any { it.osArch == OSArch.Arch64 } @@ -1036,7 +1036,7 @@ class SteamService : Service(), IChallengeUrlChanged { val dlcLicensedDepots = getLicensedDepotIds(dlcApp.id) // Resolve the DLC's own language too, so DLC that omits the container language installs. val dlcLanguage = SteamUtils.effectiveDepotLanguage( - dlcApp.depots, preferredLanguage, null, dlcLicensedDepots, hasSteamUnlockedBranch, + dlcApp.depots, preferredLanguage, null, dlcLicensedDepots, hasSteamUnlockedBranch, wantAndroid, ) val dlcEligible = eligibleDepots(dlcApp.depots, dlcLanguage, null, dlcLicensedDepots, wantAndroid) val dlcHasNonDeckWin = dlcEligible.any { !it.steamDeck && (if (wantAndroid) it.isAndroidCompatible else it.isWindowsCompatible) } diff --git a/app/src/main/java/app/gamenative/ui/PluviaMain.kt b/app/src/main/java/app/gamenative/ui/PluviaMain.kt index 9b59354b12..993d512b8b 100644 --- a/app/src/main/java/app/gamenative/ui/PluviaMain.kt +++ b/app/src/main/java/app/gamenative/ui/PluviaMain.kt @@ -1614,6 +1614,20 @@ fun preLaunchApp( // create container if it does not already exist // TODO: combine somehow with container creation in HomeLibraryAppScreen val containerManager = ContainerManager(context) + + // If a container already exists and is set to the Android platform, short-circuit before + // any Wine-specific setup below. getOrCreateContainer() can extract a Wine container + // pattern when creating a brand-new container, which a native Android launch neither + // needs nor should be able to fail because of. + if (containerManager.hasContainer(appId)) { + val existing = containerManager.getContainerById(appId) + if (existing?.platform.equals(Container.PLATFORM_ANDROID, ignoreCase = true)) { + setLoadingDialogVisible(false) + onSuccess(context, appId) + return@launch + } + } + val container = if (useTemporaryOverride) { ContainerUtils.getOrCreateContainerWithOverride(context, appId) } else { diff --git a/app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt b/app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt index bf13bf817c..56f253d446 100644 --- a/app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt +++ b/app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt @@ -1408,6 +1408,7 @@ internal fun ExecutablePathDropdown( value: String, onValueChange: (String) -> Unit, containerData: ContainerData, + enabled: Boolean = true, ) { var expanded by remember { mutableStateOf(false) } var executables by remember { mutableStateOf>(emptyList()) } @@ -1424,14 +1425,15 @@ internal fun ExecutablePathDropdown( } ExposedDropdownMenuBox( - expanded = expanded, - onExpandedChange = { expanded = it }, + expanded = expanded && enabled, + onExpandedChange = { if (enabled) expanded = it }, modifier = modifier ) { NoExtractOutlinedTextField( value = value, onValueChange = onValueChange, readOnly = true, + enabled = enabled, label = { Text(stringResource(R.string.container_config_executable_path)) }, placeholder = { Text(stringResource(R.string.container_config_executable_path_placeholder)) }, trailingIcon = { @@ -1448,7 +1450,7 @@ internal fun ExecutablePathDropdown( // so the anchor never opens via controller. Intercept it here and toggle // the menu. (Up/down focus-escape is handled in NoExtractOutlinedTextField.) .onPreviewKeyEvent { event -> - if (event.type != KeyEventType.KeyDown) return@onPreviewKeyEvent false + if (event.type != KeyEventType.KeyDown || !enabled) return@onPreviewKeyEvent false when (event.key) { Key.DirectionCenter, Key.Enter, Key.NumPadEnter, Key.Spacebar, Key.ButtonA -> { expanded = !expanded diff --git a/app/src/main/java/app/gamenative/ui/component/dialog/GeneralTab.kt b/app/src/main/java/app/gamenative/ui/component/dialog/GeneralTab.kt index abbd8dc0ae..0981d8af87 100644 --- a/app/src/main/java/app/gamenative/ui/component/dialog/GeneralTab.kt +++ b/app/src/main/java/app/gamenative/ui/component/dialog/GeneralTab.kt @@ -283,11 +283,13 @@ fun GeneralTabContent( value = config.executablePath, onValueChange = { state.config.value = config.copy(executablePath = it) }, containerData = config, + enabled = !state.isAndroidPlatform, ) NoExtractOutlinedTextField( modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 8.dp), value = config.execArgs, onValueChange = { state.config.value = config.copy(execArgs = it) }, + enabled = !state.isAndroidPlatform, label = { Text(text = stringResource(R.string.exec_arguments)) }, placeholder = { Text(text = stringResource(R.string.exec_arguments_example)) }, singleLine = true, diff --git a/app/src/main/java/app/gamenative/ui/model/MainViewModel.kt b/app/src/main/java/app/gamenative/ui/model/MainViewModel.kt index 75d28c4070..a70852c2e5 100644 --- a/app/src/main/java/app/gamenative/ui/model/MainViewModel.kt +++ b/app/src/main/java/app/gamenative/ui/model/MainViewModel.kt @@ -467,14 +467,24 @@ class MainViewModel @Inject constructor( fun launchApp(context: Context, appId: String) { viewModelScope.launch { - val container = withContext(Dispatchers.IO) { ContainerUtils.getOrCreateContainer(context, appId) } + // Shared by both the Android and Wine paths below. + viewModelScope.launch(Dispatchers.IO) { + libraryPlayHistoryDao.upsert( + LibraryPlayHistory( + appId = appId, + lastPlayed = System.currentTimeMillis(), + ), + ) + } + + // getOrCreateContainerWithOverride also picks up a temporary per-launch config + // (e.g. from an external Intent launch) — falls back to the persisted container + // untouched when there's no override, so this is safe unconditionally. + val container = withContext(Dispatchers.IO) { + ContainerUtils.getOrCreateContainerWithOverride(context, appId) + } if (container.platform.equals(Container.PLATFORM_ANDROID, ignoreCase = true)) { // Native Android (Steam Frame / Lepton) build: no Wine container involved at all. - withContext(Dispatchers.IO) { - libraryPlayHistoryDao.upsert( - LibraryPlayHistory(appId = appId, lastPlayed = System.currentTimeMillis()), - ) - } val gameId = ContainerUtils.extractGameIdFromContainerId(appId) when (withContext(Dispatchers.IO) { AndroidGameLauncher.installAndLaunch(context, gameId) }) { AndroidGameLauncher.Result.InstallStarted -> @@ -487,15 +497,6 @@ class MainViewModel @Inject constructor( } // Show booting splash before launching the app - viewModelScope.launch(Dispatchers.IO) { - libraryPlayHistoryDao.upsert( - LibraryPlayHistory( - appId = appId, - lastPlayed = System.currentTimeMillis(), - ), - ) - } - setShowBootingSplash(true) PluviaApp.events.emit(AndroidEvent.SetAllowedOrientation(PrefManager.allowedOrientation)) diff --git a/app/src/main/java/app/gamenative/ui/screen/library/appscreen/BaseAppScreen.kt b/app/src/main/java/app/gamenative/ui/screen/library/appscreen/BaseAppScreen.kt index a9217dce9e..2687a308eb 100644 --- a/app/src/main/java/app/gamenative/ui/screen/library/appscreen/BaseAppScreen.kt +++ b/app/src/main/java/app/gamenative/ui/screen/library/appscreen/BaseAppScreen.kt @@ -1079,7 +1079,10 @@ abstract class BaseAppScreen { // Android platform games: the actual install happens in the system's own installer UI, // outside our control, so listen for its completion broadcast instead of requiring the // user to leave and come back to this screen for the Play button to appear. - DisposableEffect(libraryItem.appId) { + // Keyed on download/install state too: getAndroidPackageName() reads the APK off disk, + // which is null until the download finishes, so the effect needs to re-run once that + // changes rather than being stuck with the null result from the initial composition. + DisposableEffect(libraryItem.appId, isDownloadingState, isInstalledState) { val androidPackageName = getAndroidPackageName(context, libraryItem) if (androidPackageName == null) { onDispose { } diff --git a/app/src/main/java/app/gamenative/ui/screen/library/appscreen/SteamAppScreen.kt b/app/src/main/java/app/gamenative/ui/screen/library/appscreen/SteamAppScreen.kt index 508fc03e9b..6ad62db869 100644 --- a/app/src/main/java/app/gamenative/ui/screen/library/appscreen/SteamAppScreen.kt +++ b/app/src/main/java/app/gamenative/ui/screen/library/appscreen/SteamAppScreen.kt @@ -227,8 +227,20 @@ class SteamAppScreen : BaseAppScreen() { return pendingUpdateVerifyOperations[gameId] } - // Shared state for deletion progress dialog - var showDeletingDialog by mutableStateOf(false) + // Shared state for deletion progress dialog - map of gameId to visibility + private val deletingDialogVisible = mutableStateMapOf() + + fun showDeletingDialog(gameId: Int) { + deletingDialogVisible[gameId] = true + } + + fun hideDeletingDialog(gameId: Int) { + deletingDialogVisible.remove(gameId) + } + + fun isDeletingDialogVisible(gameId: Int): Boolean { + return deletingDialogVisible[gameId] == true + } } @Composable @@ -252,11 +264,16 @@ class SteamAppScreen : BaseAppScreen() { var isInstalled by remember(libraryItem.appId) { mutableStateOf(SteamService.isAppInstalled(gameId)) } + // Bumped on every LibraryInstallStatusChanged, even when isInstalled itself doesn't + // change (e.g. platform switched on a not-yet-installed game) — sizeFromStore below + // depends on the container's platform, not just on install state. + var refreshTrigger by remember(libraryItem.appId) { mutableIntStateOf(0) } DisposableEffect(gameId) { val listener: (AndroidEvent.LibraryInstallStatusChanged) -> Unit = { event -> if (event.appId == gameId) { isInstalled = SteamService.isAppInstalled(gameId) + refreshTrigger++ } } PluviaApp.events.on(listener) @@ -290,7 +307,7 @@ class SteamAppScreen : BaseAppScreen() { // Get size on disk (async, will update via state) var sizeOnDisk by remember { mutableStateOf(null) } - LaunchedEffect(isInstalled, gameId) { + LaunchedEffect(isInstalled, gameId, refreshTrigger) { if (isInstalled) { DownloadService.getSizeOnDiskDisplay(gameId) { sizeOnDisk = it @@ -302,7 +319,7 @@ class SteamAppScreen : BaseAppScreen() { // Get size from store (async, will update via state) var sizeFromStore by remember { mutableStateOf(null) } - LaunchedEffect(isInstalled, gameId) { + LaunchedEffect(isInstalled, gameId, refreshTrigger) { if (!isInstalled) { // Load size from store on IO, assign on Main to respect Compose threading val size = withContext(Dispatchers.IO) { @@ -884,14 +901,19 @@ class SteamAppScreen : BaseAppScreen() { platformChanged && SteamService.isAppInstalled(gameId) -> { CoroutineScope(Dispatchers.IO).launch { SnackbarManager.show(context.getString(R.string.container_platform_switch_reinstalling)) - // Resolve and prompt removal of the installed Android app BEFORE its .apk - // is deleted below — that's the only place the package name can still be - // read from, and once the platform is switched away there's no other path - // back to it. + // Resolve and prompt removal of the installed Android app BEFORE its .apk is + // deleted below — that's the only place the package name can still be read + // from, and once the platform is switched away there's no other path back to + // it. Wait for confirmation: if the user cancels, leave the files/app alone + // rather than orphaning a still-installed app. if (wasAndroid) { - withContext(Dispatchers.Main) { + val canDeleteFiles = withContext(Dispatchers.Main) { AndroidGameLauncher.requestUninstall(context, gameId) } + if (!canDeleteFiles) { + SnackbarManager.show(context.getString(R.string.android_game_uninstall_cancelled)) + return@launch + } } SteamService.deleteApp(gameId) DownloadService.invalidateCache() @@ -903,12 +925,22 @@ class SteamAppScreen : BaseAppScreen() { SteamService.downloadApp(gameId) } } + platformChanged -> { + // Not installed yet, nothing to redownload — but the store-size display on the + // game page reads the container's platform and needs a nudge to recompute now + // that it changed. + PluviaApp.events.emit(AndroidEvent.LibraryInstallStatusChanged(gameId, GameSource.STEAM)) + } } } override fun supportsContainerConfig(): Boolean = true override fun hasAndroidVersion(libraryItem: LibraryItem): Boolean { + // Modern/ModernXr strip the install/uninstall-package permissions (Horizon Store + // compliance), so they can't actually install a native Android build — keep the + // selector Legacy-only rather than offering a choice that can't work there. + if (BuildConfig.MODERN_ANDROID) return false return SteamService.getAppInfoOf(libraryItem.gameId)?.depots?.values?.any { it.isAndroidCompatible } == true } @@ -1173,7 +1205,7 @@ class SteamAppScreen : BaseAppScreen() { downloadInfo?.cancel() SteamService.workshopPausedApps.remove(gameId) hideInstallDialog(gameId) - showDeletingDialog = true + showDeletingDialog(gameId) CoroutineScope(Dispatchers.IO).launch { try { SteamService.deleteApp(gameId) @@ -1181,7 +1213,7 @@ class SteamAppScreen : BaseAppScreen() { PluviaApp.events.emit(AndroidEvent.LibraryInstallStatusChanged(gameId, GameSource.STEAM)) } finally { withContext(NonCancellable + Dispatchers.Main) { - showDeletingDialog = false + hideDeletingDialog(gameId) } } } @@ -1300,7 +1332,7 @@ class SteamAppScreen : BaseAppScreen() { TextButton( onClick = { hideUninstallDialog(libraryItem.appId) - showDeletingDialog = true + showDeletingDialog(gameId) CoroutineScope(Dispatchers.IO).launch { try { @@ -1309,13 +1341,19 @@ class SteamAppScreen : BaseAppScreen() { // The installed Android app (Steam Frame / Lepton games) is a // separate system entity from GameNative's own downloaded copy — - // prompt its uninstall before we delete the .apk we need to - // resolve the package name from. Checked by looking for an actual - // .apk on disk rather than the container's current platform - // setting, which may have since been switched back. - withContext(Dispatchers.Main) { + // prompt its uninstall and wait for confirmation before we delete + // the .apk we need to resolve the package name from. Checked by + // looking for an actual .apk on disk rather than the container's + // current platform setting, which may have since been switched back. + val canDeleteFiles = withContext(Dispatchers.Main) { AndroidGameLauncher.requestUninstall(context, gameId) } + if (!canDeleteFiles) { + withContext(Dispatchers.Main) { + SnackbarManager.show(context.getString(R.string.android_game_uninstall_cancelled)) + } + return@launch + } val success = SteamService.deleteApp(gameId) DownloadService.invalidateCache() @@ -1351,7 +1389,7 @@ class SteamAppScreen : BaseAppScreen() { } } finally { withContext(NonCancellable + Dispatchers.Main) { - showDeletingDialog = false + hideDeletingDialog(gameId) } } } @@ -1380,7 +1418,7 @@ class SteamAppScreen : BaseAppScreen() { } // Deletion progress dialog - if (showDeletingDialog) { + if (isDeletingDialogVisible(gameId)) { LoadingDialog( visible = true, progress = -1f, diff --git a/app/src/main/java/app/gamenative/utils/AndroidGameLauncher.kt b/app/src/main/java/app/gamenative/utils/AndroidGameLauncher.kt index d71ba11bb3..01ccdd1357 100644 --- a/app/src/main/java/app/gamenative/utils/AndroidGameLauncher.kt +++ b/app/src/main/java/app/gamenative/utils/AndroidGameLauncher.kt @@ -1,13 +1,18 @@ package app.gamenative.utils +import android.app.Activity import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.net.Uri import android.os.Environment +import androidx.activity.ComponentActivity +import androidx.activity.result.contract.ActivityResultContracts import app.gamenative.service.SteamService +import kotlinx.coroutines.suspendCancellableCoroutine import timber.log.Timber import java.io.File +import kotlin.coroutines.resume /** * Installs/launches the native Android build of a game (Steam Frame / Lepton depot), @@ -17,16 +22,22 @@ object AndroidGameLauncher { private const val TAG = "AndroidGameLauncher" + // Depot content for an Android build is expected to be shallow (apk + an obb/ folder at + // most); bound the walk so a huge, deeply-nested Workshop/DLC tree can't make this slow. + private const val MAX_SEARCH_DEPTH = 6 + private fun findApkFile(gameId: Int): File? { val dir = File(SteamService.getAppDirPath(gameId)) if (!dir.exists()) return null - return dir.walkTopDown().firstOrNull { it.isFile && it.extension.equals("apk", ignoreCase = true) } + return dir.walkTopDown().maxDepth(MAX_SEARCH_DEPTH) + .firstOrNull { it.isFile && it.extension.equals("apk", ignoreCase = true) } } private fun findObbFiles(gameId: Int): List { val dir = File(SteamService.getAppDirPath(gameId)) if (!dir.exists()) return emptyList() - return dir.walkTopDown().filter { it.isFile && it.extension.equals("obb", ignoreCase = true) }.toList() + return dir.walkTopDown().maxDepth(MAX_SEARCH_DEPTH) + .filter { it.isFile && it.extension.equals("obb", ignoreCase = true) }.toList() } private fun getApkPackageName(context: Context, apk: File): String? { @@ -65,6 +76,23 @@ object AndroidGameLauncher { } } + /** + * Copies the downloaded .apk into the app's own cache dir so FileProvider only ever needs to + * grant access to a narrow, app-controlled location (already covered by the existing + * cache-path entry) instead of the whole install-directory tree. + */ + private fun stageApkForInstall(context: Context, gameId: Int, apk: File): File? { + return try { + val stagingDir = File(context.cacheDir, "android_game_installs").apply { mkdirs() } + val staged = File(stagingDir, "$gameId.apk") + apk.copyTo(staged, overwrite = true) + staged + } catch (e: Exception) { + Timber.tag(TAG).e(e, "Could not stage APK for install from ${apk.absolutePath}") + null + } + } + /** Package name of this game's downloaded .apk, or null if none is on disk / unreadable. */ fun resolvePackageName(context: Context, gameId: Int): String? { val apk = findApkFile(gameId) ?: return null @@ -101,8 +129,8 @@ object AndroidGameLauncher { } if (!isPackageInstalled(context, packageName)) { copyObbFiles(gameId, packageName) - UpdateInstaller.installApk(context, apk) - return Result.InstallStarted + val staged = stageApkForInstall(context, gameId, apk) ?: return Result.Failed + return if (UpdateInstaller.installApk(context, staged)) Result.InstallStarted else Result.Failed } val intent = context.packageManager.getLaunchIntentForPackage(packageName) ?: return Result.Failed @@ -112,30 +140,64 @@ object AndroidGameLauncher { } /** - * Prompts the system uninstall dialog for the Android package matching this game, if one - * is actually installed. Must be called before the downloaded .apk is deleted from disk - * (needed to resolve the package name). This is a separate step from GameNative forgetting - * the game — the installed app is a distinct entity on the system that Android requires - * explicit user confirmation to remove. + * Prompts the system uninstall dialog for the Android package matching this game (if one is + * actually installed) and suspends until the dialog is dismissed. Uses + * ACTION_UNINSTALL_PACKAGE + EXTRA_RETURN_RESULT so the result (confirmed vs. cancelled) comes + * back immediately as an activity result, instead of waiting on an ACTION_PACKAGE_REMOVED + * broadcast that a cancelled dialog never sends — that previously left the caller's "deleting" + * UI stuck for a long fixed timeout on every cancel. + * + * Returns true when it's safe for the caller to delete the downloaded .apk / GameNative's own + * install record: either nothing was installed to begin with, or the system confirmed removal. + * Returns false if the user cancelled (or context isn't an Activity capable of launching for a + * result), so the caller must NOT delete anything in that case — otherwise the app would be + * left installed with no way left to resolve its package name. + * + * Must be called before the downloaded .apk is deleted from disk (needed to resolve the + * package name in the first place). */ - fun requestUninstall(context: Context, gameId: Int) { + suspend fun requestUninstall(context: Context, gameId: Int): Boolean { val apk = findApkFile(gameId) if (apk == null) { Timber.tag(TAG).w("requestUninstall: no APK found on disk for game $gameId, skipping system uninstall") - return + return true } val packageName = getApkPackageName(context, apk) if (packageName == null) { Timber.tag(TAG).w("requestUninstall: could not resolve package name from ${apk.absolutePath}") - return + return true } if (!isPackageInstalled(context, packageName)) { - Timber.tag(TAG).w("requestUninstall: package $packageName is not currently installed, skipping") - return + Timber.tag(TAG).i("requestUninstall: package $packageName is not currently installed, nothing to do") + return true + } + val activity = context as? ComponentActivity + if (activity == null) { + Timber.tag(TAG).e("requestUninstall: context is not a ComponentActivity, cannot prompt for a result") + return false } + Timber.tag(TAG).i("requestUninstall: prompting system uninstall for $packageName") - val intent = Intent(Intent.ACTION_DELETE, Uri.parse("package:$packageName")) - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - context.startActivity(intent) + return suspendCancellableCoroutine { cont -> + lateinit var launcher: androidx.activity.result.ActivityResultLauncher + launcher = activity.activityResultRegistry.register( + "android_game_uninstall_$gameId", + ActivityResultContracts.StartActivityForResult(), + ) { result -> + launcher.unregister() + if (cont.isActive) cont.resume(result.resultCode == Activity.RESULT_OK) + } + cont.invokeOnCancellation { runCatching { launcher.unregister() } } + + val intent = Intent(Intent.ACTION_UNINSTALL_PACKAGE, Uri.parse("package:$packageName")) + .putExtra(Intent.EXTRA_RETURN_RESULT, true) + try { + launcher.launch(intent) + } catch (e: Exception) { + Timber.tag(TAG).e(e, "Could not launch system uninstall for $packageName") + launcher.unregister() + if (cont.isActive) cont.resume(false) + } + } } } diff --git a/app/src/main/java/app/gamenative/utils/SteamUtils.kt b/app/src/main/java/app/gamenative/utils/SteamUtils.kt index 3955d16d4c..773aa1d494 100644 --- a/app/src/main/java/app/gamenative/utils/SteamUtils.kt +++ b/app/src/main/java/app/gamenative/utils/SteamUtils.kt @@ -114,6 +114,7 @@ object SteamUtils { ownedDlc: Map?, licensedDepotIds: Set?, hasSteamUnlockedBranch: Boolean = false, + wantAndroid: Boolean = false, ): String { // A depot installs once its language is chosen if it passes every check except language // and arch. Arch is left out because it is a per-language preference, not a gate. @@ -126,7 +127,8 @@ object SteamUtils { licensedDepotIds == null || depotId in licensedDepotIds // Mirror the SteamChina realm gate in filterForDownloadableDepots, or we could pick a // language only the final pass drops and lose the real fallback. - return isWindowsCompatible && realm != SteamRealm.SteamChina && + val osCompatible = if (wantAndroid) isAndroidCompatible else isWindowsCompatible + return osCompatible && realm != SteamRealm.SteamChina && hasContent && ownedIfDlc && licensedIfBaseGame } diff --git a/app/src/main/java/app/gamenative/utils/UpdateInstaller.kt b/app/src/main/java/app/gamenative/utils/UpdateInstaller.kt index 70277f8602..84b35e84ca 100644 --- a/app/src/main/java/app/gamenative/utils/UpdateInstaller.kt +++ b/app/src/main/java/app/gamenative/utils/UpdateInstaller.kt @@ -64,12 +64,13 @@ object UpdateInstaller { } } - fun installApk(context: Context, apkFile: File) { + /** Returns true only if the system install intent was actually launched. */ + fun installApk(context: Context, apkFile: File): Boolean { try { // Verify file exists before attempting installation if (!apkFile.exists()) { Timber.e("APK file does not exist: ${apkFile.absolutePath}") - return + return false } Timber.i("Installing APK from: ${apkFile.absolutePath}, size: ${apkFile.length()} bytes") @@ -84,7 +85,7 @@ object UpdateInstaller { ) } catch (e: Exception) { Timber.e(e, "Error getting FileProvider URI") - return + return false } } else { // Use file:// URI for older versions @@ -114,8 +115,10 @@ object UpdateInstaller { context.startActivity(intent) Timber.i("Install intent launched successfully") + return true } catch (e: Exception) { Timber.e(e, "Error launching install intent") + return false } } } diff --git a/app/src/main/java/com/winlator/container/ContainerManager.java b/app/src/main/java/com/winlator/container/ContainerManager.java index 1ee9538815..2f029127be 100644 --- a/app/src/main/java/com/winlator/container/ContainerManager.java +++ b/app/src/main/java/com/winlator/container/ContainerManager.java @@ -225,6 +225,7 @@ private void duplicateContainer(Container srcContainer) { dstContainer.setDesktopTheme(srcContainer.getDesktopTheme()); dstContainer.setRcfileId(srcContainer.getRCFileId()); dstContainer.setWineVersion(srcContainer.getWineVersion()); + dstContainer.setPlatform(srcContainer.getPlatform()); dstContainer.saveData(); containers.add(dstContainer); diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 6920e18c5a..12385f3abd 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -647,12 +647,13 @@ Plus de variables connues Variante du conteneur Version - Choisissez entre télécharger la version Windows (via Wine) ou la version Android native, lorsque le jeu la propose. - Normal (Windows) + Choisissez de télécharger la version Windows (via Wine/Proton) ou la version Android native, lorsque le jeu la propose. + Normale (Windows) Android Changement de version — suppression de l\'installation actuelle et récupération de la nouvelle… Installation de la version Android — appuyez de nouveau sur Jouer une fois l\'installation terminée. Impossible d\'installer ou de lancer la version Android de ce jeu. + Désinstallation annulée — l\'application est toujours installée, rien n\'a été supprimé. Version Wine Arguments d\'exécution Exemple : -dx11 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0d460a4514..6cb29cf107 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -768,12 +768,13 @@ No more known variables Container Variant Version - Choose whether to download the Windows version (runs via Wine) or the native Android version, when the game offers one. + Choose whether to download the Windows version (runs via Wine/Proton) or the native Android version, when the game offers one. Normal (Windows) Android Switching version — removing the current install and fetching the new one… Installing the Android version — press Play again once it\'s installed. Couldn\'t install or launch the Android version of this game. + Uninstall was cancelled — the app is still installed, nothing was deleted. Wine Version Exec Arguments Example: -dx11 diff --git a/app/src/main/res/xml/file_provider_paths.xml b/app/src/main/res/xml/file_provider_paths.xml index a1ec971da2..cf58fd023e 100644 --- a/app/src/main/res/xml/file_provider_paths.xml +++ b/app/src/main/res/xml/file_provider_paths.xml @@ -3,15 +3,8 @@ - - - - + diff --git a/app/src/test/java/app/gamenative/enums/OSTest.kt b/app/src/test/java/app/gamenative/enums/OSTest.kt new file mode 100644 index 0000000000..f3b6aec7c8 --- /dev/null +++ b/app/src/test/java/app/gamenative/enums/OSTest.kt @@ -0,0 +1,58 @@ +package app.gamenative.enums + +import java.util.EnumSet +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test + +class OSTest { + + @Test + fun `from string parses android`() { + assertEquals(EnumSet.of(OS.android), OS.from("android")) + } + + @Test + fun `from string parses a comma separated list including android`() { + assertEquals(EnumSet.of(OS.windows, OS.android), OS.from("windows,android")) + } + + @Test + fun `from string falls back to none for an unknown value`() { + assertEquals(EnumSet.of(OS.none), OS.from("some_unknown_os")) + } + + @Test + fun `from string returns none for a null or empty value`() { + assertEquals(EnumSet.of(OS.none), OS.from(null)) + assertEquals(EnumSet.of(OS.none), OS.from("")) + } + + // OS.from(Int) always contains `none` too, since none.code == 0 and any bitmask ANDed with 0 + // is 0 — a pre-existing quirk unrelated to the `android` bit — so these check membership + // rather than exact set equality. + @Test + fun `code and from int round trip for android alone`() { + val roundTripped = OS.from(OS.code(EnumSet.of(OS.android))) + assertTrue(roundTripped.contains(OS.android)) + assertFalse(roundTripped.contains(OS.windows)) + assertFalse(roundTripped.contains(OS.macos)) + assertFalse(roundTripped.contains(OS.linux)) + } + + @Test + fun `code and from int round trip for a combination including android`() { + val osses = EnumSet.of(OS.windows, OS.linux, OS.android) + val roundTripped = OS.from(OS.code(osses)) + assertTrue(roundTripped.containsAll(osses)) + assertFalse(roundTripped.contains(OS.macos)) + } + + @Test + fun `android has a distinct bit from the other OS values`() { + assertEquals(0x08, OS.android.code) + val others = listOf(OS.none, OS.windows, OS.macos, OS.linux) + others.forEach { assertEquals(0, it.code and OS.android.code) } + } +} From 73875031b2dff575f2a6a4b6456f9509f18d8716 Mon Sep 17 00:00:00 2001 From: moi952 <52632400+moi952@users.noreply.github.com> Date: Sun, 19 Jul 2026 20:04:17 +0200 Subject: [PATCH 4/7] fix: skip Wine override on Android containers, propagate install result, fix receiver race, clean up staged APKs --- .../screen/library/appscreen/BaseAppScreen.kt | 15 +++++++++++---- .../screen/library/appscreen/SteamAppScreen.kt | 2 ++ .../app/gamenative/utils/AndroidGameLauncher.kt | 17 ++++++++++++++++- .../java/app/gamenative/utils/ContainerUtils.kt | 8 ++++++-- .../app/gamenative/utils/UpdateInstaller.kt | 4 ++-- 5 files changed, 37 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/app/gamenative/ui/screen/library/appscreen/BaseAppScreen.kt b/app/src/main/java/app/gamenative/ui/screen/library/appscreen/BaseAppScreen.kt index 2687a308eb..9ba0a136ba 100644 --- a/app/src/main/java/app/gamenative/ui/screen/library/appscreen/BaseAppScreen.kt +++ b/app/src/main/java/app/gamenative/ui/screen/library/appscreen/BaseAppScreen.kt @@ -14,6 +14,7 @@ import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateMapOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -1053,6 +1054,11 @@ abstract class BaseAppScreen { val uiScope = rememberCoroutineScope() + // Increments on every refresh, unlike isDownloadingState/isInstalledState which can flip + // true->false within the same recomposition on a very fast download and never be observed + // by an effect keyed on them — this always counts as a distinct key. + var stateRefreshGeneration by remember(libraryItem.appId) { mutableIntStateOf(0) } + suspend fun performStateRefresh(includeUpdatePending: Boolean) { isInstalledState = isInstalled(context, libraryItem) isValidToDownloadState = isValidToDownload(context, libraryItem) @@ -1064,6 +1070,7 @@ abstract class BaseAppScreen { if (includeUpdatePending) { isUpdatePendingState = isUpdatePendingSuspend(context, libraryItem) } + stateRefreshGeneration++ } fun requestStateRefresh(includeUpdatePending: Boolean) { @@ -1079,10 +1086,10 @@ abstract class BaseAppScreen { // Android platform games: the actual install happens in the system's own installer UI, // outside our control, so listen for its completion broadcast instead of requiring the // user to leave and come back to this screen for the Play button to appear. - // Keyed on download/install state too: getAndroidPackageName() reads the APK off disk, - // which is null until the download finishes, so the effect needs to re-run once that - // changes rather than being stuck with the null result from the initial composition. - DisposableEffect(libraryItem.appId, isDownloadingState, isInstalledState) { + // Keyed on stateRefreshGeneration: getAndroidPackageName() reads the APK off disk, which + // is null until the download finishes, so the effect needs to re-run once that changes + // rather than being stuck with the null result from the initial composition. + DisposableEffect(libraryItem.appId, stateRefreshGeneration) { val androidPackageName = getAndroidPackageName(context, libraryItem) if (androidPackageName == null) { onDispose { } diff --git a/app/src/main/java/app/gamenative/ui/screen/library/appscreen/SteamAppScreen.kt b/app/src/main/java/app/gamenative/ui/screen/library/appscreen/SteamAppScreen.kt index 6ad62db869..2932878733 100644 --- a/app/src/main/java/app/gamenative/ui/screen/library/appscreen/SteamAppScreen.kt +++ b/app/src/main/java/app/gamenative/ui/screen/library/appscreen/SteamAppScreen.kt @@ -914,6 +914,7 @@ class SteamAppScreen : BaseAppScreen() { SnackbarManager.show(context.getString(R.string.android_game_uninstall_cancelled)) return@launch } + AndroidGameLauncher.cleanupStagedApk(context, gameId) } SteamService.deleteApp(gameId) DownloadService.invalidateCache() @@ -1354,6 +1355,7 @@ class SteamAppScreen : BaseAppScreen() { } return@launch } + AndroidGameLauncher.cleanupStagedApk(context, gameId) val success = SteamService.deleteApp(gameId) DownloadService.invalidateCache() diff --git a/app/src/main/java/app/gamenative/utils/AndroidGameLauncher.kt b/app/src/main/java/app/gamenative/utils/AndroidGameLauncher.kt index 01ccdd1357..6b468afa30 100644 --- a/app/src/main/java/app/gamenative/utils/AndroidGameLauncher.kt +++ b/app/src/main/java/app/gamenative/utils/AndroidGameLauncher.kt @@ -76,6 +76,8 @@ object AndroidGameLauncher { } } + private const val STAGING_DIR_NAME = "android_game_installs" + /** * Copies the downloaded .apk into the app's own cache dir so FileProvider only ever needs to * grant access to a narrow, app-controlled location (already covered by the existing @@ -83,7 +85,7 @@ object AndroidGameLauncher { */ private fun stageApkForInstall(context: Context, gameId: Int, apk: File): File? { return try { - val stagingDir = File(context.cacheDir, "android_game_installs").apply { mkdirs() } + val stagingDir = File(context.cacheDir, STAGING_DIR_NAME).apply { mkdirs() } val staged = File(stagingDir, "$gameId.apk") apk.copyTo(staged, overwrite = true) staged @@ -93,6 +95,19 @@ object AndroidGameLauncher { } } + /** + * Removes this game's staged install copy, if any. Each staged .apk is only overwritten by a + * later install of the *same* game, so without this, every distinct Android game ever + * installed leaves a permanent copy behind in the cache dir. Safe to call any time, including + * when nothing was ever staged. + */ + fun cleanupStagedApk(context: Context, gameId: Int) { + val staged = File(File(context.cacheDir, STAGING_DIR_NAME), "$gameId.apk") + if (staged.exists() && !staged.delete()) { + Timber.tag(TAG).w("Could not delete staged APK ${staged.absolutePath}") + } + } + /** Package name of this game's downloaded .apk, or null if none is on disk / unreadable. */ fun resolvePackageName(context: Context, gameId: Int): String? { val apk = findApkFile(gameId) ?: return null diff --git a/app/src/main/java/app/gamenative/utils/ContainerUtils.kt b/app/src/main/java/app/gamenative/utils/ContainerUtils.kt index 7cf53904fe..2bba257d47 100644 --- a/app/src/main/java/app/gamenative/utils/ContainerUtils.kt +++ b/app/src/main/java/app/gamenative/utils/ContainerUtils.kt @@ -1084,8 +1084,12 @@ object ContainerUtils { return if (containerManager.hasContainer(appId)) { val container = containerManager.getContainerById(appId) - // Apply temporary override if present (without saving to disk) - if (IntentLaunchManager.hasTemporaryOverride(appId)) { + // Temporary overrides only carry Wine/Winlator settings (graphics driver, dxwrapper, + // etc.) and applyToContainer() unconditionally writes to .wine/user.reg — a native + // Android container never has a .wine folder, so skip overrides entirely there rather + // than risk that write failing before AndroidGameLauncher ever runs. + val isAndroidContainer = container.platform.equals(Container.PLATFORM_ANDROID, ignoreCase = true) + if (!isAndroidContainer && IntentLaunchManager.hasTemporaryOverride(appId)) { val overrideConfig = IntentLaunchManager.getTemporaryOverride(appId) if (overrideConfig != null) { // Backup original config before applying override (if not already backed up) diff --git a/app/src/main/java/app/gamenative/utils/UpdateInstaller.kt b/app/src/main/java/app/gamenative/utils/UpdateInstaller.kt index 84b35e84ca..0c752537b8 100644 --- a/app/src/main/java/app/gamenative/utils/UpdateInstaller.kt +++ b/app/src/main/java/app/gamenative/utils/UpdateInstaller.kt @@ -53,11 +53,11 @@ object UpdateInstaller { Timber.i("Download complete: ${destFile.absolutePath}, size: $fileSize bytes") // Install the APK - withContext(Dispatchers.Main) { + val installStarted = withContext(Dispatchers.Main) { installApk(context, destFile) } - return@withContext true + return@withContext installStarted } catch (e: Exception) { Timber.e(e, "Error downloading/installing update") return@withContext false From 3136d13f7aaed45b33b4ca7a8b840378bf45c5d5 Mon Sep 17 00:00:00 2001 From: moi952 <52632400+moi952@users.noreply.github.com> Date: Sun, 19 Jul 2026 21:24:56 +0200 Subject: [PATCH 5/7] feat: add Android-version library filter, fix Edit Container blocking on the main thread --- .../component/dialog/ContainerConfigDialog.kt | 8 ++++- .../java/app/gamenative/ui/enums/AppFilter.kt | 6 ++++ .../gamenative/ui/model/LibraryViewModel.kt | 22 +++++++++++--- .../screen/library/appscreen/BaseAppScreen.kt | 20 +++++++++++-- .../library/components/LibraryOptionsPanel.kt | 29 ++++++++++++------- .../gamenative/utils/AndroidGameLauncher.kt | 10 ++++++- app/src/main/res/values-fr/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 8 files changed, 77 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt b/app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt index 56f253d446..0951f7c800 100644 --- a/app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt +++ b/app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt @@ -1415,6 +1415,12 @@ internal fun ExecutablePathDropdown( var isLoading by remember { mutableStateOf(true) } val context = LocalContext.current + // Otherwise a stale `expanded = true` from before this field was disabled would keep the + // menu open (and its items selectable) even though it's now greyed out. + LaunchedEffect(enabled) { + if (!enabled) expanded = false + } + // Load executables from A: drive when component is first created LaunchedEffect(containerData.drives) { isLoading = true @@ -1464,7 +1470,7 @@ internal fun ExecutablePathDropdown( if (!isLoading && executables.isNotEmpty()) { ExposedDropdownMenu( - expanded = expanded, + expanded = expanded && enabled, onDismissRequest = { expanded = false } ) { executables.forEach { executable -> diff --git a/app/src/main/java/app/gamenative/ui/enums/AppFilter.kt b/app/src/main/java/app/gamenative/ui/enums/AppFilter.kt index 662b08256b..d78cd779a7 100644 --- a/app/src/main/java/app/gamenative/ui/enums/AppFilter.kt +++ b/app/src/main/java/app/gamenative/ui/enums/AppFilter.kt @@ -1,6 +1,7 @@ package app.gamenative.ui.enums import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Android import androidx.compose.material.icons.filled.AvTimer import androidx.compose.material.icons.filled.Build import androidx.compose.material.icons.filled.Computer @@ -84,6 +85,11 @@ enum class AppFilter( displayTextRes = R.string.filter_proven_gpu, icon = Icons.Rounded.SportsEsports, ), + ANDROID( + code = 0x1000, + displayTextRes = R.string.app_filter_android, + icon = Icons.Default.Android, + ), // ALPHABETIC( // code = 0x20, // displayText = "Alphabetic", diff --git a/app/src/main/java/app/gamenative/ui/model/LibraryViewModel.kt b/app/src/main/java/app/gamenative/ui/model/LibraryViewModel.kt index 884332a57d..6306d70013 100644 --- a/app/src/main/java/app/gamenative/ui/model/LibraryViewModel.kt +++ b/app/src/main/java/app/gamenative/ui/model/LibraryViewModel.kt @@ -626,6 +626,15 @@ class LibraryViewModel @Inject constructor( true } } + .filter { item -> + // item.depots is already deserialized in memory on the SteamApp we just + // loaded, so this check is free — no per-item DB lookup. + if (currentState.appInfoSortType.contains(AppFilter.ANDROID)) { + item.depots.values.any { it.isAndroidCompatible } + } else { + true + } + } .toList() // Per-collection counts: computed from the owner/type/search-filtered set (independent of the @@ -946,12 +955,17 @@ class LibraryViewModel @Inject constructor( // sources can't match it — keep them out of the combined list (and their tab counts). val steamCollectionSelected = allowedSteamAppIds != null + // The Android depot feature only exists for Steam apps (it's a Steam Frame / Lepton + // concept), so the other sources can never match this filter either. + val androidFilterActive = currentState.appInfoSortType.contains(AppFilter.ANDROID) + val excludeNonSteam = steamCollectionSelected || androidFilterActive + val combined = buildList { if (includeSteam) addAll(steamEntries) - if (includeOpen && !steamCollectionSelected) addAll(customEntries) - if (includeGOG && !steamCollectionSelected) addAll(gogEntries) - if (includeEpic && !steamCollectionSelected) addAll(epicEntries) - if (includeAmazon && !steamCollectionSelected) addAll(amazonEntries) + if (includeOpen && !excludeNonSteam) addAll(customEntries) + if (includeGOG && !excludeNonSteam) addAll(gogEntries) + if (includeEpic && !excludeNonSteam) addAll(epicEntries) + if (includeAmazon && !excludeNonSteam) addAll(amazonEntries) }.sortedWith(sortComparator).mapIndexed { idx, entry -> entry.item.copy(index = idx, isInstalled = entry.isInstalled) } diff --git a/app/src/main/java/app/gamenative/ui/screen/library/appscreen/BaseAppScreen.kt b/app/src/main/java/app/gamenative/ui/screen/library/appscreen/BaseAppScreen.kt index 9ba0a136ba..7e6521711b 100644 --- a/app/src/main/java/app/gamenative/ui/screen/library/appscreen/BaseAppScreen.kt +++ b/app/src/main/java/app/gamenative/ui/screen/library/appscreen/BaseAppScreen.kt @@ -1052,6 +1052,14 @@ abstract class BaseAppScreen { mutableStateOf(hasLeftoverInstall(context, libraryItem)) } + // hasAndroidVersion() can hit the database (SteamService.getAppInfoOf does a blocking + // Room query) — compute it off the main thread once per screen visit, instead of + // re-running it synchronously on the main thread every time Edit Container is opened. + var hasAndroidVersionState by remember(libraryItem.appId) { mutableStateOf(false) } + LaunchedEffect(libraryItem.appId) { + hasAndroidVersionState = withContext(Dispatchers.IO) { hasAndroidVersion(libraryItem) } + } + val uiScope = rememberCoroutineScope() // Increments on every refresh, unlike isDownloadingState/isInstalledState which can flip @@ -1127,8 +1135,14 @@ abstract class BaseAppScreen { } val onEditContainer: () -> Unit = { - containerData = loadContainerData(context, libraryItem) - showConfigDialog = true + // loadContainerData() reads/parses the container's JSON off disk (and may create it + // on first access) — do that off the main thread so opening this dialog doesn't + // freeze the screen while it loads. + uiScope.launch { + val data = withContext(Dispatchers.IO) { loadContainerData(context, libraryItem) } + containerData = data + showConfigDialog = true + } } // Export for Frontend launcher @@ -1429,7 +1443,7 @@ abstract class BaseAppScreen { ContainerConfigDialog( title = "${displayInfo.name} Config", initialConfig = containerData, - hasAndroidVersion = remember(libraryItem.appId) { hasAndroidVersion(libraryItem) }, + hasAndroidVersion = hasAndroidVersionState, onDismissRequest = { showConfigDialog = false }, onSave = { saveContainerConfig(context, libraryItem, it) diff --git a/app/src/main/java/app/gamenative/ui/screen/library/components/LibraryOptionsPanel.kt b/app/src/main/java/app/gamenative/ui/screen/library/components/LibraryOptionsPanel.kt index 34c49efec9..1360226a17 100644 --- a/app/src/main/java/app/gamenative/ui/screen/library/components/LibraryOptionsPanel.kt +++ b/app/src/main/java/app/gamenative/ui/screen/library/components/LibraryOptionsPanel.kt @@ -73,6 +73,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import app.gamenative.BuildConfig import app.gamenative.PrefManager import app.gamenative.R import app.gamenative.data.SteamCollection @@ -259,18 +260,24 @@ fun LibraryOptionsPanel( .padding(horizontal = 8.dp), verticalArrangement = Arrangement.spacedBy(2.dp) ) { + // Modern/ModernXr builds can't install a native Android build (Horizon + // Store compliance strips the install-package permissions), so this + // filter would just hide games with nothing wrong with them there. + val statusFilters = remember { + buildList { + add(AppFilter.INSTALLED) + add(AppFilter.SHARED) + add(AppFilter.COMPATIBLE) + add(AppFilter.EXPIRED) + add(AppFilter.PLAYABLE) + add(AppFilter.FIVE_STAR) + add(AppFilter.FIVE_STAR_GPU) + add(AppFilter.PROVEN_GPU) + if (!BuildConfig.MODERN_ANDROID) add(AppFilter.ANDROID) + } + } AppFilter.entries.forEach { appFilter -> - if (appFilter in listOf( - AppFilter.INSTALLED, - AppFilter.SHARED, - AppFilter.COMPATIBLE, - AppFilter.EXPIRED, - AppFilter.PLAYABLE, - AppFilter.FIVE_STAR, - AppFilter.FIVE_STAR_GPU, - AppFilter.PROVEN_GPU, - ) - ) { + if (appFilter in statusFilters) { OptionListItem( text = stringResource(appFilter.displayTextRes), selected = selectedFilters.contains(appFilter), diff --git a/app/src/main/java/app/gamenative/utils/AndroidGameLauncher.kt b/app/src/main/java/app/gamenative/utils/AndroidGameLauncher.kt index 6b468afa30..41040b3cbe 100644 --- a/app/src/main/java/app/gamenative/utils/AndroidGameLauncher.kt +++ b/app/src/main/java/app/gamenative/utils/AndroidGameLauncher.kt @@ -2,6 +2,7 @@ package app.gamenative.utils import android.app.Activity import android.content.Context +import android.content.ContextWrapper import android.content.Intent import android.content.pm.PackageManager import android.net.Uri @@ -49,6 +50,13 @@ object AndroidGameLauncher { } } + /** Unwraps a possibly-wrapped Context (e.g. a ContextWrapper) to find the backing Activity. */ + private tailrec fun Context.findActivity(): ComponentActivity? = when (this) { + is ComponentActivity -> this + is ContextWrapper -> baseContext.findActivity() + else -> null + } + private fun isPackageInstalled(context: Context, packageName: String): Boolean { return try { context.packageManager.getPackageInfo(packageName, 0) @@ -186,7 +194,7 @@ object AndroidGameLauncher { Timber.tag(TAG).i("requestUninstall: package $packageName is not currently installed, nothing to do") return true } - val activity = context as? ComponentActivity + val activity = context.findActivity() if (activity == null) { Timber.tag(TAG).e("requestUninstall: context is not a ComponentActivity, cannot prompt for a result") return false diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 12385f3abd..fe622d987e 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -1629,6 +1629,7 @@ Avis 5 étoiles sur l\'appareil Avis 5 étoiles sur le GPU Éprouvé sur votre GPU + Version Android disponible Gérer les mods Nexus Effacer la recherche Tout diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6cb29cf107..409678657e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -726,6 +726,7 @@ 5-star reviews on device 5-star reviews on GPU Proven on your GPU + Android version available Connecting to Steam… From 654e7d90beecf8654465e5dcee7c6f32abceb011 Mon Sep 17 00:00:00 2001 From: moi952 <52632400+moi952@users.noreply.github.com> Date: Sun, 19 Jul 2026 21:46:01 +0200 Subject: [PATCH 6/7] fix: strip persisted Android library filter on Modern/ModernXr builds, add Android-mode depot language test coverage --- .../app/gamenative/ui/data/LibraryState.kt | 17 +++++- .../utils/SteamUtilsDepotLanguageTest.kt | 53 ++++++++++++++++++- 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/app/gamenative/ui/data/LibraryState.kt b/app/src/main/java/app/gamenative/ui/data/LibraryState.kt index ec17d918a5..4c138af897 100644 --- a/app/src/main/java/app/gamenative/ui/data/LibraryState.kt +++ b/app/src/main/java/app/gamenative/ui/data/LibraryState.kt @@ -1,5 +1,6 @@ package app.gamenative.ui.data +import app.gamenative.BuildConfig import app.gamenative.PrefManager import app.gamenative.data.GameCompatibilityStatus import app.gamenative.data.GameSource @@ -11,8 +12,22 @@ import app.gamenative.ui.enums.LibraryTab import app.gamenative.ui.enums.SortOption import java.util.EnumSet +/** + * Modern/ModernXr can't show or toggle the Android filter (see LibraryOptionsPanel), but a + * persisted selection from a Legacy install — or a restored backup — could still carry the flag. + * Strip it here so it can never silently restrict/empty the library with no way to turn it off. + */ +private fun sanitizedLibraryFilter(): EnumSet { + val stored = PrefManager.libraryFilter + return if (BuildConfig.MODERN_ANDROID && stored.contains(AppFilter.ANDROID)) { + EnumSet.copyOf(stored).apply { remove(AppFilter.ANDROID) } + } else { + stored + } +} + data class LibraryState( - val appInfoSortType: EnumSet = PrefManager.libraryFilter, + val appInfoSortType: EnumSet = sanitizedLibraryFilter(), val appInfoList: List = emptyList(), val isRefreshing: Boolean = false, diff --git a/app/src/test/java/app/gamenative/utils/SteamUtilsDepotLanguageTest.kt b/app/src/test/java/app/gamenative/utils/SteamUtilsDepotLanguageTest.kt index dc6cd0a851..742a3754fb 100644 --- a/app/src/test/java/app/gamenative/utils/SteamUtilsDepotLanguageTest.kt +++ b/app/src/test/java/app/gamenative/utils/SteamUtilsDepotLanguageTest.kt @@ -48,7 +48,8 @@ class SteamUtilsDepotLanguageTest { ownedDlc: Map? = null, licensedDepotIds: Set? = null, hasSteamUnlockedBranch: Boolean = false, - ) = SteamUtils.effectiveDepotLanguage(depots, preferred, ownedDlc, licensedDepotIds, hasSteamUnlockedBranch) + wantAndroid: Boolean = false, + ) = SteamUtils.effectiveDepotLanguage(depots, preferred, ownedDlc, licensedDepotIds, hasSteamUnlockedBranch, wantAndroid) // -- Priority 1: requested language wins when available -- @@ -195,4 +196,54 @@ class SteamUtilsDepotLanguageTest { fun `returns preferred language when there are no depots`() { assertEquals("french", resolve(emptyMap(), "french")) } + + // -- Android depot selection (wantAndroid) -- + + @Test + fun `returns requested language when the android depot ships it`() { + val depots = depotsOf( + depot(depotId = 1, language = "english", osList = EnumSet.of(OS.android)), + depot(depotId = 2, language = "french", osList = EnumSet.of(OS.android)), + ) + assertEquals("french", resolve(depots, "french", wantAndroid = true)) + } + + @Test + fun `falls back to english for android depots when requested language is absent`() { + val depots = depotsOf( + depot(depotId = 1, language = "english", osList = EnumSet.of(OS.android)), + depot(depotId = 2, language = "german", osList = EnumSet.of(OS.android)), + ) + assertEquals("english", resolve(depots, "french", wantAndroid = true)) + } + + @Test + fun `an untagged depot is not a neutral fallback for android — windows-only neutrality does not carry over`() { + val depots = depotsOf( + depot(depotId = 1, language = "german", osList = EnumSet.of(OS.android)), + // Untagged (OS.none): counts as installable for Windows (isWindowsCompatible), but + // isAndroidCompatible requires an explicit android tag, so this must NOT satisfy the + // Android pass and must NOT be treated as a neutral depot there. + depot(depotId = 2, language = "", osList = EnumSet.of(OS.none)), + ) + assertEquals("german", resolve(depots, "french", wantAndroid = true)) + } + + @Test + fun `a windows-only depot is ignored when android is requested`() { + val depots = depotsOf( + depot(depotId = 1, language = "german", osList = EnumSet.of(OS.windows)), + ) + // No android-compatible depot at all -> nothing to steer the language away from preferred. + assertEquals("french", resolve(depots, "french", wantAndroid = true)) + } + + @Test + fun `a depot explicitly tagged both android and neutral-language keeps the preferred language`() { + val depots = depotsOf( + depot(depotId = 1, language = "", osList = EnumSet.of(OS.android)), + depot(depotId = 2, language = "german", osList = EnumSet.of(OS.android)), + ) + assertEquals("french", resolve(depots, "french", wantAndroid = true)) + } } From cd2e4bbf6a09d92bff3e6531e251ac5db9ac3123 Mon Sep 17 00:00:00 2001 From: moi952 <52632400+moi952@users.noreply.github.com> Date: Thu, 23 Jul 2026 12:22:43 +0200 Subject: [PATCH 7/7] feat: add Meta Quest immersive launch mode with Xbox-mapped Touch controllers --- app/build.gradle.kts | 19 + app/src/main/AndroidManifest.xml | 61 + app/src/main/cpp/xrimmersive/CMakeLists.txt | 33 + app/src/main/cpp/xrimmersive/jni_bridge.cpp | 222 +++ app/src/main/cpp/xrimmersive/xr_immersive.cpp | 1218 ++++++++++++ app/src/main/cpp/xrimmersive/xr_immersive.h | 286 +++ .../main/java/app/gamenative/MainActivity.kt | 12 + .../app/gamenative/events/AndroidEvent.kt | 1 + .../app/gamenative/service/SteamService.kt | 8 +- .../app/gamenative/ui/component/QuickMenu.kt | 473 ++++- .../ui/component/ScreenEffectsPanel.kt | 34 +- .../app/gamenative/ui/model/MainViewModel.kt | 50 +- .../ui/screen/library/LibraryAppScreen.kt | 36 + .../screen/library/appscreen/BaseAppScreen.kt | 32 + .../gamenative/ui/screen/xr/DirectGLBridge.kt | 126 ++ .../ui/screen/xr/DirectVulkanBridge.kt | 38 + .../ui/screen/xr/ImmersiveControls.kt | 44 + .../ui/screen/xr/ImmersiveXrActivity.kt | 1768 +++++++++++++++++ .../ui/screen/xr/XrGamepadBridge.kt | 89 + .../app/gamenative/ui/screen/xr/XrNative.kt | 94 + .../ui/screen/xserver/XServerScreen.kt | 72 + .../com/winlator/container/Container.java | 13 + .../renderer/GLHardwareBufferImporter.java | 26 + .../com/winlator/renderer/GLRenderer.java | 33 +- .../com/winlator/renderer/VulkanRenderer.java | 37 + .../renderer/VulkanXrFrameBridge.java | 31 + .../com/winlator/renderer/XrFrameBridge.java | 27 + .../main/jniLibs/arm64-v8a/libxrimmersive.so | Bin 0 -> 1572960 bytes app/src/main/res/values-fr/strings.xml | 14 + app/src/main/res/values/strings.xml | 14 + 30 files changed, 4867 insertions(+), 44 deletions(-) create mode 100644 app/src/main/cpp/xrimmersive/CMakeLists.txt create mode 100644 app/src/main/cpp/xrimmersive/jni_bridge.cpp create mode 100644 app/src/main/cpp/xrimmersive/xr_immersive.cpp create mode 100644 app/src/main/cpp/xrimmersive/xr_immersive.h create mode 100644 app/src/main/java/app/gamenative/ui/screen/xr/DirectGLBridge.kt create mode 100644 app/src/main/java/app/gamenative/ui/screen/xr/DirectVulkanBridge.kt create mode 100644 app/src/main/java/app/gamenative/ui/screen/xr/ImmersiveControls.kt create mode 100644 app/src/main/java/app/gamenative/ui/screen/xr/ImmersiveXrActivity.kt create mode 100644 app/src/main/java/app/gamenative/ui/screen/xr/XrGamepadBridge.kt create mode 100644 app/src/main/java/app/gamenative/ui/screen/xr/XrNative.kt create mode 100644 app/src/main/java/com/winlator/renderer/GLHardwareBufferImporter.java create mode 100644 app/src/main/java/com/winlator/renderer/VulkanXrFrameBridge.java create mode 100644 app/src/main/java/com/winlator/renderer/XrFrameBridge.java create mode 100755 app/src/main/jniLibs/arm64-v8a/libxrimmersive.so diff --git a/app/build.gradle.kts b/app/build.gradle.kts index eff1ae7158..708e0e1daf 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -207,6 +207,10 @@ android { buildFeatures { compose = true buildConfig = true + // Exposes the openxr_loader_for_android AAR's native headers/lib to CMake, for the + // (not yet wired into the default build — see xrimmersive/CMakeLists.txt) immersive + // VR native module. + prefab = true } packaging { @@ -314,6 +318,17 @@ android { // } // } + // Meta Quest immersive launch mode's native OpenXR module. Same convention as the + // other native modules above: not part of the default build (native libs ship as + // prebuilt .so files in jniLibs/) — temporarily uncomment to build+test locally, + // then copy the resulting libxrimmersive.so into jniLibs/arm64-v8a/ and re-comment. + // externalNativeBuild { + // cmake { + // path = file("src/main/cpp/xrimmersive/CMakeLists.txt") + // version = "3.22.1" + // } + // } + // (For now) Uncomment for LeakCanary to work. // configurations { // debugImplementation { @@ -348,6 +363,10 @@ dependencies { // Split Modules implementation(libs.bundles.google) + // Official Khronos OpenXR loader (Apache-2.0) for the Meta Quest immersive launch mode's + // native module (app/src/main/cpp/xrimmersive) — not a Winlator/GameNativeXR dependency. + implementation("org.khronos.openxr:openxr_loader_for_android:1.1.61") + // Winlator implementation(libs.bundles.winlator) implementation(libs.libarchive.android) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 29f6f4110f..94bf5a7201 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -6,6 +6,20 @@ android:glEsVersion="0x00020000" android:required="true" /> + + + + + + @@ -90,6 +104,53 @@ + + + + + + + + + +#include +#include +#include + +#include "xr_immersive.h" + +namespace { + +struct NativeHandle { + xrimmersive::XrImmersiveSession *session; + jobject activityGlobalRef; +}; + +#define LOG_TAG "xrimmersive_jni" +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) + +} // namespace + +extern "C" { + +JNIEXPORT jlong JNICALL +Java_app_gamenative_ui_screen_xr_XrNative_nativeCreate(JNIEnv *env, jclass, jobject activity) { + JavaVM *vm = nullptr; + env->GetJavaVM(&vm); + + auto *handle = new NativeHandle(); + handle->activityGlobalRef = env->NewGlobalRef(activity); + handle->session = new xrimmersive::XrImmersiveSession(); + handle->session->initialize(vm, handle->activityGlobalRef); + return reinterpret_cast(handle); +} + +JNIEXPORT void JNICALL +Java_app_gamenative_ui_screen_xr_XrNative_nativeRequestStop(JNIEnv *, jclass, jlong handlePtr) { + auto *handle = reinterpret_cast(handlePtr); + if (handle != nullptr) handle->session->requestStop(); +} + +JNIEXPORT void JNICALL +Java_app_gamenative_ui_screen_xr_XrNative_nativeJoinAndDestroy(JNIEnv *env, jclass, jlong handlePtr) { + auto *handle = reinterpret_cast(handlePtr); + if (handle == nullptr) return; + handle->session->join(); + delete handle->session; + env->DeleteGlobalRef(handle->activityGlobalRef); + delete handle; +} + +// outButtons[0] receives the Xbox-layout button bitmask (see XrGamepadBridge's BUTTON_*); +// outAxes receives [leftX, leftY, rightX, rightY, triggerL, triggerR]. outHandPoses receives +// [leftX,leftY,leftZ, leftFwdX,leftFwdY,leftFwdZ, rightX,rightY,rightZ, rightFwdX,rightFwdY, +// rightFwdZ] (aim-pose ray per hand, in the quad's local space) — only meaningful when +// outFlags[0] (handPosesValid) is true. outFlags[1] is whether the XR-pointer-mode toggle +// (double-click of either thumbstick) fired since the last poll. outFlags[2] is the level state +// of the Menu/Start button — true for its entire physical hold, not just the rising edge — so +// Kotlin can suppress menu-navigation reads while the same hand may still be resting on/near the +// thumbstick. Returns whether the quick-menu trigger (Menu/Start held 600ms) fired since the last +// poll — a short press of the same button is still forwarded as a normal gamepad Start press, +// see xr_immersive.cpp's syncControllerInputs(). +JNIEXPORT jboolean JNICALL +Java_app_gamenative_ui_screen_xr_XrNative_nativePollSnapshot(JNIEnv *env, jclass, jlong handlePtr, + jintArray outButtons, + jfloatArray outAxes, + jfloatArray outHandPoses, + jbooleanArray outFlags) { + auto *handle = reinterpret_cast(handlePtr); + if (handle == nullptr) return JNI_FALSE; + + xrimmersive::InputSnapshot snapshot = handle->session->pollSnapshot(); + + jint buttons[1] = {static_cast(snapshot.buttons)}; + env->SetIntArrayRegion(outButtons, 0, 1, buttons); + + jfloat axes[6] = {snapshot.leftX, snapshot.leftY, snapshot.rightX, + snapshot.rightY, snapshot.triggerL, snapshot.triggerR}; + env->SetFloatArrayRegion(outAxes, 0, 6, axes); + + jfloat poses[12] = { + snapshot.leftHandPosX, snapshot.leftHandPosY, snapshot.leftHandPosZ, + snapshot.leftHandFwdX, snapshot.leftHandFwdY, snapshot.leftHandFwdZ, + snapshot.rightHandPosX, snapshot.rightHandPosY, snapshot.rightHandPosZ, + snapshot.rightHandFwdX, snapshot.rightHandFwdY, snapshot.rightHandFwdZ, + }; + env->SetFloatArrayRegion(outHandPoses, 0, 12, poses); + + jboolean flags[3] = { + static_cast(snapshot.handPosesValid ? JNI_TRUE : JNI_FALSE), + static_cast(snapshot.pointerModeToggled ? JNI_TRUE : JNI_FALSE), + static_cast(snapshot.menuButtonHeld ? JNI_TRUE : JNI_FALSE), + }; + env->SetBooleanArrayRegion(outFlags, 0, 3, flags); + + return snapshot.quickMenuClicked ? JNI_TRUE : JNI_FALSE; +} + +// Called from ImmersiveXrActivity's PixelCopy capture loop with the game's actual rendered +// frame (ARGB_8888 bitmap). Copies the pixels into the session's pending-frame buffer; the +// render thread uploads them to the GPU and draws them into the quad layer on its own. +JNIEXPORT void JNICALL +Java_app_gamenative_ui_screen_xr_XrNative_nativeSubmitFrame(JNIEnv *env, jclass, jlong handlePtr, + jobject bitmap) { + auto *handle = reinterpret_cast(handlePtr); + if (handle == nullptr) return; + + AndroidBitmapInfo info; + if (AndroidBitmap_getInfo(env, bitmap, &info) != ANDROID_BITMAP_RESULT_SUCCESS) return; + if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) return; + + void *pixels = nullptr; + if (AndroidBitmap_lockPixels(env, bitmap, &pixels) != ANDROID_BITMAP_RESULT_SUCCESS) return; + + handle->session->submitFrame(static_cast(pixels), static_cast(info.width), + static_cast(info.height)); + + AndroidBitmap_unlockPixels(env, bitmap); +} + +JNIEXPORT void JNICALL +Java_app_gamenative_ui_screen_xr_XrNative_nativeSetQuadTransform(JNIEnv *, jclass, jlong handlePtr, + jfloat x, jfloat y, jfloat z, + jfloat width, jfloat height, + jfloat contentScaleX, + jfloat contentScaleY) { + auto *handle = reinterpret_cast(handlePtr); + if (handle == nullptr) return; + handle->session->setQuadTransform(x, y, z, width, height, contentScaleX, contentScaleY); +} + +JNIEXPORT void JNICALL +Java_app_gamenative_ui_screen_xr_XrNative_nativeSetPassthroughEnabled(JNIEnv *, jclass, + jlong handlePtr, + jboolean enabled) { + auto *handle = reinterpret_cast(handlePtr); + if (handle == nullptr) return; + handle->session->setPassthroughEnabled(enabled == JNI_TRUE); +} + +// See DirectGLBridge.kt / GLRenderer.XrFrameBridge — hardwareBuffer is the same +// android.hardware.HardwareBuffer GLRenderer is writing into on its own thread/context. +JNIEXPORT void JNICALL +Java_app_gamenative_ui_screen_xr_XrNative_nativeSetSharedGameBuffer(JNIEnv *env, jclass, + jlong handlePtr, + jobject hardwareBuffer) { + auto *handle = reinterpret_cast(handlePtr); + if (handle == nullptr || hardwareBuffer == nullptr) return; + AHardwareBuffer *buffer = AHardwareBuffer_fromHardwareBuffer(env, hardwareBuffer); + if (buffer == nullptr) return; + handle->session->setSharedGameBuffer(buffer); +} + +// VulkanRenderer's zero-copy scanout path (DirectVulkanBridge.kt / VulkanXrFrameBridge) already +// has the game frame as a raw AHardwareBuffer* pointer (no android.hardware.HardwareBuffer Java +// object involved at all — VulkanRenderer.onUpdateWindowContent gets it straight from native +// GPUImage.getHardwareBufferPtr()), so this variant skips the Java-object-unwrapping step and +// reinterprets the pointer directly. Same underlying session method as the GL path above — +// setSharedGameBuffer()/importSharedBufferIfNeeded() don't care which renderer produced the +// buffer. +JNIEXPORT void JNICALL +Java_app_gamenative_ui_screen_xr_XrNative_nativeSetSharedGameBufferPtr(JNIEnv *, jclass, + jlong handlePtr, + jlong ahbPtr) { + auto *handle = reinterpret_cast(handlePtr); + if (handle == nullptr || ahbPtr == 0) return; + handle->session->setSharedGameBuffer(reinterpret_cast(ahbPtr)); +} + +// GLRenderer's direct-render path (DirectGLBridge.kt) needs to import the same HardwareBuffer as +// a GL texture in ITS OWN thread/context (a different context than the XR session's own thread +// above) — the public Android SDK has no Java binding for eglGetNativeClientBufferANDROID / +// eglCreateImageKHR (GLES11Ext's Java binding still takes a raw java.nio.Buffer, a legacy +// pre-HardwareBuffer calling convention), so this stateless helper does the same native EGLImage +// import dance and hands back a plain GL texture name the Java side can use normally. Must be +// called with an EGL context already current on the calling thread. +JNIEXPORT jint JNICALL +Java_com_winlator_renderer_GLHardwareBufferImporter_importAsTexture(JNIEnv *env, jclass, + jobject hardwareBuffer) { + if (hardwareBuffer == nullptr) return 0; + AHardwareBuffer *buffer = AHardwareBuffer_fromHardwareBuffer(env, hardwareBuffer); + if (buffer == nullptr) return 0; + + EGLDisplay display = eglGetCurrentDisplay(); + if (display == EGL_NO_DISPLAY) { + LOGE("GLHardwareBufferImporter: no current EGL display on this thread"); + return 0; + } + + EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID(buffer); + if (clientBuffer == nullptr) { + LOGE("GLHardwareBufferImporter: eglGetNativeClientBufferANDROID failed"); + return 0; + } + + const EGLint attrs[] = {EGL_NONE}; + EGLImageKHR image = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, + clientBuffer, attrs); + if (image == EGL_NO_IMAGE_KHR) { + LOGE("GLHardwareBufferImporter: eglCreateImageKHR failed (0x%x)", eglGetError()); + return 0; + } + + GLuint texture = 0; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glBindTexture(GL_TEXTURE_2D, 0); + + // The texture keeps the buffer's contents alive on the driver side; the EGLImage itself can + // be destroyed right after the glEGLImageTargetTexture2DOES call, same pattern used by + // Camera2/MediaCodec consumers. + eglDestroyImageKHR(display, image); + + LOGI("GLHardwareBufferImporter: imported HardwareBuffer as GL texture %u", texture); + return static_cast(texture); +} + +} // extern "C" diff --git a/app/src/main/cpp/xrimmersive/xr_immersive.cpp b/app/src/main/cpp/xrimmersive/xr_immersive.cpp new file mode 100644 index 0000000000..b11fddb65a --- /dev/null +++ b/app/src/main/cpp/xrimmersive/xr_immersive.cpp @@ -0,0 +1,1218 @@ +#include "xr_immersive.h" + +#include +#include +#include +#include +#include +#include +#include + +#define LOG_TAG "xrimmersive" +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) + +namespace xrimmersive { + +namespace { + +bool XrCheck(XrResult result, const char *what) { + if (XR_FAILED(result)) { + LOGE("%s failed: %d", what, static_cast(result)); + return false; + } + return true; +} + +bool IsInstanceExtensionSupported(const char *name) { + uint32_t count = 0; + xrEnumerateInstanceExtensionProperties(nullptr, 0, &count, nullptr); + std::vector properties(count, {XR_TYPE_EXTENSION_PROPERTIES}); + xrEnumerateInstanceExtensionProperties(nullptr, count, &count, properties.data()); + for (const auto &property : properties) { + if (std::strcmp(property.extensionName, name) == 0) return true; + } + return false; +} + +XrPosef IdentityPose() { + XrPosef pose; + pose.orientation.x = 0.0f; + pose.orientation.y = 0.0f; + pose.orientation.z = 0.0f; + pose.orientation.w = 1.0f; + pose.position.x = 0.0f; + pose.position.y = 0.0f; + pose.position.z = 0.0f; + return pose; +} + +} // namespace + +bool XrImmersiveSession::initialize(JavaVM *vm, jobject activityRef) { + vm_ = vm; + activityRef_ = activityRef; + + thread_ = std::thread([this] { runLoop(); }); + return true; +} + +void XrImmersiveSession::requestStop() { + stopRequested_.store(true); +} + +void XrImmersiveSession::join() { + if (thread_.joinable()) { + thread_.join(); + } +} + +InputSnapshot XrImmersiveSession::pollSnapshot() { + std::lock_guard lock(snapshotMutex_); + InputSnapshot result = snapshot_; + // Rising-edge flags are consumed on read. + snapshot_.quickMenuClicked = false; + snapshot_.pointerModeToggled = false; + return result; +} + +void XrImmersiveSession::submitFrame(const uint8_t *rgbaPixels, int32_t width, int32_t height) { + std::lock_guard lock(frameMutex_); + const size_t byteCount = static_cast(width) * static_cast(height) * 4; + pendingFramePixels_.resize(byteCount); + std::memcpy(pendingFramePixels_.data(), rgbaPixels, byteCount); + pendingFrameWidth_ = width; + pendingFrameHeight_ = height; + hasPendingFrame_ = true; +} + +void XrImmersiveSession::setSharedGameBuffer(AHardwareBuffer *buffer) { + std::lock_guard lock(sharedBufferMutex_); + if (pendingSharedBuffer_ != nullptr) { + AHardwareBuffer_release(pendingSharedBuffer_); + } + AHardwareBuffer_acquire(buffer); + pendingSharedBuffer_ = buffer; + sharedBufferChanged_ = true; +} + +// Must run on the render thread (this thread) — glEGLImageTargetTexture2DOES needs this +// thread's EGL context current. AHardwareBuffer's underlying memory is what's actually shared +// across contexts/processes; the EGLImage/texture wrapping it is per-context, so importing +// again here (GLRenderer already imported the same buffer once, into its own context) is +// expected and correct, not a duplicate/wasted step. +void XrImmersiveSession::importSharedBufferIfNeeded() { + AHardwareBuffer *buffer = nullptr; + { + std::lock_guard lock(sharedBufferMutex_); + if (!sharedBufferChanged_) return; + buffer = pendingSharedBuffer_; + sharedBufferChanged_ = false; + } + if (buffer == nullptr) return; + + if (sharedGameImage_ != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(eglDisplay_, sharedGameImage_); + sharedGameImage_ = EGL_NO_IMAGE_KHR; + } + + EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID(buffer); + if (clientBuffer == nullptr) { + LOGE("Immersive direct-render: eglGetNativeClientBufferANDROID failed"); + return; + } + + const EGLint attrs[] = {EGL_NONE}; + sharedGameImage_ = eglCreateImageKHR(eglDisplay_, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, + clientBuffer, attrs); + if (sharedGameImage_ == EGL_NO_IMAGE_KHR) { + LOGE("Immersive direct-render: eglCreateImageKHR failed (0x%x)", eglGetError()); + return; + } + + if (sharedGameTexture_ == 0) { + glGenTextures(1, &sharedGameTexture_); + } + glBindTexture(GL_TEXTURE_2D, sharedGameTexture_); + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, sharedGameImage_); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glBindTexture(GL_TEXTURE_2D, 0); + hasSharedGameTexture_ = true; + LOGI("Immersive direct-render: shared game buffer imported into XR thread's context (texture=%u)", + sharedGameTexture_); +} + +void XrImmersiveSession::runLoop() { + if (!setupInstanceAndSession()) { + LOGE("OpenXR setup failed, aborting immersive session thread"); + teardown(); + return; + } + + while (!stopRequested_.load()) { + pollXrEvents(); + + if (!sessionRunning_) { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + continue; + } + + XrFrameWaitInfo waitInfo{XR_TYPE_FRAME_WAIT_INFO}; + XrFrameState frameState{XR_TYPE_FRAME_STATE}; + if (!XrCheck(xrWaitFrame(session_, &waitInfo, &frameState), "xrWaitFrame")) break; + + XrFrameBeginInfo beginInfo{XR_TYPE_FRAME_BEGIN_INFO}; + if (!XrCheck(xrBeginFrame(session_, &beginInfo), "xrBeginFrame")) break; + + applyPendingPassthroughState(); + syncControllerInputs(frameState.predictedDisplayTime); + + if (frameState.shouldRender) { + renderFrame(); + submitQuadLayer(frameState.predictedDisplayTime, localSpace_, swapchain_, + kSwapchainWidth, kSwapchainHeight, true); + } else { + XrFrameEndInfo endInfo{XR_TYPE_FRAME_END_INFO}; + endInfo.displayTime = frameState.predictedDisplayTime; + endInfo.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE; + endInfo.layerCount = 0; + endInfo.layers = nullptr; + xrEndFrame(session_, &endInfo); + } + } + + teardown(); +} + +bool XrImmersiveSession::setupInstanceAndSession() { + // Android requires the loader to be explicitly initialized before xrCreateInstance. + PFN_xrInitializeLoaderKHR initializeLoader = nullptr; + xrGetInstanceProcAddr(XR_NULL_HANDLE, "xrInitializeLoaderKHR", + reinterpret_cast(&initializeLoader)); + if (initializeLoader == nullptr) { + LOGE("xrInitializeLoaderKHR not available"); + return false; + } + + XrLoaderInitInfoAndroidKHR loaderInitInfo{XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR}; + loaderInitInfo.applicationVM = vm_; + loaderInitInfo.applicationContext = activityRef_; + if (!XrCheck(initializeLoader(reinterpret_cast(&loaderInitInfo)), + "xrInitializeLoaderKHR")) { + return false; + } + + std::vector extensions = { + XR_KHR_ANDROID_CREATE_INSTANCE_EXTENSION_NAME, + XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME, + }; + passthroughExtensionAvailable_ = IsInstanceExtensionSupported(XR_FB_PASSTHROUGH_EXTENSION_NAME); + if (passthroughExtensionAvailable_) { + extensions.push_back(XR_FB_PASSTHROUGH_EXTENSION_NAME); + } else { + LOGI("XR_FB_passthrough not supported by this runtime — passthrough toggle will no-op"); + } + + // Perf/scheduling extensions GameNativeXR's libxr.so uses (confirmed via strings on the + // binary — standard OpenXR extensions, nothing proprietary) that this module never + // requested at all: without XR_EXT_performance_settings the runtime applies its own default + // (often conservative) CPU/GPU clock levels for the session; without + // XR_KHR_android_thread_settings, Horizon OS's scheduler has no hint which thread is the XR + // render thread and may not prioritize it; XR_FB_display_refresh_rate lets us target a + // lower refresh rate (less frame-budget pressure) instead of whatever the system default is. + perfSettingsExtensionAvailable_ = IsInstanceExtensionSupported(XR_EXT_PERFORMANCE_SETTINGS_EXTENSION_NAME); + if (perfSettingsExtensionAvailable_) extensions.push_back(XR_EXT_PERFORMANCE_SETTINGS_EXTENSION_NAME); + threadSettingsExtensionAvailable_ = IsInstanceExtensionSupported(XR_KHR_ANDROID_THREAD_SETTINGS_EXTENSION_NAME); + if (threadSettingsExtensionAvailable_) extensions.push_back(XR_KHR_ANDROID_THREAD_SETTINGS_EXTENSION_NAME); + refreshRateExtensionAvailable_ = IsInstanceExtensionSupported(XR_FB_DISPLAY_REFRESH_RATE_EXTENSION_NAME); + if (refreshRateExtensionAvailable_) extensions.push_back(XR_FB_DISPLAY_REFRESH_RATE_EXTENSION_NAME); + + XrInstanceCreateInfoAndroidKHR androidInfo{XR_TYPE_INSTANCE_CREATE_INFO_ANDROID_KHR}; + androidInfo.applicationVM = vm_; + androidInfo.applicationActivity = activityRef_; + + XrInstanceCreateInfo createInfo{XR_TYPE_INSTANCE_CREATE_INFO}; + createInfo.next = &androidInfo; + std::strncpy(createInfo.applicationInfo.applicationName, "GameNative", + XR_MAX_APPLICATION_NAME_SIZE - 1); + createInfo.applicationInfo.applicationVersion = 1; + std::strncpy(createInfo.applicationInfo.engineName, "GameNative", XR_MAX_ENGINE_NAME_SIZE - 1); + createInfo.applicationInfo.engineVersion = 1; + createInfo.applicationInfo.apiVersion = XR_CURRENT_API_VERSION; + createInfo.enabledExtensionCount = static_cast(extensions.size()); + createInfo.enabledExtensionNames = extensions.data(); + + if (!XrCheck(xrCreateInstance(&createInfo, &instance_), "xrCreateInstance")) return false; + + XrSystemGetInfo systemGetInfo{XR_TYPE_SYSTEM_GET_INFO}; + systemGetInfo.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY; + if (!XrCheck(xrGetSystem(instance_, &systemGetInfo, &systemId_), "xrGetSystem")) return false; + + // Passthrough needs ALPHA_BLEND — but requesting a blend mode xrEndFrame doesn't list as + // supported for this view config is a validation error (this runtime rejected every frame + // with xrEndFrame failing -1 once passthrough was toggled on, until this check was added). + uint32_t blendModeCount = 0; + xrEnumerateEnvironmentBlendModes(instance_, systemId_, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, + 0, &blendModeCount, nullptr); + if (blendModeCount > 0) { + std::vector blendModes(blendModeCount); + xrEnumerateEnvironmentBlendModes(instance_, systemId_, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, + blendModeCount, &blendModeCount, blendModes.data()); + for (auto mode : blendModes) { + if (mode == XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND) alphaBlendSupported_ = true; + } + } + if (!alphaBlendSupported_) { + LOGI("XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND not supported by this runtime — passthrough toggle will no-op"); + } + + PFN_xrGetOpenGLESGraphicsRequirementsKHR getGraphicsRequirements = nullptr; + xrGetInstanceProcAddr(instance_, "xrGetOpenGLESGraphicsRequirementsKHR", + reinterpret_cast(&getGraphicsRequirements)); + XrGraphicsRequirementsOpenGLESKHR graphicsRequirements{XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_ES_KHR}; + if (getGraphicsRequirements != nullptr) { + getGraphicsRequirements(instance_, systemId_, &graphicsRequirements); + } + + // --- EGL context, dedicated to this session (not yet shared with the app's own + // GLRenderer/DXVK-facing surface — see the header comment / plan follow-ups). --- + eglDisplay_ = eglGetDisplay(EGL_DEFAULT_DISPLAY); + EGLint eglMajor, eglMinor; + eglInitialize(eglDisplay_, &eglMajor, &eglMinor); + eglBindAPI(EGL_OPENGL_ES_API); + + const EGLint configAttribs[] = { + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR, + EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, + EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, + EGL_NONE, + }; + EGLint numConfigs = 0; + eglChooseConfig(eglDisplay_, configAttribs, &eglConfig_, 1, &numConfigs); + if (numConfigs == 0) { + LOGE("eglChooseConfig found no matching config"); + return false; + } + + const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE}; + eglContext_ = eglCreateContext(eglDisplay_, eglConfig_, EGL_NO_CONTEXT, contextAttribs); + + const EGLint pbufferAttribs[] = {EGL_WIDTH, 16, EGL_HEIGHT, 16, EGL_NONE}; + eglPbufferSurface_ = eglCreatePbufferSurface(eglDisplay_, eglConfig_, pbufferAttribs); + eglMakeCurrent(eglDisplay_, eglPbufferSurface_, eglPbufferSurface_, eglContext_); + + XrGraphicsBindingOpenGLESAndroidKHR graphicsBinding{XR_TYPE_GRAPHICS_BINDING_OPENGL_ES_ANDROID_KHR}; + graphicsBinding.display = eglDisplay_; + graphicsBinding.config = eglConfig_; + graphicsBinding.context = eglContext_; + + XrSessionCreateInfo sessionCreateInfo{XR_TYPE_SESSION_CREATE_INFO}; + sessionCreateInfo.next = &graphicsBinding; + sessionCreateInfo.systemId = systemId_; + if (!XrCheck(xrCreateSession(instance_, &sessionCreateInfo, &session_), "xrCreateSession")) { + return false; + } + + // Perf levels: request BOOST for both CPU and GPU domains — this game is running Box64 + // (x86 emulation) + Wine + our own screen-scraping capture on top of everything else, so + // there's no reason to accept the runtime's own default (often more conservative) clock + // levels for an immersive session that specifically exists to maximize performance. + if (perfSettingsExtensionAvailable_) { + PFN_xrPerfSettingsSetPerformanceLevelEXT setPerfLevel = nullptr; + xrGetInstanceProcAddr(instance_, "xrPerfSettingsSetPerformanceLevelEXT", + reinterpret_cast(&setPerfLevel)); + if (setPerfLevel != nullptr) { + // BOOST is documented as a short-burst level, not meant for continuous sustained + // rendering — requesting it for an entire session risks hitting thermal limits + // sooner, which the runtime then compensates for by throttling clocks back down, + // net WORSE and less consistent than just requesting the sustained level up front + // (confirmed by testing: frame pacing got worse after BOOST was added, not better). + XrCheck(setPerfLevel(session_, XR_PERF_SETTINGS_DOMAIN_CPU_EXT, XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT), + "xrPerfSettingsSetPerformanceLevelEXT(CPU)"); + XrCheck(setPerfLevel(session_, XR_PERF_SETTINGS_DOMAIN_GPU_EXT, XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT), + "xrPerfSettingsSetPerformanceLevelEXT(GPU)"); + } + } + + // Thread hint: tells Horizon OS's scheduler which OS thread is the XR render thread (this + // one — setupInstanceAndSession() runs on the dedicated thread created in initialize(), so + // gettid() here is correct) so it can prioritize it. Without this the scheduler has no + // signal that this thread's timing matters for frame delivery. + if (threadSettingsExtensionAvailable_) { + PFN_xrSetAndroidApplicationThreadKHR setThread = nullptr; + xrGetInstanceProcAddr(instance_, "xrSetAndroidApplicationThreadKHR", + reinterpret_cast(&setThread)); + if (setThread != nullptr) { + XrCheck(setThread(session_, XR_ANDROID_THREAD_TYPE_RENDERER_MAIN_KHR, gettid()), + "xrSetAndroidApplicationThreadKHR"); + } + } + + // Target 72Hz rather than whatever higher default the runtime picks (90/120Hz) — matches + // GameNativeXR's own default. Less frame-budget pressure for a pipeline (game render + + // PixelCopy readback + composite + texture upload) that doesn't have headroom to spare. + if (refreshRateExtensionAvailable_) { + PFN_xrRequestDisplayRefreshRateFB requestRefreshRate = nullptr; + xrGetInstanceProcAddr(instance_, "xrRequestDisplayRefreshRateFB", + reinterpret_cast(&requestRefreshRate)); + if (requestRefreshRate != nullptr) { + XrCheck(requestRefreshRate(session_, 72.0f), "xrRequestDisplayRefreshRateFB"); + } + } + + XrReferenceSpaceCreateInfo spaceCreateInfo{XR_TYPE_REFERENCE_SPACE_CREATE_INFO}; + spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL; + spaceCreateInfo.poseInReferenceSpace = IdentityPose(); + if (!XrCheck(xrCreateReferenceSpace(session_, &spaceCreateInfo, &localSpace_), + "xrCreateReferenceSpace")) { + return false; + } + + uint32_t formatCount = 0; + xrEnumerateSwapchainFormats(session_, 0, &formatCount, nullptr); + std::vector formats(formatCount); + xrEnumerateSwapchainFormats(session_, formatCount, &formatCount, formats.data()); + int64_t chosenFormat = formats.empty() ? 0x8058 /* GL_RGBA8 */ : formats[0]; + for (int64_t f : formats) { + if (f == 0x8058) { + chosenFormat = f; + break; + } + } + + XrSwapchainCreateInfo swapchainCreateInfo{XR_TYPE_SWAPCHAIN_CREATE_INFO}; + swapchainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_SAMPLED_BIT; + swapchainCreateInfo.format = chosenFormat; + swapchainCreateInfo.sampleCount = 1; + swapchainCreateInfo.width = kSwapchainWidth; + swapchainCreateInfo.height = kSwapchainHeight; + swapchainCreateInfo.faceCount = 1; + swapchainCreateInfo.arraySize = 1; + swapchainCreateInfo.mipCount = 1; + if (!XrCheck(xrCreateSwapchain(session_, &swapchainCreateInfo, &swapchain_), "xrCreateSwapchain")) { + return false; + } + + uint32_t imageCount = 0; + xrEnumerateSwapchainImages(swapchain_, 0, &imageCount, nullptr); + swapchainImages_.resize(imageCount); + for (auto &image : swapchainImages_) image.type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR; + xrEnumerateSwapchainImages( + swapchain_, imageCount, &imageCount, + reinterpret_cast(swapchainImages_.data())); + + glGenFramebuffers(1, &framebuffer_); + + // --- Input: action set + Meta Quest Touch controller bindings. --- + XrActionSetCreateInfo actionSetInfo{XR_TYPE_ACTION_SET_CREATE_INFO}; + std::strncpy(actionSetInfo.actionSetName, "gameplay", XR_MAX_ACTION_SET_NAME_SIZE - 1); + std::strncpy(actionSetInfo.localizedActionSetName, "Gameplay", XR_MAX_LOCALIZED_ACTION_SET_NAME_SIZE - 1); + actionSetInfo.priority = 0; + if (!XrCheck(xrCreateActionSet(instance_, &actionSetInfo, &actionSet_), "xrCreateActionSet")) { + return false; + } + + auto createAction = [this](const char *name, XrActionType type, XrAction *out) { + XrActionCreateInfo info{XR_TYPE_ACTION_CREATE_INFO}; + std::strncpy(info.actionName, name, XR_MAX_ACTION_NAME_SIZE - 1); + std::strncpy(info.localizedActionName, name, XR_MAX_LOCALIZED_ACTION_NAME_SIZE - 1); + info.actionType = type; + info.countSubactionPaths = 0; + XrCheck(xrCreateAction(actionSet_, &info, out), name); + }; + + createAction("button_x", XR_ACTION_TYPE_BOOLEAN_INPUT, &buttonXAction_); + createAction("button_y", XR_ACTION_TYPE_BOOLEAN_INPUT, &buttonYAction_); + createAction("button_a", XR_ACTION_TYPE_BOOLEAN_INPUT, &buttonAAction_); + createAction("button_b", XR_ACTION_TYPE_BOOLEAN_INPUT, &buttonBAction_); + createAction("squeeze_left", XR_ACTION_TYPE_FLOAT_INPUT, &squeezeLAction_); + createAction("squeeze_right", XR_ACTION_TYPE_FLOAT_INPUT, &squeezeRAction_); + createAction("trigger_left", XR_ACTION_TYPE_FLOAT_INPUT, &triggerLAction_); + createAction("trigger_right", XR_ACTION_TYPE_FLOAT_INPUT, &triggerRAction_); + createAction("thumbstick_left", XR_ACTION_TYPE_VECTOR2F_INPUT, &thumbstickLAction_); + createAction("thumbstick_right", XR_ACTION_TYPE_VECTOR2F_INPUT, &thumbstickRAction_); + createAction("thumbstick_left_click", XR_ACTION_TYPE_BOOLEAN_INPUT, &thumbstickLClickAction_); + createAction("thumbstick_right_click", XR_ACTION_TYPE_BOOLEAN_INPUT, &thumbstickRClickAction_); + createAction("menu_left", XR_ACTION_TYPE_BOOLEAN_INPUT, &menuLAction_); + createAction("aim_pose_left", XR_ACTION_TYPE_POSE_INPUT, &aimPoseLeftAction_); + createAction("aim_pose_right", XR_ACTION_TYPE_POSE_INPUT, &aimPoseRightAction_); + + auto path = [this](const char *p) { + XrPath result = XR_NULL_PATH; + xrStringToPath(instance_, p, &result); + return result; + }; + + std::vector bindings = { + {buttonXAction_, path("/user/hand/left/input/x/click")}, + {buttonYAction_, path("/user/hand/left/input/y/click")}, + {buttonAAction_, path("/user/hand/right/input/a/click")}, + {buttonBAction_, path("/user/hand/right/input/b/click")}, + {squeezeLAction_, path("/user/hand/left/input/squeeze/value")}, + {squeezeRAction_, path("/user/hand/right/input/squeeze/value")}, + {triggerLAction_, path("/user/hand/left/input/trigger/value")}, + {triggerRAction_, path("/user/hand/right/input/trigger/value")}, + {thumbstickLAction_, path("/user/hand/left/input/thumbstick")}, + {thumbstickRAction_, path("/user/hand/right/input/thumbstick")}, + {thumbstickLClickAction_, path("/user/hand/left/input/thumbstick/click")}, + {thumbstickRClickAction_, path("/user/hand/right/input/thumbstick/click")}, + {menuLAction_, path("/user/hand/left/input/menu/click")}, + {aimPoseLeftAction_, path("/user/hand/left/input/aim/pose")}, + {aimPoseRightAction_, path("/user/hand/right/input/aim/pose")}, + }; + + XrInteractionProfileSuggestedBinding suggestedBinding{XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING}; + suggestedBinding.interactionProfile = path("/interaction_profiles/oculus/touch_controller"); + suggestedBinding.countSuggestedBindings = static_cast(bindings.size()); + suggestedBinding.suggestedBindings = bindings.data(); + XrCheck(xrSuggestInteractionProfileBindings(instance_, &suggestedBinding), + "xrSuggestInteractionProfileBindings"); + + XrSessionActionSetsAttachInfo attachInfo{XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO}; + attachInfo.countActionSets = 1; + attachInfo.actionSets = &actionSet_; + XrCheck(xrAttachSessionActionSets(session_, &attachInfo), "xrAttachSessionActionSets"); + + XrActionSpaceCreateInfo aimSpaceInfo{XR_TYPE_ACTION_SPACE_CREATE_INFO}; + aimSpaceInfo.poseInActionSpace = IdentityPose(); + aimSpaceInfo.action = aimPoseLeftAction_; + XrCheck(xrCreateActionSpace(session_, &aimSpaceInfo, &aimSpaceLeft_), "xrCreateActionSpace(left aim)"); + aimSpaceInfo.action = aimPoseRightAction_; + XrCheck(xrCreateActionSpace(session_, &aimSpaceInfo, &aimSpaceRight_), "xrCreateActionSpace(right aim)"); + + if (passthroughExtensionAvailable_) { + setupPassthrough(); + } + + LOGI("OpenXR immersive session initialized (%dx%d quad, %u swapchain images)", + kSwapchainWidth, kSwapchainHeight, imageCount); + return true; +} + +void XrImmersiveSession::pollXrEvents() { + XrEventDataBuffer event{XR_TYPE_EVENT_DATA_BUFFER}; + while (instance_ != XR_NULL_HANDLE && xrPollEvent(instance_, &event) == XR_SUCCESS) { + if (event.type == XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED) { + auto *stateEvent = reinterpret_cast(&event); + sessionState_ = stateEvent->state; + LOGI("OpenXR session state changed: %d", static_cast(sessionState_)); + switch (sessionState_) { + case XR_SESSION_STATE_READY: { + XrSessionBeginInfo beginInfo{XR_TYPE_SESSION_BEGIN_INFO}; + beginInfo.primaryViewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; + XrCheck(xrBeginSession(session_, &beginInfo), "xrBeginSession"); + sessionRunning_ = true; + break; + } + case XR_SESSION_STATE_STOPPING: + xrEndSession(session_); + sessionRunning_ = false; + break; + case XR_SESSION_STATE_EXITING: + case XR_SESSION_STATE_LOSS_PENDING: + stopRequested_.store(true); + break; + default: + break; + } + } + event.type = XR_TYPE_EVENT_DATA_BUFFER; + } +} + +namespace { +constexpr char kQuadVertexShader[] = + "#version 300 es\n" + "layout(location = 0) in vec2 aPosition;\n" + "layout(location = 1) in vec2 aTexCoord;\n" + "out vec2 vTexCoord;\n" + "void main() {\n" + " vTexCoord = aTexCoord;\n" + " gl_Position = vec4(aPosition, 0.0, 1.0);\n" + "}\n"; + +constexpr char kQuadFragmentShader[] = + "#version 300 es\n" + "precision mediump float;\n" + "in vec2 vTexCoord;\n" + "out vec4 fragColor;\n" + "uniform sampler2D uTexture;\n" + "void main() {\n" + " fragColor = texture(uTexture, vTexCoord);\n" + "}\n"; + +// Direct-render path: uGameTexture is the shared-buffer texture GLRenderer writes into on its +// own thread (always opaque, drawn as the base); uOverlayTexture is the same CPU-uploaded +// menu/HUD layer as the non-direct path, alpha-blended on top — same visual result as the +// non-direct path's Kotlin-side Canvas composite, just with the base layer sourced from a +// shared GPU buffer instead of a PixelCopy'd CPU bitmap. +// +// uContentScale: the quad itself is grown by a margin band (pointer-mode resize/move handles +// live there — see POINTER_BORDER_MARGIN_METERS in ImmersiveXrActivity.kt), but uGameTexture is +// the raw game frame at its own native resolution with no margin baked in — sampling it at +// vTexCoord directly (spanning the WHOLE, margin-grown quad) would stretch it into the margin. +// uContentScale is content-half-extent / quad-half-extent per axis (< 1.0 whenever a margin is +// present); remapping vTexCoord by it recovers the sub-rectangle of the quad's UV space that is +// actually "the game", and anything outside that rectangle (the margin) is treated as if the +// game layer were fully transparent there — only uOverlayTexture (which DOES span the whole +// quad, including the margin — same bitmap the non-direct path already draws its handles/cursor +// into) shows through, at ITS OWN alpha, matching the non-direct path's compositing model. +constexpr char kDirectQuadFragmentShader[] = + "#version 300 es\n" + "precision mediump float;\n" + "in vec2 vTexCoord;\n" + "out vec4 fragColor;\n" + "uniform sampler2D uGameTexture;\n" + "uniform sampler2D uOverlayTexture;\n" + "uniform vec2 uContentScale;\n" + "void main() {\n" + " vec2 gameUv = (vTexCoord - 0.5) / uContentScale + 0.5;\n" + " bool insideGame = gameUv.x >= 0.0 && gameUv.x <= 1.0 && gameUv.y >= 0.0 && gameUv.y <= 1.0;\n" + " vec4 game = insideGame ? texture(uGameTexture, gameUv) : vec4(0.0);\n" + " vec4 overlay = texture(uOverlayTexture, vTexCoord);\n" + " vec3 rgb = mix(game.rgb, overlay.rgb, overlay.a);\n" + " float alpha = insideGame ? 1.0 : overlay.a;\n" + " fragColor = vec4(rgb, alpha);\n" + "}\n"; + +GLuint CompileShader(GLenum type, const char *source) { + GLuint shader = glCreateShader(type); + glShaderSource(shader, 1, &source, nullptr); + glCompileShader(shader); + GLint compiled = 0; + glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); + if (!compiled) { + char log[512]; + glGetShaderInfoLog(shader, sizeof(log), nullptr, log); + LOGE("Shader compile error: %s", log); + glDeleteShader(shader); + return 0; + } + return shader; +} +} // namespace + +void XrImmersiveSession::ensureQuadGeometryAndShader() { + if (quadProgram_ != 0) return; + + GLuint vertexShader = CompileShader(GL_VERTEX_SHADER, kQuadVertexShader); + GLuint fragmentShader = CompileShader(GL_FRAGMENT_SHADER, kQuadFragmentShader); + if (vertexShader == 0 || fragmentShader == 0) return; + + quadProgram_ = glCreateProgram(); + glAttachShader(quadProgram_, vertexShader); + glAttachShader(quadProgram_, fragmentShader); + glLinkProgram(quadProgram_); + GLint linked = 0; + glGetProgramiv(quadProgram_, GL_LINK_STATUS, &linked); + if (!linked) { + char log[512]; + glGetProgramInfoLog(quadProgram_, sizeof(log), nullptr, log); + LOGE("Shader link error: %s", log); + } + glDeleteShader(vertexShader); + glDeleteShader(fragmentShader); + + quadPositionLoc_ = 0; + quadTexCoordLoc_ = 1; + quadSamplerLoc_ = glGetUniformLocation(quadProgram_, "uTexture"); + + // Full-screen quad (2 triangles as a strip): xy in clip space, uv in texture space. + // v=0 maps to the bitmap's first (top) row, v=1 to its last (bottom) row — Android + // Bitmaps are stored top-down, so this should show right-side-up; flip here if a + // device test shows it upside down. + const float vertices[] = { + // x, y, u, v + -1.0f, -1.0f, 0.0f, 1.0f, + 1.0f, -1.0f, 1.0f, 1.0f, + -1.0f, 1.0f, 0.0f, 0.0f, + 1.0f, 1.0f, 1.0f, 0.0f, + }; + glGenBuffers(1, &quadVbo_); + glBindBuffer(GL_ARRAY_BUFFER, quadVbo_); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glGenTextures(1, &gameTexture_); + glBindTexture(GL_TEXTURE_2D, gameTexture_); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glBindTexture(GL_TEXTURE_2D, 0); + + GLuint directVertexShader = CompileShader(GL_VERTEX_SHADER, kQuadVertexShader); + GLuint directFragmentShader = CompileShader(GL_FRAGMENT_SHADER, kDirectQuadFragmentShader); + if (directVertexShader != 0 && directFragmentShader != 0) { + directQuadProgram_ = glCreateProgram(); + glAttachShader(directQuadProgram_, directVertexShader); + glAttachShader(directQuadProgram_, directFragmentShader); + glLinkProgram(directQuadProgram_); + GLint directLinked = 0; + glGetProgramiv(directQuadProgram_, GL_LINK_STATUS, &directLinked); + if (!directLinked) { + char log[512]; + glGetProgramInfoLog(directQuadProgram_, sizeof(log), nullptr, log); + LOGE("Direct quad shader link error: %s", log); + } + directQuadPositionLoc_ = 0; + directQuadTexCoordLoc_ = 1; + directGameSamplerLoc_ = glGetUniformLocation(directQuadProgram_, "uGameTexture"); + directOverlaySamplerLoc_ = glGetUniformLocation(directQuadProgram_, "uOverlayTexture"); + directContentScaleLoc_ = glGetUniformLocation(directQuadProgram_, "uContentScale"); + } + glDeleteShader(directVertexShader); + glDeleteShader(directFragmentShader); +} + +void XrImmersiveSession::uploadPendingGameFrameLocked() { + std::lock_guard lock(frameMutex_); + if (!hasPendingFrame_) return; + + glBindTexture(GL_TEXTURE_2D, gameTexture_); + if (pendingFrameWidth_ != gameTextureWidth_ || pendingFrameHeight_ != gameTextureHeight_) { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, pendingFrameWidth_, pendingFrameHeight_, 0, + GL_RGBA, GL_UNSIGNED_BYTE, pendingFramePixels_.data()); + gameTextureWidth_ = pendingFrameWidth_; + gameTextureHeight_ = pendingFrameHeight_; + } else { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, pendingFrameWidth_, pendingFrameHeight_, GL_RGBA, + GL_UNSIGNED_BYTE, pendingFramePixels_.data()); + } + glBindTexture(GL_TEXTURE_2D, 0); + hasPendingFrame_ = false; +} + +void XrImmersiveSession::renderFrame() { + XrSwapchainImageAcquireInfo acquireInfo{XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO}; + uint32_t imageIndex = 0; + if (!XrCheck(xrAcquireSwapchainImage(swapchain_, &acquireInfo, &imageIndex), + "xrAcquireSwapchainImage")) { + return; + } + + XrSwapchainImageWaitInfo waitInfo{XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO}; + waitInfo.timeout = XR_INFINITE_DURATION; + xrWaitSwapchainImage(swapchain_, &waitInfo); + + ensureQuadGeometryAndShader(); + importSharedBufferIfNeeded(); + uploadPendingGameFrameLocked(); + + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + swapchainImages_[imageIndex].image, 0); + glViewport(0, 0, kSwapchainWidth, kSwapchainHeight); + + if (hasSharedGameTexture_ && directQuadProgram_ != 0) { + // GLRenderer wrote this frame's game image straight into sharedGameTexture_ on its own + // thread (zero CPU copy) — gameTexture_ here now carries just the overlay (menu/HUD), + // still fed the same way as the non-direct path (uploadPendingGameFrameLocked, above). + static int directFrameLogCounter = 0; + if (++directFrameLogCounter % 300 == 1) { + LOGI("Immersive direct-render: compositing shared game texture + overlay (frame #%d)", + directFrameLogCounter); + } + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + glUseProgram(directQuadProgram_); + glBindBuffer(GL_ARRAY_BUFFER, quadVbo_); + glEnableVertexAttribArray(directQuadPositionLoc_); + glVertexAttribPointer(directQuadPositionLoc_, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), + reinterpret_cast(0)); + glEnableVertexAttribArray(directQuadTexCoordLoc_); + glVertexAttribPointer(directQuadTexCoordLoc_, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), + reinterpret_cast(2 * sizeof(float))); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, sharedGameTexture_); + glUniform1i(directGameSamplerLoc_, 0); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, gameTexture_); + glUniform1i(directOverlaySamplerLoc_, 1); + glUniform2f(directContentScaleLoc_, quadContentScaleX_.load(), quadContentScaleY_.load()); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, 0); + glUseProgram(0); + } else if (gameTextureWidth_ > 0 && quadProgram_ != 0) { + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + glUseProgram(quadProgram_); + glBindBuffer(GL_ARRAY_BUFFER, quadVbo_); + glEnableVertexAttribArray(quadPositionLoc_); + glVertexAttribPointer(quadPositionLoc_, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), + reinterpret_cast(0)); + glEnableVertexAttribArray(quadTexCoordLoc_); + glVertexAttribPointer(quadTexCoordLoc_, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), + reinterpret_cast(2 * sizeof(float))); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, gameTexture_); + glUniform1i(quadSamplerLoc_, 0); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindTexture(GL_TEXTURE_2D, 0); + glUseProgram(0); + } else { + // No game frame captured yet (e.g. libxrimmersive.so present but the app hasn't + // reached ImmersiveXrActivity's capture loop yet) — flat color instead of garbage. + glClearColor(0.05f, 0.05f, 0.08f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + XrSwapchainImageReleaseInfo releaseInfo{XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO}; + xrReleaseSwapchainImage(swapchain_, &releaseInfo); +} + +void XrImmersiveSession::submitQuadLayer(XrTime predictedDisplayTime, XrSpace space, + XrSwapchain swapchain, int32_t width, int32_t height, + bool sessionActive) { + if (!sessionActive) return; + + XrCompositionLayerQuad quad{XR_TYPE_COMPOSITION_LAYER_QUAD}; + // Without this flag the compositor ignores the texture's alpha channel entirely and treats + // every pixel as fully opaque — the composited bitmap already has real alpha=0 in the + // pointer-margin band (Kotlin side clears it to transparent before drawing the game frame + // inset), but with no blend flag those pixels' RGB (0,0,0, i.e. black) still render at full + // opacity, which is exactly the black border the passthrough toggle was supposed to see + // through. This makes the quad respect its own alpha, so the passthrough layer behind it + // (when active) shows through those pixels instead. + quad.layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT; + quad.space = space; + quad.eyeVisibility = XR_EYE_VISIBILITY_BOTH; + quad.subImage.swapchain = swapchain; + quad.subImage.imageRect.offset.x = 0; + quad.subImage.imageRect.offset.y = 0; + quad.subImage.imageRect.extent.width = width; + quad.subImage.imageRect.extent.height = height; + quad.subImage.imageArrayIndex = 0; + // Orbit around the player on BOTH axes instead of sliding on a flat plane: quadPosX_/quadPosY_ + // are tangential (left/right, up/down) offsets and quadPosZ_ is always -distance (Kotlin + // never sends a raw Cartesian z), so distance = -quadPosZ_ is the orbit radius, yaw = + // atan2(quadPosX_, distance) is the angle swept left/right, and pitch = atan2(quadPosY_, + // distance) is the angle swept up/down. Position moves on the SPHERE of that radius (not a + // flat plane), and orientation rotates to keep facing the player at every point on it — + // moving the panel in any direction now feels like walking it around the player rather than + // sliding a flat sign past them, including vertically (an earlier version only did this for + // yaw/left-right, leaving up/down as a plain Cartesian translation that visibly turned away). + // + // Derivation: at yaw=pitch=0 (centered), identity orientation already faces the player (the + // pre-existing, known-working baseline) — so the quad's local +Z axis is its "faces the + // player" normal, and local +X/+Y are its right/up axes. Composing a yaw (about world Y) with + // a pitch (about the quad's own, already-yawed local X) and solving for "local +Z ends up + // pointing from the new position back to the player" gives position = distance * + // (sin(yaw)cos(pitch), sin(pitch), -cos(yaw)cos(pitch)) and orientation quaternion (using + // half-angles cy/sy for yaw, cx/sx for pitch, with the same yaw-sign convention as the + // yaw-only case): (cy*sx, -sy*cx, sy*sx, cy*cx) — reduces exactly to the old yaw-only + // quaternion (0, -sy, 0, cy) when pitch=0, confirming the two derivations agree. + const float distance = -quadPosZ_.load(); + const float tangentialX = quadPosX_.load(); + const float tangentialY = quadPosY_.load(); + const float yaw = distance > 0.0001f ? std::atan2(tangentialX, distance) : 0.0f; + const float pitch = distance > 0.0001f ? std::atan2(tangentialY, distance) : 0.0f; + const float cy = std::cos(yaw * 0.5f); + const float sy = std::sin(yaw * 0.5f); + const float cx = std::cos(pitch * 0.5f); + const float sx = std::sin(pitch * 0.5f); + quad.pose = IdentityPose(); + quad.pose.orientation.x = cy * sx; + quad.pose.orientation.y = -sy * cx; + quad.pose.orientation.z = sy * sx; + quad.pose.orientation.w = cy * cx; + quad.pose.position.x = distance * std::sin(yaw) * std::cos(pitch); + quad.pose.position.y = distance * std::sin(pitch); + quad.pose.position.z = -distance * std::cos(yaw) * std::cos(pitch); + quad.size.width = quadWidth_.load(); + quad.size.height = quadHeight_.load(); + + // Unlike the quad, a passthrough layer isn't anchored to a location in the world — it + // replaces the whole environment/background — so per Meta's own samples it takes + // XR_NULL_HANDLE here, not a real reference space. Passing our local reference space (a + // valid, non-null XrSpace meant for spatially-anchored layers like the quad) is a plausible + // cause of the xrEndFrame validation failure (-1) seen on every frame once passthrough was + // toggled on, previously misdiagnosed as an unsupported blend mode. + XrCompositionLayerPassthroughFB passthroughLayer{XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_FB}; + passthroughLayer.space = XR_NULL_HANDLE; + passthroughLayer.layerHandle = passthroughLayer_; + + std::vector layers; + if (passthroughActive_ && passthroughLayer_ != XR_NULL_HANDLE) { + layers.push_back(reinterpret_cast(&passthroughLayer)); + } + layers.push_back(reinterpret_cast(&quad)); + + // Passthrough shows the real world through gaps, so the "background" must be + // ADDITIVE/ALPHA_BLEND rather than OPAQUE while it's active, or it'll just look black. + XrFrameEndInfo endInfo{XR_TYPE_FRAME_END_INFO}; + endInfo.displayTime = predictedDisplayTime; + endInfo.environmentBlendMode = passthroughActive_ ? XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND + : XR_ENVIRONMENT_BLEND_MODE_OPAQUE; + endInfo.layerCount = static_cast(layers.size()); + endInfo.layers = layers.data(); + XrCheck(xrEndFrame(session_, &endInfo), "xrEndFrame"); +} + +void XrImmersiveSession::setQuadTransform(float x, float y, float z, float width, float height, + float contentScaleX, float contentScaleY) { + quadPosX_.store(x); + quadPosY_.store(y); + quadPosZ_.store(z); + quadWidth_.store(width); + quadHeight_.store(height); + quadContentScaleX_.store(contentScaleX); + quadContentScaleY_.store(contentScaleY); +} + +void XrImmersiveSession::setPassthroughEnabled(bool enabled) { + passthroughRequested_.store(enabled); +} + +void XrImmersiveSession::setupPassthrough() { + auto resolve = [this](const char *name, PFN_xrVoidFunction *out) { + xrGetInstanceProcAddr(instance_, name, out); + }; + resolve("xrCreatePassthroughFB", reinterpret_cast(&xrCreatePassthroughFB_)); + resolve("xrDestroyPassthroughFB", reinterpret_cast(&xrDestroyPassthroughFB_)); + resolve("xrPassthroughStartFB", reinterpret_cast(&xrPassthroughStartFB_)); + resolve("xrPassthroughPauseFB", reinterpret_cast(&xrPassthroughPauseFB_)); + resolve("xrCreatePassthroughLayerFB", + reinterpret_cast(&xrCreatePassthroughLayerFB_)); + resolve("xrDestroyPassthroughLayerFB", + reinterpret_cast(&xrDestroyPassthroughLayerFB_)); + resolve("xrPassthroughLayerResumeFB", + reinterpret_cast(&xrPassthroughLayerResumeFB_)); + resolve("xrPassthroughLayerPauseFB", + reinterpret_cast(&xrPassthroughLayerPauseFB_)); + + if (xrCreatePassthroughFB_ == nullptr || xrCreatePassthroughLayerFB_ == nullptr) { + LOGE("XR_FB_passthrough advertised but function pointers missing — disabling"); + passthroughExtensionAvailable_ = false; + return; + } + + XrPassthroughCreateInfoFB passthroughCreateInfo{XR_TYPE_PASSTHROUGH_CREATE_INFO_FB}; + if (!XrCheck(xrCreatePassthroughFB_(session_, &passthroughCreateInfo, &passthrough_), + "xrCreatePassthroughFB")) { + passthroughExtensionAvailable_ = false; + return; + } + + XrPassthroughLayerCreateInfoFB layerCreateInfo{XR_TYPE_PASSTHROUGH_LAYER_CREATE_INFO_FB}; + layerCreateInfo.passthrough = passthrough_; + layerCreateInfo.purpose = XR_PASSTHROUGH_LAYER_PURPOSE_RECONSTRUCTION_FB; + if (!XrCheck(xrCreatePassthroughLayerFB_(session_, &layerCreateInfo, &passthroughLayer_), + "xrCreatePassthroughLayerFB")) { + passthroughExtensionAvailable_ = false; + return; + } + + LOGI("Passthrough initialized (inactive until toggled on)"); +} + +void XrImmersiveSession::teardownPassthrough() { + if (passthroughLayer_ != XR_NULL_HANDLE && xrDestroyPassthroughLayerFB_ != nullptr) { + xrDestroyPassthroughLayerFB_(passthroughLayer_); + passthroughLayer_ = XR_NULL_HANDLE; + } + if (passthrough_ != XR_NULL_HANDLE && xrDestroyPassthroughFB_ != nullptr) { + xrDestroyPassthroughFB_(passthrough_); + passthrough_ = XR_NULL_HANDLE; + } +} + +void XrImmersiveSession::applyPendingPassthroughState() { + if (!passthroughExtensionAvailable_) return; + if (!alphaBlendSupported_) { + passthroughRequested_.store(false); + return; + } + const bool requested = passthroughRequested_.load(); + if (requested == passthroughActive_) return; + + if (requested) { + // Only report "active" (and thus switch to ALPHA_BLEND + attach the passthrough layer in + // submitQuadLayer) if the runtime actually started passthrough — otherwise we'd flip to + // ALPHA_BLEND with no real passthrough image behind it, which just looks like a black + // screen (nothing bridges the gap where OPAQUE mode's implicit skybox used to be). + const bool started = XrCheck(xrPassthroughStartFB_(passthrough_), "xrPassthroughStartFB") && + XrCheck(xrPassthroughLayerResumeFB_(passthroughLayer_), "xrPassthroughLayerResumeFB"); + if (!started) { + LOGE("Passthrough failed to start — leaving environment opaque"); + passthroughRequested_.store(false); + } + passthroughActive_ = started; + } else { + XrCheck(xrPassthroughLayerPauseFB_(passthroughLayer_), "xrPassthroughLayerPauseFB"); + XrCheck(xrPassthroughPauseFB_(passthrough_), "xrPassthroughPauseFB"); + passthroughActive_ = false; + } +} + +void XrImmersiveSession::syncControllerInputs(XrTime predictedDisplayTime) { + XrActiveActionSet activeActionSet{actionSet_, XR_NULL_PATH}; + XrActionsSyncInfo syncInfo{XR_TYPE_ACTIONS_SYNC_INFO}; + syncInfo.countActiveActionSets = 1; + syncInfo.activeActionSets = &activeActionSet; + if (!XrCheck(xrSyncActions(session_, &syncInfo), "xrSyncActions")) return; + + auto getBool = [this](XrAction action) { + XrActionStateGetInfo info{XR_TYPE_ACTION_STATE_GET_INFO}; + info.action = action; + XrActionStateBoolean state{XR_TYPE_ACTION_STATE_BOOLEAN}; + xrGetActionStateBoolean(session_, &info, &state); + return state.isActive && state.currentState; + }; + auto getFloat = [this](XrAction action) { + XrActionStateGetInfo info{XR_TYPE_ACTION_STATE_GET_INFO}; + info.action = action; + XrActionStateFloat state{XR_TYPE_ACTION_STATE_FLOAT}; + xrGetActionStateFloat(session_, &info, &state); + return state.isActive ? state.currentState : 0.0f; + }; + auto getVector2 = [this](XrAction action, float *x, float *y) { + XrActionStateGetInfo info{XR_TYPE_ACTION_STATE_GET_INFO}; + info.action = action; + XrActionStateVector2f state{XR_TYPE_ACTION_STATE_VECTOR2F}; + xrGetActionStateVector2f(session_, &info, &state); + if (state.isActive) { + *x = state.currentState.x; + *y = state.currentState.y; + } else { + *x = *y = 0.0f; + } + }; + + InputSnapshot next; + next.buttons = 0; + if (getBool(buttonXAction_)) next.buttons |= (1u << BUTTON_X); + if (getBool(buttonYAction_)) next.buttons |= (1u << BUTTON_Y); + if (getBool(buttonAAction_)) next.buttons |= (1u << BUTTON_A); + if (getBool(buttonBAction_)) next.buttons |= (1u << BUTTON_B); + if (getFloat(squeezeLAction_) > 0.5f) next.buttons |= (1u << BUTTON_LB); + if (getFloat(squeezeRAction_) > 0.5f) next.buttons |= (1u << BUTTON_RB); + + // Menu/Start button: a short press still forwards a normal Start press to the game (for + // the game's own pause/settings menu); holding it for 600ms instead opens the immersive + // quick menu. The raw OpenXR click can bounce (several false->true transitions within a + // single physical press), so lastMenuDebouncedPressed_ applies a short bounce-tolerance + // window before measuring hold duration — a millisecond-scale dropout mid-hold must not + // reset the 600ms timer. + const auto nowMenu = std::chrono::steady_clock::now(); + const bool rawMenuPressed = getBool(menuLAction_); + if (rawMenuPressed) lastMenuRawTrueTime_ = nowMenu; + const bool debouncedMenuPressed = rawMenuPressed || + (nowMenu - lastMenuRawTrueTime_) < std::chrono::milliseconds(50); + if (debouncedMenuPressed && !lastMenuDebouncedPressed_) { + menuHoldStartTime_ = nowMenu; + menuLongPressTriggered_ = false; + } + if (debouncedMenuPressed) { + if (!menuLongPressTriggered_ && + (nowMenu - menuHoldStartTime_) >= std::chrono::milliseconds(600)) { + next.quickMenuClicked = true; + menuLongPressTriggered_ = true; + } + } else if (lastMenuDebouncedPressed_ && !menuLongPressTriggered_) { + // Released before reaching the long-press threshold: forward a clean momentary Start + // press to the game, same as a real controller's Menu button. + menuDebouncedActive_ = true; + menuPressStartTime_ = nowMenu; + } + lastMenuDebouncedPressed_ = debouncedMenuPressed; + next.menuButtonHeld = debouncedMenuPressed; + if (menuDebouncedActive_) { + if ((nowMenu - menuPressStartTime_) < std::chrono::milliseconds(150)) { + next.buttons |= (1u << BUTTON_START); + } else { + menuDebouncedActive_ = false; + } + } + + const bool l3Pressed = getBool(thumbstickLClickAction_); + const bool r3Pressed = getBool(thumbstickRClickAction_); + if (l3Pressed) next.buttons |= (1u << BUTTON_L3); + if (r3Pressed) next.buttons |= (1u << BUTTON_R3); + + next.triggerL = getFloat(triggerLAction_); + next.triggerR = getFloat(triggerRAction_); + getVector2(thumbstickLAction_, &next.leftX, &next.leftY); + getVector2(thumbstickRAction_, &next.rightX, &next.rightY); + // Confirmed by testing: this OpenXR runtime's thumbstick Y is inverted relative to what + // WinHandler's shared-memory XInput buffer expects (positive Y = down here, but the buffer + // wants positive Y = up, same as a real Xbox controller) — up/down were swapped in-game. + // Only the left stick was confirmed broken; negating both since they're read the same way + // and almost certainly share the same convention. + next.leftY = -next.leftY; + next.rightY = -next.rightY; + // No synthetic dpad: on a real Xbox controller the left stick and the d-pad are two + // separate physical controls, and Touch controllers have no physical d-pad at all. Faking + // one out of the stick's direction fired alongside the real analog values, which is why + // stick movement felt like the d-pad instead of a proper analog Xbox stick. leftX/leftY/ + // rightX/rightY carry the real analog values; dpadUp/Down/Left/Right stay false. + + // XR pointer-mode toggle: double-click of either thumbstick (L3 or R3) — checked + // independently rather than requiring both at once, since it's easier to double-click one + // stick with one hand than to coordinate a multi-button chord. Reserved for grabbing the + // quad's corner (resize) / top-bottom bar (reposition), and later a real VR game's own + // pointer needs. + const auto now = std::chrono::steady_clock::now(); + auto detectDoubleClick = [&](bool pressed, bool &lastPressed, + std::chrono::steady_clock::time_point &lastClickTime, + int &clickCount) { + bool triggered = false; + if (pressed && !lastPressed) { + clickCount = (now - lastClickTime) < std::chrono::milliseconds(400) ? clickCount + 1 : 1; + lastClickTime = now; + if (clickCount >= 2) { + triggered = true; + clickCount = 0; + } + } + lastPressed = pressed; + return triggered; + }; + const bool leftDoubleClick = detectDoubleClick(l3Pressed, lastL3Pressed_, lastL3ClickTime_, l3ClickCount_); + const bool rightDoubleClick = detectDoubleClick(r3Pressed, lastR3Pressed_, lastR3ClickTime_, r3ClickCount_); + next.pointerModeToggled = leftDoubleClick || rightDoubleClick; + + // Aim-pose ray for each hand, in the same LOCAL space the quad transform lives in — lets + // Kotlin do a simple ray/plane intersection against the quad's known rectangle instead of + // needing any OpenXR types on that side. + auto rotateForward = [](const XrQuaternionf &q) { + // Standard quaternion-rotate-vector for v=(0,0,-1) (OpenXR's forward), expanded by hand + // since -1 in one component simplifies most of the cross-product terms away. + const float vx = 0.0f, vy = 0.0f, vz = -1.0f; + const float tx = 2.0f * (q.y * vz - q.z * vy); + const float ty = 2.0f * (q.z * vx - q.x * vz); + const float tz = 2.0f * (q.x * vy - q.y * vx); + const float rx = vx + q.w * tx + (q.y * tz - q.z * ty); + const float ry = vy + q.w * ty + (q.z * tx - q.x * tz); + const float rz = vz + q.w * tz + (q.x * ty - q.y * tx); + return std::array{rx, ry, rz}; + }; + + XrSpaceLocation leftLoc{XR_TYPE_SPACE_LOCATION}; + XrSpaceLocation rightLoc{XR_TYPE_SPACE_LOCATION}; + constexpr XrSpaceLocationFlags kPoseValidBits = + XR_SPACE_LOCATION_POSITION_VALID_BIT | XR_SPACE_LOCATION_ORIENTATION_VALID_BIT; + const bool haveLeft = aimSpaceLeft_ != XR_NULL_HANDLE && + XrCheck(xrLocateSpace(aimSpaceLeft_, localSpace_, predictedDisplayTime, &leftLoc), "xrLocateSpace(left)") && + (leftLoc.locationFlags & kPoseValidBits) == kPoseValidBits; + const bool haveRight = aimSpaceRight_ != XR_NULL_HANDLE && + XrCheck(xrLocateSpace(aimSpaceRight_, localSpace_, predictedDisplayTime, &rightLoc), "xrLocateSpace(right)") && + (rightLoc.locationFlags & kPoseValidBits) == kPoseValidBits; + next.handPosesValid = haveLeft && haveRight; + if (haveLeft) { + next.leftHandPosX = leftLoc.pose.position.x; + next.leftHandPosY = leftLoc.pose.position.y; + next.leftHandPosZ = leftLoc.pose.position.z; + const auto fwd = rotateForward(leftLoc.pose.orientation); + next.leftHandFwdX = fwd[0]; + next.leftHandFwdY = fwd[1]; + next.leftHandFwdZ = fwd[2]; + } + if (haveRight) { + next.rightHandPosX = rightLoc.pose.position.x; + next.rightHandPosY = rightLoc.pose.position.y; + next.rightHandPosZ = rightLoc.pose.position.z; + const auto fwd = rotateForward(rightLoc.pose.orientation); + next.rightHandFwdX = fwd[0]; + next.rightHandFwdY = fwd[1]; + next.rightHandFwdZ = fwd[2]; + } + + // Rate-limited raw-value dump (~every 0.5s at 90Hz) — temporary, for diagnosing controller + // mapping reports; safe to remove once the analog stick mapping is confirmed solid. + if ((syncFrameCounter_++ % 45) == 0) { + LOGI("controller raw: leftX=%.2f leftY=%.2f rightX=%.2f rightY=%.2f buttons=0x%03x", + next.leftX, next.leftY, next.rightX, next.rightY, next.buttons); + } + + std::lock_guard lock(snapshotMutex_); + // Preserve a quick-menu click that a concurrent pollSnapshot() hasn't consumed yet. + next.quickMenuClicked = next.quickMenuClicked || snapshot_.quickMenuClicked; + snapshot_ = next; +} + +void XrImmersiveSession::teardown() { + teardownPassthrough(); + if (gameTexture_ != 0) { + glDeleteTextures(1, &gameTexture_); + gameTexture_ = 0; + } + if (quadVbo_ != 0) { + glDeleteBuffers(1, &quadVbo_); + quadVbo_ = 0; + } + if (quadProgram_ != 0) { + glDeleteProgram(quadProgram_); + quadProgram_ = 0; + } + if (directQuadProgram_ != 0) { + glDeleteProgram(directQuadProgram_); + directQuadProgram_ = 0; + } + if (sharedGameTexture_ != 0) { + glDeleteTextures(1, &sharedGameTexture_); + sharedGameTexture_ = 0; + } + if (sharedGameImage_ != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(eglDisplay_, sharedGameImage_); + sharedGameImage_ = EGL_NO_IMAGE_KHR; + } + { + std::lock_guard lock(sharedBufferMutex_); + if (pendingSharedBuffer_ != nullptr) { + AHardwareBuffer_release(pendingSharedBuffer_); + pendingSharedBuffer_ = nullptr; + } + } + if (framebuffer_ != 0) { + glDeleteFramebuffers(1, &framebuffer_); + framebuffer_ = 0; + } + if (swapchain_ != XR_NULL_HANDLE) { + xrDestroySwapchain(swapchain_); + swapchain_ = XR_NULL_HANDLE; + } + if (localSpace_ != XR_NULL_HANDLE) { + xrDestroySpace(localSpace_); + localSpace_ = XR_NULL_HANDLE; + } + if (aimSpaceLeft_ != XR_NULL_HANDLE) { + xrDestroySpace(aimSpaceLeft_); + aimSpaceLeft_ = XR_NULL_HANDLE; + } + if (aimSpaceRight_ != XR_NULL_HANDLE) { + xrDestroySpace(aimSpaceRight_); + aimSpaceRight_ = XR_NULL_HANDLE; + } + if (actionSet_ != XR_NULL_HANDLE) { + xrDestroyActionSet(actionSet_); + actionSet_ = XR_NULL_HANDLE; + } + if (session_ != XR_NULL_HANDLE) { + xrDestroySession(session_); + session_ = XR_NULL_HANDLE; + } + if (instance_ != XR_NULL_HANDLE) { + xrDestroyInstance(instance_); + instance_ = XR_NULL_HANDLE; + } + if (eglContext_ != EGL_NO_CONTEXT) { + eglMakeCurrent(eglDisplay_, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroyContext(eglDisplay_, eglContext_); + eglContext_ = EGL_NO_CONTEXT; + } + if (eglPbufferSurface_ != EGL_NO_SURFACE) { + eglDestroySurface(eglDisplay_, eglPbufferSurface_); + eglPbufferSurface_ = EGL_NO_SURFACE; + } +} + +} // namespace xrimmersive diff --git a/app/src/main/cpp/xrimmersive/xr_immersive.h b/app/src/main/cpp/xrimmersive/xr_immersive.h new file mode 100644 index 0000000000..5d6a86b9b0 --- /dev/null +++ b/app/src/main/cpp/xrimmersive/xr_immersive.h @@ -0,0 +1,286 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#define EGL_EGLEXT_PROTOTYPES +#include +#include +#define GL_GLEXT_PROTOTYPES +#include +#include +#include + +#define XR_USE_PLATFORM_ANDROID 1 +#define XR_USE_GRAPHICS_API_OPENGL_ES 1 +#include +#include + +namespace xrimmersive { + +// Bit indices match XrGamepadBridge.kt / WinHandler.sendVirtualGamepadState's Xbox layout. +enum ButtonBit { + BUTTON_A = 0, + BUTTON_B = 1, + BUTTON_X = 2, + BUTTON_Y = 3, + BUTTON_LB = 4, + BUTTON_RB = 5, + BUTTON_BACK = 6, + BUTTON_START = 7, + BUTTON_L3 = 8, + BUTTON_R3 = 9, +}; + +// Snapshot of one frame's controller state, written by the OpenXR thread and +// polled from Kotlin. Deliberately plain-old-data so it can be copied under a +// single mutex without touching any JNI/OpenXR types. +struct InputSnapshot { + uint32_t buttons = 0; + // No d-pad: Touch controllers have no physical one, and faking it from the thumbstick + // direction fought with the real analog values (see syncControllerInputs()). + float leftX = 0.0f; + float leftY = 0.0f; + float rightX = 0.0f; + float rightY = 0.0f; + float triggerL = 0.0f; + float triggerR = 0.0f; + // Rising-edge only: holding the Touch controllers' Menu button for 600ms summons + // GameNative's own quick menu. A short press of the same button is still forwarded as a + // normal gamepad Start press (games use it for their own pause/settings menu). + bool quickMenuClicked = false; + // Debounced level (not edge) state of the same Menu button — true for the entire physical + // hold, from press to release, regardless of whether it's still building up to the 600ms + // threshold or has already fired quickMenuClicked. Lets Kotlin suppress menu-navigation + // reads (thumbstick-as-dpad, click) while the hand that's holding this button may still be + // resting on/near the thumbstick — see handleMenuNavigation's suppression kdoc. + bool menuButtonHeld = false; + + // Rising-edge only: double-clicking either thumbstick (L3 or R3) toggles "XR pointer + // mode" — a laser-pointer-driven direct manipulation scheme for the quad (grab a corner to + // resize, grab the top/bottom bar to reposition), reserved with a real VR game's own + // pointer needs in mind later. + bool pointerModeToggled = false; + // Aim-pose ray for each hand (world position + normalized forward direction, in the same + // LOCAL reference space the quad transform is expressed in) — only meaningful pointer-mode. + bool handPosesValid = false; + float leftHandPosX = 0.0f, leftHandPosY = 0.0f, leftHandPosZ = 0.0f; + float leftHandFwdX = 0.0f, leftHandFwdY = 0.0f, leftHandFwdZ = -1.0f; + float rightHandPosX = 0.0f, rightHandPosY = 0.0f, rightHandPosZ = 0.0f; + float rightHandFwdX = 0.0f, rightHandFwdY = 0.0f, rightHandFwdZ = -1.0f; +}; + +// Owns the OpenXR instance/session and its dedicated frame-loop thread. +class XrImmersiveSession { +public: + bool initialize(JavaVM *vm, jobject activityRef); + void requestStop(); + void join(); + + InputSnapshot pollSnapshot(); + + // Called from the JNI bridge with a freshly PixelCopy'd RGBA_8888 frame of the game's + // actual rendered output (see ImmersiveXrActivity's capture loop). Copies into a + // double-buffered CPU-side slot; the render thread uploads it to the GPU on its own. + void submitFrame(const uint8_t *rgbaPixels, int32_t width, int32_t height); + + // Quad position (meters, in the LOCAL reference space; z is negative = in front of the + // player) and size (meters). x/y/z are reinterpreted as (tangential X offset, tangential Y + // offset, -radius) rather than plain Cartesian coordinates — see submitQuadLayer's kdoc — so + // the quad orbits the player on both axes and stays facing them as x and/or y move away from + // 0, instead of sliding on a flat plane and turning away. contentScaleX/Y are content-half-extent / quad-half-extent + // per axis (1.0 if the quad has no margin band beyond its content, e.g. the non-direct-render + // path where the composited bitmap already has the margin baked in) — only meaningful for + // the direct-render path's shader, which needs to know how much of the quad's UV space is + // real game texture vs. margin-only overlay (see kDirectQuadFragmentShader). Safe to call + // from any thread — read by the render thread at the top of each frame. + void setQuadTransform(float x, float y, float z, float width, float height, + float contentScaleX, float contentScaleY); + + // Enables/disables the XR_FB_passthrough layer behind the quad. No-op (logged) if the + // runtime doesn't support the extension. + void setPassthroughEnabled(bool enabled); + + // Direct-render path — see the member comment near sharedBufferMutex_. Safe to call from + // any thread; the actual EGLImage/texture import happens lazily on the render thread. + // Pass the SAME AHardwareBuffer every frame (GLRenderer writes into it repeatedly) — this + // only needs calling again if the buffer identity itself changes (e.g. a resize). + void setSharedGameBuffer(AHardwareBuffer *buffer); + +private: + void runLoop(); + bool setupInstanceAndSession(); + void teardown(); + void pollXrEvents(); + void renderFrame(); + void syncControllerInputs(XrTime predictedDisplayTime); + void submitQuadLayer(XrTime predictedDisplayTime, XrSpace space, XrSwapchain swapchain, + int32_t width, int32_t height, bool sessionActive); + void ensureQuadGeometryAndShader(); + void uploadPendingGameFrameLocked(); + void setupPassthrough(); + void teardownPassthrough(); + void applyPendingPassthroughState(); + + JavaVM *vm_ = nullptr; + jobject activityRef_ = nullptr; + + XrInstance instance_ = XR_NULL_HANDLE; + XrSystemId systemId_ = XR_NULL_SYSTEM_ID; + XrSession session_ = XR_NULL_HANDLE; + XrSpace localSpace_ = XR_NULL_HANDLE; + XrSwapchain swapchain_ = XR_NULL_HANDLE; + XrSessionState sessionState_ = XR_SESSION_STATE_UNKNOWN; + bool sessionRunning_ = false; + + // Reverted back to 1280x720 — bumping this to 1920x1080 (2.25x the pixels) to reduce + // pointer-margin softening measurably made overall performance worse (confirmed by the user + // immediately after that change), and performance matters far more than this margin's + // sharpness. Revisit only alongside real perf headroom, not before. + static constexpr int32_t kSwapchainWidth = 1280; + static constexpr int32_t kSwapchainHeight = 720; + + EGLDisplay eglDisplay_ = EGL_NO_DISPLAY; + EGLContext eglContext_ = EGL_NO_CONTEXT; + EGLSurface eglPbufferSurface_ = EGL_NO_SURFACE; + EGLConfig eglConfig_ = nullptr; + GLuint framebuffer_ = 0; + std::vector swapchainImages_; + + // Textured quad used to draw the captured game frame into the swapchain image. + GLuint gameTexture_ = 0; + GLuint quadProgram_ = 0; + GLuint quadVbo_ = 0; + GLint quadPositionLoc_ = -1; + GLint quadTexCoordLoc_ = -1; + GLint quadSamplerLoc_ = -1; + int32_t gameTextureWidth_ = 0; + int32_t gameTextureHeight_ = 0; + + // Direct-render path (setSharedGameBuffer): the game (GLRenderer, on its own thread/EGL + // context) writes straight into an AHardwareBuffer-backed texture instead of us + // screen-scraping its finished frame via PixelCopy. importSharedBufferIfNeeded() imports + // that same buffer a second time, into THIS thread's own context (AHardwareBuffer memory is + // shareable across contexts/processes; the texture/EGLImage wrapping it is not, so each side + // needs its own). When active, gameTexture_ above is repurposed to carry just the overlay + // layer (menu/HUD/Resume button — still CPU-composited, see uploadPendingGameFrameLocked) + // instead of the full composited frame, and the two are blended in directQuadProgram_. + std::mutex sharedBufferMutex_; + AHardwareBuffer *pendingSharedBuffer_ = nullptr; + bool sharedBufferChanged_ = false; + EGLImageKHR sharedGameImage_ = EGL_NO_IMAGE_KHR; + GLuint sharedGameTexture_ = 0; + bool hasSharedGameTexture_ = false; + GLuint directQuadProgram_ = 0; + GLint directQuadPositionLoc_ = -1; + GLint directQuadTexCoordLoc_ = -1; + GLint directGameSamplerLoc_ = -1; + GLint directOverlaySamplerLoc_ = -1; + GLint directContentScaleLoc_ = -1; + + void importSharedBufferIfNeeded(); + + // Latest captured game frame, written by submitFrame() (any thread) and consumed by + // the render thread. Separate from snapshotMutex_ — controller input and video frames + // are unrelated and shouldn't contend on the same lock. + std::mutex frameMutex_; + std::vector pendingFramePixels_; + int32_t pendingFrameWidth_ = 0; + int32_t pendingFrameHeight_ = 0; + bool hasPendingFrame_ = false; + + XrActionSet actionSet_ = XR_NULL_HANDLE; + XrAction buttonAAction_ = XR_NULL_HANDLE; + XrAction buttonBAction_ = XR_NULL_HANDLE; + XrAction buttonXAction_ = XR_NULL_HANDLE; + XrAction buttonYAction_ = XR_NULL_HANDLE; + XrAction squeezeLAction_ = XR_NULL_HANDLE; + XrAction squeezeRAction_ = XR_NULL_HANDLE; + XrAction triggerLAction_ = XR_NULL_HANDLE; + XrAction triggerRAction_ = XR_NULL_HANDLE; + XrAction thumbstickLAction_ = XR_NULL_HANDLE; + XrAction thumbstickRAction_ = XR_NULL_HANDLE; + XrAction thumbstickLClickAction_ = XR_NULL_HANDLE; + XrAction thumbstickRClickAction_ = XR_NULL_HANDLE; + XrAction menuLAction_ = XR_NULL_HANDLE; + uint64_t syncFrameCounter_ = 0; + + // XR pointer-mode toggle: double-click of either thumbstick (L3 or R3) — see + // InputSnapshot::pointerModeToggled. + XrAction aimPoseLeftAction_ = XR_NULL_HANDLE; + XrAction aimPoseRightAction_ = XR_NULL_HANDLE; + XrSpace aimSpaceLeft_ = XR_NULL_HANDLE; + XrSpace aimSpaceRight_ = XR_NULL_HANDLE; + bool lastL3Pressed_ = false; + std::chrono::steady_clock::time_point lastL3ClickTime_; + int l3ClickCount_ = 0; + bool lastR3Pressed_ = false; + std::chrono::steady_clock::time_point lastR3ClickTime_; + int r3ClickCount_ = 0; + + // Debounce state for the Menu/Start button. A short press still forwards a normal Start + // press to the game (menuDebouncedActive_/menuPressStartTime_); holding it for 600ms opens + // the immersive quick menu instead (see quickMenuClicked in the .cpp). The raw OpenXR click + // can bounce (several false->true transitions within a single physical press), so + // lastMenuDebouncedPressed_/lastMenuRawTrueTime_ apply a short bounce-tolerance window + // before measuring hold duration. + std::chrono::steady_clock::time_point lastMenuRawTrueTime_; + bool lastMenuDebouncedPressed_ = false; + std::chrono::steady_clock::time_point menuHoldStartTime_; + bool menuLongPressTriggered_ = false; + bool menuDebouncedActive_ = false; + std::chrono::steady_clock::time_point menuPressStartTime_; + + std::thread thread_; + std::atomic stopRequested_{false}; + + std::mutex snapshotMutex_; + InputSnapshot snapshot_; + + // Quad transform — defaults match the original hardcoded values (2m in front, 16:9, + // 1.6m wide). Atomics: written from Kotlin/JNI callers, read from the render thread. + std::atomic quadPosX_{0.0f}; + std::atomic quadPosY_{0.0f}; + std::atomic quadPosZ_{-2.0f}; + std::atomic quadWidth_{1.6f}; + std::atomic quadHeight_{0.9f}; + std::atomic quadContentScaleX_{1.0f}; + std::atomic quadContentScaleY_{1.0f}; + + // Passthrough (XR_FB_passthrough). All the passthrough* function pointers are resolved + // once in setupInstanceAndSession() and are null if the runtime doesn't support the + // extension, in which case setPassthroughEnabled() is a no-op. + bool passthroughExtensionAvailable_ = false; + // Whether this runtime/view-config actually lists ALPHA_BLEND in + // xrEnumerateEnvironmentBlendModes — requesting an unlisted blend mode in xrEndFrame is a + // validation error (observed: xrEndFrame failing with -1 on every frame once passthrough + // was toggled), so passthrough is refused entirely if this is false. + bool alphaBlendSupported_ = false; + + // Perf/scheduling extensions — see setupInstanceAndSession()'s comment for why these were + // missing entirely before (this is the same architecture GameNativeXR's libxr.so uses, per + // standard OpenXR extensions, not anything proprietary). + bool perfSettingsExtensionAvailable_ = false; + bool threadSettingsExtensionAvailable_ = false; + bool refreshRateExtensionAvailable_ = false; + + std::atomic passthroughRequested_{false}; + bool passthroughActive_ = false; + XrPassthroughFB passthrough_ = XR_NULL_HANDLE; + XrPassthroughLayerFB passthroughLayer_ = XR_NULL_HANDLE; + PFN_xrCreatePassthroughFB xrCreatePassthroughFB_ = nullptr; + PFN_xrDestroyPassthroughFB xrDestroyPassthroughFB_ = nullptr; + PFN_xrPassthroughStartFB xrPassthroughStartFB_ = nullptr; + PFN_xrPassthroughPauseFB xrPassthroughPauseFB_ = nullptr; + PFN_xrCreatePassthroughLayerFB xrCreatePassthroughLayerFB_ = nullptr; + PFN_xrDestroyPassthroughLayerFB xrDestroyPassthroughLayerFB_ = nullptr; + PFN_xrPassthroughLayerResumeFB xrPassthroughLayerResumeFB_ = nullptr; + PFN_xrPassthroughLayerPauseFB xrPassthroughLayerPauseFB_ = nullptr; +}; + +} // namespace xrimmersive diff --git a/app/src/main/java/app/gamenative/MainActivity.kt b/app/src/main/java/app/gamenative/MainActivity.kt index c8ef8d9757..c71878be15 100644 --- a/app/src/main/java/app/gamenative/MainActivity.kt +++ b/app/src/main/java/app/gamenative/MainActivity.kt @@ -80,6 +80,18 @@ class MainActivity : ComponentActivity() { Build.MANUFACTURER.equals("Meta", true) || Build.MANUFACTURER.equals("Pico", true) + // Immersive/VR launch mode currently only targets Meta Quest — unlike isHeadset() + // above, this intentionally excludes Pico. + // + // Confirmed on a real Quest 3: `hasSystemFeature("android.hardware.vr.headtracking")` + // returns false for a plain 2D-panel app (that feature isn't declared outside an + // active VR/XR session), so — unlike an earlier version of this check — it must NOT + // be required here. Manufacturer/brand alone is enough, same as isHeadset() does. + fun isMetaQuest(context: Context): Boolean = + Build.MANUFACTURER.equals("Oculus", true) || + Build.MANUFACTURER.equals("Meta", true) || + Build.BRAND.equals("oculus", true) + // Store pending launch request to be processed after UI is ready @Volatile private var pendingLaunchRequest: IntentLaunchManager.LaunchRequest? = null diff --git a/app/src/main/java/app/gamenative/events/AndroidEvent.kt b/app/src/main/java/app/gamenative/events/AndroidEvent.kt index a2ec39df10..eb900d5d85 100644 --- a/app/src/main/java/app/gamenative/events/AndroidEvent.kt +++ b/app/src/main/java/app/gamenative/events/AndroidEvent.kt @@ -20,6 +20,7 @@ interface AndroidEvent : Event { data class ShowLaunchingOverlay(val appName: String) : AndroidEvent data object HideLaunchingOverlay : AndroidEvent data class SetBootingSplashText(val text: String) : AndroidEvent + data object ClearBootingSplash : AndroidEvent data class DownloadPausedDueToConnectivity(val appId: Int) : AndroidEvent data class DownloadStatusChanged(val appId: Int, val isDownloading: Boolean) : AndroidEvent data class PostInstallSyncStatusChanged(val appId: Int, val isSyncing: Boolean) : AndroidEvent diff --git a/app/src/main/java/app/gamenative/service/SteamService.kt b/app/src/main/java/app/gamenative/service/SteamService.kt index af631daa46..538a1b18e3 100644 --- a/app/src/main/java/app/gamenative/service/SteamService.kt +++ b/app/src/main/java/app/gamenative/service/SteamService.kt @@ -3597,11 +3597,15 @@ class SteamService : Service(), IChallengeUrlChanged { override fun onTaskRemoved(rootIntent: Intent?) { super.onTaskRemoved(rootIntent) - if (!hasActiveOperations()) { + // keepAlive is the same flag MainActivity's onStop/onDestroy already respect (see + // MainActivity.kt) — this callback is a separate Android lifecycle hook (fires when a + // TASK is torn down, not tied to any one Activity's own lifecycle) that was bypassing it + // entirely, force-stopping Steam out from under an immersive session in progress. + if (!hasActiveOperations() && !keepAlive) { Timber.i("Task removed and no active work — stopping service") stopSelf() } else { - Timber.i("Task removed but active work exists — keeping service alive") + Timber.i("Task removed but active work or keepAlive exists — keeping service alive") } } diff --git a/app/src/main/java/app/gamenative/ui/component/QuickMenu.kt b/app/src/main/java/app/gamenative/ui/component/QuickMenu.kt index 6b634ec6f0..3c1dafd2a5 100644 --- a/app/src/main/java/app/gamenative/ui/component/QuickMenu.kt +++ b/app/src/main/java/app/gamenative/ui/component/QuickMenu.kt @@ -1,6 +1,7 @@ package app.gamenative.ui.component import android.view.KeyEvent +import timber.log.Timber import androidx.activity.compose.BackHandler import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.MutableTransitionState @@ -56,6 +57,7 @@ import androidx.compose.material.icons.filled.QueryStats import androidx.compose.material.icons.filled.Settings import androidx.compose.material.icons.filled.Speed import androidx.compose.material.icons.filled.TouchApp +import androidx.compose.material.icons.filled.ViewInAr import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.LinearProgressIndicator @@ -63,11 +65,14 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment @@ -76,6 +81,9 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector @@ -98,6 +106,7 @@ import com.winlator.renderer.GLRenderer import com.winlator.renderer.VulkanRenderer import com.winlator.winhandler.ProcessInfo import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import kotlin.math.roundToInt object QuickMenuAction { @@ -118,6 +127,7 @@ private object QuickMenuTab { const val EFFECTS = 2 const val CONTROLLER = 3 const val TOOLS = 4 + const val IMMERSIVE = 5 } data class QuickMenuItem( @@ -234,6 +244,35 @@ private fun matchesPerformanceHudPreset( // fpsLimiterSteps / fpsLimiterCurrentIndex / fpsLimiterProgress / // nextFpsLimiterValue / previousFpsLimiterValue live in FpsLimiterUtils.kt +/** + * Lets ANY quick-menu row — no matter how deeply nested, in this file or any other — report its + * own gamepad interaction to the Meta Quest immersive activity, without threading a dedicated + * parameter through every intermediate composable in between (the previous approach: adding a + * new callback param to QuickMenuAdjustmentRow, then to its tab wrapper, then to QuickMenu itself, + * then to every call site — for every single row that needed it. A new row buried in some future + * tab would need the SAME threading redone by hand, easy to forget). + * + * Exists because normal Android/Compose gamepad input (real hardware, real KeyEvents) already + * works correctly for every existing row with zero extra code — this is ONLY needed because the + * immersive activity's Quest-Touch-controller input is synthetic and confirmed, repeatedly this + * session, to never reach Compose's own key dispatch or default click-on-focused-view behavior in + * that Activity. [QuickMenu] provides the real implementation (backed by state it forwards to + * ImmersiveXrActivity via registerAdjustmentControl/registerFocusedActivate); everywhere else — + * i.e. normal 2D panel mode — gets the no-op default below, so a row that reports itself here + * costs nothing and needs no `if (immersive)` branching of its own. + */ +class ImmersiveInputBypass( + // Reports (onDecrease, onIncrease) while a row is both focused and lock-toggled (A to lock, + // DPAD_LEFT/RIGHT to adjust, B or losing focus to unlock) — for slider-style rows. + val reportAdjustment: (Pair<() -> Unit, () -> Unit>?) -> Unit, + // Reports a row's own click/select action while it's focused — for plain radio/toggle rows + // that only need a single BUTTON_A/DPAD_CENTER press to activate. + val reportActivate: ((() -> Unit)?) -> Unit, +) +val LocalImmersiveInputBypass = staticCompositionLocalOf { + ImmersiveInputBypass(reportAdjustment = {}, reportActivate = {}) +} + @Composable fun QuickMenu( isVisible: Boolean, @@ -268,9 +307,71 @@ fun QuickMenu( onLsfgMultiplierChanged: (Int) -> Unit = {}, onLsfgFlowScaleChanged: (Float) -> Unit = {}, onLsfgPerformanceModeChanged: (Boolean) -> Unit = {}, + // Immersive tab — only shown when non-null (i.e. hosted by ImmersiveXrActivity) + immersiveControls: app.gamenative.ui.screen.xr.ImmersiveControls? = null, onAnimationComplete: (Boolean) -> Unit = {}, + // Hands the live FocusManager up to the caller — see its call site's kdoc for why: + // dispatching synthetic dpad KeyEvents through Activity.dispatchKeyEvent() turned out to + // never reach Compose's key/focus dispatch at all in the immersive activity (confirmed via + // logging: dispatchKeyEvent() gets called and returns false, but nothing here ever sees the + // event — some other Android View still holds real view-level focus). Calling + // FocusManager.moveFocus() directly bypasses that whole broken path. + registerFocusManager: ((androidx.compose.ui.focus.FocusManager) -> Unit)? = null, + // Same bypass, for the tab rail specifically: LB/RB tab-cycling used to go through + // dispatchMenuKeyEvent (KEYCODE_BUTTON_L1/R1) into the SAME broken KeyEvent path — this hands + // up a direct "cycle forward/backward through the tabs actually shown right now" action + // instead, called straight from ImmersiveXrActivity's LB/RB handling. + registerCycleTab: ((Boolean) -> Unit) -> Unit = {}, + // Same bypass again, for a locked QuickMenuAdjustmentRow's left/right slider drag: arrow-key + // movement already skips Compose's key dispatch entirely for this Activity (moveMenuFocus + // calls FocusManager.moveFocus() directly, see registerFocusManager's kdoc above) — which + // means a locked slider's own onPreviewKeyEvent (listening for DPAD_LEFT/RIGHT) never sees + // those presses either, since moveFocus fires first and moves focus away mid-adjustment + // instead. Registers a live getter for "is some row currently locked, and if so its + // decrease/increase actions" so ImmersiveXrActivity can drive it directly, the same way it + // drives tab cycling and focus movement. + registerAdjustmentControl: (() -> Pair<() -> Unit, () -> Unit>?) -> Unit = {}, + // Same bypass again, for LEFT specifically: FocusManager.moveFocus(Left) reaching the tab + // rail from tab content turned out to depend on the focused row's vertical position lining + // up with a rail icon in Compose's spatial search heuristic — it worked from some rows and + // not others, confirmed by comparing logs from a session where it worked against one where it + // didn't. A direct requestFocus() on the CURRENTLY SELECTED tab's own rail button is + // reliable regardless of where in the content focus currently is, same as registerCycleTab. + registerFocusTabRail: (() -> Unit) -> Unit = {}, + // Same bypass again, for a plain radio/selectable row that only reacts to BUTTON_A/DPAD_CENTER + // (e.g. ScreenEffectsPanel's scaling-mode rows): relying on Android's default "click whatever + // holds real view focus" behavior for that key turned out to be exactly as unreliable here as + // it was for the tab rail and adjustment-row lock — confirmed via logs showing scaling-mode + // selection simply not registering in the immersive activity despite Compose showing the row + // as focused. Registers a live getter for "is some row focused right now that just wants a + // plain activate on A, and if so its action" so ImmersiveXrActivity can invoke it directly. + registerFocusedActivate: (() -> (() -> Unit)?) -> Unit = {}, + // Receives the Menu/Start button's physical-hold state, pushed live from + // ImmersiveXrActivity's native polling. While true, this Composable's own onPreviewKeyEvent + // consumes any key event it sees — Horizon OS's own gamepad input dispatch generates real + // Android KeyEvents (DPAD_CENTER/BUTTON_A/BUTTON_START) while that button is physically held, + // a completely separate path from the immersive activity's OpenXR-polling-driven bypasses, + // which has no visibility into or control over it. + registerStartHeld: ((Boolean) -> Unit) -> Unit = {}, modifier: Modifier = Modifier, ) { + val focusManager = LocalFocusManager.current + LaunchedEffect(registerFocusManager) { + registerFocusManager?.invoke(focusManager) + } + val focusTabRailScope = rememberCoroutineScope() + var activeAdjustment by remember { mutableStateOf Unit, () -> Unit>?>(null) } + var activeRadioActivate by remember { mutableStateOf<(() -> Unit)?>(null) } + LaunchedEffect(Unit) { + registerFocusedActivate { activeRadioActivate } + } + var startHeld by remember { mutableStateOf(false) } + LaunchedEffect(Unit) { + registerStartHeld { startHeld = it } + } + LaunchedEffect(Unit) { + registerAdjustmentControl { activeAdjustment } + } val exitGameItem = QuickMenuItem( id = QuickMenuAction.EXIT_GAME, icon = Icons.AutoMirrored.Filled.ExitToApp, @@ -341,9 +442,11 @@ fun QuickMenu( var selectedTab by rememberSaveable { mutableIntStateOf( - if (PrefManager.quickMenuLastTab == QuickMenuTab.LSFG && !isLsfgAvailable) - QuickMenuTab.HUD - else PrefManager.quickMenuLastTab + when { + PrefManager.quickMenuLastTab == QuickMenuTab.LSFG && !isLsfgAvailable -> QuickMenuTab.HUD + PrefManager.quickMenuLastTab == QuickMenuTab.IMMERSIVE && immersiveControls == null -> QuickMenuTab.HUD + else -> PrefManager.quickMenuLastTab + } ) } val selectedTabLabelResId = when (selectedTab) { @@ -351,6 +454,7 @@ fun QuickMenu( QuickMenuTab.LSFG -> R.string.lsfg_tab_title QuickMenuTab.EFFECTS -> R.string.screen_effects QuickMenuTab.TOOLS -> R.string.task_manager + QuickMenuTab.IMMERSIVE -> R.string.quick_menu_tab_immersive else -> R.string.quick_menu_tab_controller } @@ -368,6 +472,9 @@ fun QuickMenu( val controllerItemFocusRequester = remember { FocusRequester() } val toolsItemFocusRequester = remember { FocusRequester() } val lsfgItemFocusRequester = remember { FocusRequester() } + val immersiveScrollState = rememberScrollState() + val immersiveTabFocusRequester = remember { FocusRequester() } + val immersiveItemFocusRequester = remember { FocusRequester() } val visibleState = remember { MutableTransitionState(false) } visibleState.targetState = isVisible @@ -382,7 +489,120 @@ fun QuickMenu( onDismiss() } - Box(modifier = modifier.fillMaxSize()) { + // Only the tabs actually shown in the rail, in on-screen order — mirrors the conditions each + // QuickMenuTabButton below is gated on (isLsfgAvailable, a renderer being available, etc). + val availableTabs = remember(isLsfgAvailable, renderer, glRenderer, immersiveControls) { + buildList { + add(QuickMenuTab.HUD) + if (isLsfgAvailable) add(QuickMenuTab.LSFG) + if (renderer != null || glRenderer != null) add(QuickMenuTab.EFFECTS) + add(QuickMenuTab.CONTROLLER) + add(QuickMenuTab.TOOLS) + if (immersiveControls != null) add(QuickMenuTab.IMMERSIVE) + } + } + + LaunchedEffect(Unit) { + registerFocusTabRail { + val requester = when (selectedTab) { + QuickMenuTab.HUD -> hudTabFocusRequester + QuickMenuTab.LSFG -> lsfgTabFocusRequester + QuickMenuTab.EFFECTS -> effectsTabFocusRequester + QuickMenuTab.CONTROLLER -> controllerTabFocusRequester + QuickMenuTab.TOOLS -> toolsTabFocusRequester + QuickMenuTab.IMMERSIVE -> immersiveTabFocusRequester + else -> null + } + if (requester == null) return@registerFocusTabRail + // A single attempt genuinely failed in practice — "FocusRequester is not + // initialized" thrown for the rail button's own requester, confirmed via logging — + // meaning the rail wasn't done composing/attaching yet at that exact instant. The + // OTHER requestFocus call site in this file (content-item focus on tab switch) + // already retries 3x for the identical reason; this one previously didn't, so a + // single mistimed LEFT press silently did nothing instead of retrying like that one. + focusTabRailScope.launch { + repeat(3) { attempt -> + try { + requester.requestFocus() + Timber.i("QuickMenu: requestFocusTabRail succeeded for tab=%s on attempt=%d", selectedTab, attempt) + return@launch + } catch (e: IllegalStateException) { + Timber.w(e, "QuickMenu: requestFocusTabRail failed for tab=%s on attempt=%d", selectedTab, attempt) + delay(80) + } + } + Timber.w("QuickMenu: requestFocusTabRail never succeeded for tab=%s after 3 attempts", selectedTab) + } + } + } + + LaunchedEffect(availableTabs) { + registerCycleTab { forward -> + val currentIndex = availableTabs.indexOf(selectedTab).takeIf { it >= 0 } ?: 0 + val nextTab = if (forward) { + availableTabs[(currentIndex + 1) % availableTabs.size] + } else { + availableTabs[(currentIndex - 1 + availableTabs.size) % availableTabs.size] + } + selectedTab = nextTab + PrefManager.quickMenuLastTab = nextTab + } + } + + CompositionLocalProvider( + LocalImmersiveInputBypass provides ImmersiveInputBypass( + reportAdjustment = { activeAdjustment = it }, + reportActivate = { activeRadioActivate = it }, + ), + ) { + Box( + modifier = modifier.fillMaxSize() + // Directly cycling tabs on the shoulder buttons, instead of relying on Compose's + // directional (dpad) focus search to cross from the content pane into the separate + // tab-rail column, sidesteps a real reported problem: users couldn't reach the rail + // ("les categories") via dpad-left at all once focus was inside a tab's content — + // same LB/RB pattern as a real console dashboard's tab switching, and it works + // regardless of where focus currently is or how the two panes are laid out. + .onPreviewKeyEvent { keyEvent -> + // Real KeyEvent.KEYCODE_BUTTON_START/DPAD_CENTER/BUTTON_A events reach here + // through Android's own gamepad input dispatch while the Menu/Start button is + // physically held — a completely separate path from the immersive activity's + // native OpenXR polling/bypass mechanism, which has no visibility into or control + // over it. Left unconsumed, Android's key-fallback mechanism re-dispatches + // BUTTON_START as KEYCODE_DPAD_CENTER — the "click the focused element" behavior + // reported as "Start acts like the A button" (toggling the HUD/FPS limiter, + // unlocking a locked adjustment row, etc., purely from holding Start). + // registerStartHeld's level state covers this for every affected key code, not + // just BUTTON_START — consuming everything while it's true, on every action (not + // just ACTION_DOWN), blocks the fallback from ever firing while the menu is + // visible; Start's actual open/close behavior is handled entirely separately, by + // the long-press detector in ImmersiveXrActivity (see toggleQuickMenu). + if (isVisible && startHeld) { + return@onPreviewKeyEvent true + } + if (keyEvent.nativeKeyEvent.action != KeyEvent.ACTION_DOWN) return@onPreviewKeyEvent false + // Fires for ANY key event that reaches this Composable, from wherever focus + // currently is, bubbling up through every ancestor on its way here (onPreviewKeyEvent + // semantics) — if a dispatched dpad/DPAD_CENTER event never shows up in this log at + // all, the event isn't reaching Compose's key/focus dispatch in the first place; + // if it DOES show up but focus never visibly moves, the problem is downstream of + // here (directional focus search itself, or nothing was focused to move from). + Timber.i("QuickMenu: onPreviewKeyEvent keyCode=%d selectedTab=%d", keyEvent.nativeKeyEvent.keyCode, selectedTab) + val currentIndex = availableTabs.indexOf(selectedTab).takeIf { it >= 0 } ?: 0 + val nextTab = when (keyEvent.nativeKeyEvent.keyCode) { + KeyEvent.KEYCODE_BUTTON_L1 -> availableTabs[(currentIndex - 1 + availableTabs.size) % availableTabs.size] + KeyEvent.KEYCODE_BUTTON_R1 -> availableTabs[(currentIndex + 1) % availableTabs.size] + else -> null + } + if (nextTab != null) { + selectedTab = nextTab + PrefManager.quickMenuLastTab = nextTab + true + } else { + false + } + }, + ) { AnimatedVisibility( visible = isVisible, enter = fadeIn(animationSpec = tween(200)), @@ -392,11 +612,13 @@ fun QuickMenu( modifier = Modifier .fillMaxSize() .background(MaterialTheme.colorScheme.scrim.copy(alpha = 0f)) - .clickable( - interactionSource = remember { MutableInteractionSource() }, - indication = null, - onClick = onDismiss, - ), + // Not .clickable() on purpose: that implicitly adds a focus target the size + // of the whole screen, sitting right behind every other focusable item in + // the menu — a prime candidate for Compose's directional (dpad) focus search + // to land on by mistake, which would silently close the whole menu on the + // next DPAD_CENTER. pointerInput+detectTapGestures dismisses on tap without + // registering any focus node at all. + .pointerInput(Unit) { detectTapGestures { onDismiss() } }, ) } @@ -529,6 +751,20 @@ fun QuickMenu( modifier = Modifier.width(56.dp), focusRequester = toolsTabFocusRequester, ) + if (immersiveControls != null) { + QuickMenuTabButton( + icon = Icons.Default.ViewInAr, + contentDescriptionResId = R.string.quick_menu_tab_immersive, + selected = selectedTab == QuickMenuTab.IMMERSIVE, + accentColor = PluviaTheme.colors.accentPurple, + onSelected = { + selectedTab = QuickMenuTab.IMMERSIVE + PrefManager.quickMenuLastTab = selectedTab + }, + modifier = Modifier.width(56.dp), + focusRequester = immersiveTabFocusRequester, + ) + } } Box( @@ -655,6 +891,17 @@ fun QuickMenu( ) } + QuickMenuTab.IMMERSIVE -> { + if (immersiveControls != null) { + ImmersiveQuickMenuTab( + controls = immersiveControls, + scrollState = immersiveScrollState, + focusRequester = immersiveItemFocusRequester, + modifier = Modifier.fillMaxSize(), + ) + } + } + else -> { Column( modifier = Modifier @@ -700,27 +947,32 @@ fun QuickMenu( } } } + } LaunchedEffect(isVisible, selectedTab) { onToolsVisibilityChanged(isVisible && selectedTab == QuickMenuTab.TOOLS) } - LaunchedEffect(isVisible) { + LaunchedEffect(isVisible, selectedTab) { if (isVisible) { - repeat(3) { + repeat(3) { attempt -> try { when (selectedTab) { QuickMenuTab.HUD -> hudItemFocusRequester.requestFocus() QuickMenuTab.LSFG -> lsfgItemFocusRequester.requestFocus() QuickMenuTab.EFFECTS -> effectsItemFocusRequester.requestFocus() QuickMenuTab.TOOLS -> toolsItemFocusRequester.requestFocus() + QuickMenuTab.IMMERSIVE -> immersiveItemFocusRequester.requestFocus() else -> controllerItemFocusRequester.requestFocus() } + Timber.i("QuickMenu: requestFocus succeeded for tab=%d on attempt=%d", selectedTab, attempt) return@LaunchedEffect - } catch (_: Exception) { + } catch (t: Exception) { + Timber.w(t, "QuickMenu: requestFocus threw for tab=%d on attempt=%d", selectedTab, attempt) delay(80) } } + Timber.w("QuickMenu: requestFocus never succeeded for tab=%d after 3 attempts", selectedTab) } } } @@ -855,7 +1107,9 @@ private fun PerformanceHudQuickMenuTab( ) Row( - modifier = Modifier.padding(horizontal = 8.dp), + modifier = Modifier + .padding(horizontal = 8.dp) + .focusGroup(), horizontalArrangement = Arrangement.spacedBy(8.dp), ) { PerformanceHudPreset.values().forEach { preset -> @@ -889,7 +1143,9 @@ private fun PerformanceHudQuickMenuTab( ) Row( - modifier = Modifier.padding(horizontal = 8.dp), + modifier = Modifier + .padding(horizontal = 8.dp) + .focusGroup(), horizontalArrangement = Arrangement.spacedBy(8.dp), ) { listOf( @@ -1145,7 +1401,9 @@ private fun LsfgQuickMenuTab( title = stringResource(R.string.lsfg_multiplier), ) Row( - modifier = Modifier.padding(horizontal = 8.dp), + modifier = Modifier + .padding(horizontal = 8.dp) + .focusGroup(), horizontalArrangement = Arrangement.spacedBy(8.dp), ) { listOf(0, 2, 3, 4).forEach { value -> @@ -1202,6 +1460,111 @@ private fun LsfgQuickMenuTab( } } +@Composable +private fun ImmersiveQuickMenuTab( + controls: app.gamenative.ui.screen.xr.ImmersiveControls, + scrollState: ScrollState, + focusRequester: FocusRequester? = null, + modifier: Modifier = Modifier, +) { + val accentColor = PluviaTheme.colors.accentPurple + + Column( + modifier = modifier + .verticalScroll(scrollState) + .focusGroup(), + verticalArrangement = Arrangement.spacedBy(4.dp), + ) { + // ── Passthrough ──────────────────────────────────────────────── + QuickMenuSectionHeader( + title = stringResource(R.string.immersive_passthrough), + subtitle = stringResource(R.string.immersive_passthrough_desc), + ) + QuickMenuToggleRow( + title = stringResource(R.string.immersive_passthrough_toggle), + enabled = controls.passthroughEnabled, + onToggle = { controls.onPassthroughToggle(!controls.passthroughEnabled) }, + accentColor = accentColor, + focusRequester = focusRequester, + ) + + Spacer(modifier = Modifier.height(4.dp)) + + // ── Resize/move handles ─────────────────────────────────────────── + // Position/size sliders removed at the user's explicit request — the trigger-driven + // grab handles (drawn just inside the content edges while this toggle is on) are now the + // ONLY way to move/resize, not an alternative alongside a slider-based one. + QuickMenuSectionHeader( + title = stringResource(R.string.immersive_resize_mode_title), + subtitle = stringResource(R.string.immersive_resize_mode_desc), + ) + QuickMenuToggleRow( + title = stringResource(R.string.immersive_resize_mode_toggle), + enabled = controls.resizeModeEnabled, + onToggle = { controls.onResizeModeToggle(!controls.resizeModeEnabled) }, + accentColor = accentColor, + ) + + if (controls.directRenderBlockedByEffects != null) { + Spacer(modifier = Modifier.height(4.dp)) + QuickMenuSectionHeader( + title = stringResource(R.string.immersive_direct_render_title), + subtitle = stringResource( + if (controls.directRenderBlockedByEffects) { + R.string.immersive_reset_effects_status_blocked + } else { + R.string.immersive_reset_effects_status_ok + }, + ), + ) + val resetInteractionSource = remember { MutableInteractionSource() } + val resetIsFocused by resetInteractionSource.collectIsFocusedAsState() + Column( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 8.dp, vertical = 2.dp) + .clip(RoundedCornerShape(14.dp)) + .background( + if (resetIsFocused) { + accentColor.copy(alpha = 0.16f) + } else { + MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.18f) + }, + ) + .then( + if (resetIsFocused) { + Modifier.border(width = 2.dp, color = accentColor.copy(alpha = 0.7f), shape = RoundedCornerShape(14.dp)) + } else { + Modifier + } + ) + .selectable( + selected = false, + interactionSource = resetInteractionSource, + indication = null, + onClick = controls.onResetScreenEffects, + ) + .padding(horizontal = 16.dp, vertical = 14.dp), + verticalArrangement = Arrangement.spacedBy(4.dp), + ) { + Text( + text = stringResource(R.string.immersive_reset_effects_button), + style = MaterialTheme.typography.bodyLarge, + fontWeight = FontWeight.SemiBold, + color = MaterialTheme.colorScheme.onSurface, + ) + Text( + text = stringResource(R.string.immersive_reset_effects_desc), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } + + Spacer(modifier = Modifier.height(12.dp)) + } +} + @Composable private fun QuickMenuSectionHeader( title: String, @@ -1253,8 +1616,7 @@ private fun QuickMenuCloseButton( interactionSource = interactionSource, indication = null, onClick = onClick, - ) - .focusable(interactionSource = interactionSource), + ), contentAlignment = Alignment.Center, ) { Icon( @@ -1283,6 +1645,14 @@ private fun QuickMenuTabButton( val interactionSource = remember { MutableInteractionSource() } val isFocused by interactionSource.collectIsFocusedAsState() val shape = RoundedCornerShape(14.dp) + // Same default-click unreliability as ScreenEffectRadioRow/QuickMenuChoiceChip — see + // LocalImmersiveInputBypass's kdoc. Without this, moving focus onto a rail button via LEFT + // + UP/DOWN and pressing A didn't reliably select the tab; LB/RB (registerCycleTab, which + // calls onSelected directly) was the only reliable way in. + val inputBypass = LocalImmersiveInputBypass.current + LaunchedEffect(isFocused) { + inputBypass.reportActivate(if (isFocused) onSelected else null) + } Box( modifier = modifier @@ -1303,18 +1673,19 @@ private fun QuickMenuTabButton( Modifier } ) - .onFocusChanged { - if (it.isFocused && !selected) { - onSelected() - } - } + // Selecting on focus alone (not requiring a click/DPAD_CENTER) was a real hazard once + // directional focus movement actually started working (see registerFocusManager): + // moving focus onto a rail button recomposes the whole tab content area, which can + // reshuffle what's focusable and trigger another focus change, another selection, + // another recompose — the tab switch that froze the menu. Selecting only on an + // explicit click (still fires from LB/RB now, see registerTabSelector) removes that + // whole class of cascade. .selectable( selected = selected, interactionSource = interactionSource, indication = null, onClick = onSelected, - ) - .focusable(interactionSource = interactionSource), + ), contentAlignment = Alignment.Center, ) { Icon( @@ -1382,8 +1753,7 @@ private fun QuickMenuRailActionButton( interactionSource = interactionSource, indication = null, onClick = onClick, - ) - .focusable(interactionSource = interactionSource), + ), contentAlignment = Alignment.Center, ) { Icon( @@ -1407,6 +1777,10 @@ private fun QuickMenuChoiceChip( val interactionSource = remember { MutableInteractionSource() } val isFocused by interactionSource.collectIsFocusedAsState() val shape = RoundedCornerShape(12.dp) + val inputBypass = LocalImmersiveInputBypass.current + LaunchedEffect(isFocused) { + inputBypass.reportActivate(if (isFocused) onClick else null) + } Box( modifier = modifier @@ -1447,7 +1821,6 @@ private fun QuickMenuChoiceChip( indication = null, onClick = onClick, ) - .focusable(interactionSource = interactionSource) .padding(horizontal = 12.dp), contentAlignment = Alignment.Center, ) { @@ -1476,6 +1849,16 @@ private fun QuickMenuAdjustmentRow( val isFocused by interactionSource.collectIsFocusedAsState() val shape = RoundedCornerShape(14.dp) var isAdjustmentLocked by remember { mutableStateOf(false) } + // Reports (onDecrease, onIncrease) up while this row is both focused and lock-toggled, null + // otherwise — see LocalImmersiveInputBypass's kdoc. Keyed only on isFocused/isAdjustmentLocked, + // not onDecrease/onIncrease: those two are fresh lambda instances every recomposition (inline + // closures over the caller's own state), which would restart this effect on every unrelated + // recompose — harmless but wasteful. The closures stored here stay valid regardless of when + // they were captured, since they read their captured state fresh at call time, not creation. + val inputBypass = LocalImmersiveInputBypass.current + LaunchedEffect(isFocused, isAdjustmentLocked) { + inputBypass.reportAdjustment(if (isFocused && isAdjustmentLocked) (onDecrease to onIncrease) else null) + } Column( modifier = modifier @@ -1518,6 +1901,14 @@ private fun QuickMenuAdjustmentRow( ) .onFocusChanged { if (!it.isFocused) { + // Only logged when this row was actually locked — an unrelated row losing + // focus during normal navigation is expected and not interesting; a LOCKED + // row losing focus (forcing the lock off) is exactly the event that needed + // direct evidence, since from the outside it's indistinguishable from a + // deliberate B-press unlock. + if (isAdjustmentLocked) { + Timber.i("QuickMenu: row '%s' lost focus while locked — force-unlocking", title) + } isAdjustmentLocked = false } } @@ -1525,12 +1916,29 @@ private fun QuickMenuAdjustmentRow( .onPreviewKeyEvent { keyEvent -> if (keyEvent.nativeKeyEvent.action == KeyEvent.ACTION_DOWN && isFocused) { when { - keyEvent.nativeKeyEvent.keyCode == KeyEvent.KEYCODE_BUTTON_A -> { - isAdjustmentLocked = !isAdjustmentLocked + // A only LOCKS, never toggles off — a repeated A press (whether the user + // meant to confirm again, or it arrived via the DPAD_CENTER/BUTTON_A + // fallback dispatched for the "nothing else focused" case, see + // ImmersiveXrActivity's BUTTON_A handling) must never undo an in-progress + // adjustment. Only BUTTON_B or losing focus unlocks — see below and + // onFocusChanged. Confirmed via logs: this WAS toggling off mid-adjustment + // ("row lock toggled to false" from a genuine BUTTON_A press, not B), + // exactly matching "one tick works, then I need B to get back in". + !isAdjustmentLocked && keyEvent.nativeKeyEvent.keyCode == KeyEvent.KEYCODE_BUTTON_A -> { + isAdjustmentLocked = true + Timber.i("QuickMenu: row '%s' locked", title) true } - isAdjustmentLocked && keyEvent.nativeKeyEvent.keyCode == KeyEvent.KEYCODE_BUTTON_B -> { + // ImmersiveXrActivity's B handler dispatches KEYCODE_BACK, not + // KEYCODE_BUTTON_B (see triggerMenuDirection's caller) — this never + // matched before, so B was "working" only by accident: the unconsumed + // BACK event propagated further and cleared focus, which onFocusChanged + // then read as an unlock. Checking BACK directly makes this an explicit, + // consumed unlock instead of a side effect of losing focus. + isAdjustmentLocked && + (keyEvent.nativeKeyEvent.keyCode == KeyEvent.KEYCODE_BUTTON_B || + keyEvent.nativeKeyEvent.keyCode == KeyEvent.KEYCODE_BACK) -> { isAdjustmentLocked = false true } @@ -1770,7 +2178,6 @@ private fun QuickMenuToggleRow( indication = null, onClick = onToggle, ) - .focusable(interactionSource = interactionSource) .padding(horizontal = 16.dp, vertical = 14.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(16.dp), @@ -1995,10 +2402,6 @@ private fun QuickMenuItemRow( indication = null, onClick = onClick, ) - .focusable( - enabled = isEnabled, - interactionSource = interactionSource, - ) .padding(horizontal = 12.dp, vertical = 14.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(16.dp), diff --git a/app/src/main/java/app/gamenative/ui/component/ScreenEffectsPanel.kt b/app/src/main/java/app/gamenative/ui/component/ScreenEffectsPanel.kt index 6389ad6429..f54522dd2b 100644 --- a/app/src/main/java/app/gamenative/ui/component/ScreenEffectsPanel.kt +++ b/app/src/main/java/app/gamenative/ui/component/ScreenEffectsPanel.kt @@ -974,6 +974,15 @@ private fun ScreenEffectAdjustmentRow( val accentColor = PluviaTheme.colors.accentPurple val shape = RoundedCornerShape(14.dp) var isAdjustmentLocked by remember { mutableStateOf(false) } + // See QuickMenu.kt's LocalImmersiveInputBypass kdoc (identical mechanism as + // QuickMenuAdjustmentRow, kept in sync) — reports (onDecrease, onIncrease) up while this row + // is both focused and lock-toggled, null otherwise, so the Meta Quest immersive activity + // (which bypasses this row's own onPreviewKeyEvent entirely for arrow keys, since synthetic + // dpad KeyEvents never reach Compose's key dispatch in that Activity) can still drive it. + val inputBypass = LocalImmersiveInputBypass.current + LaunchedEffect(isFocused, isAdjustmentLocked) { + inputBypass.reportAdjustment(if (isFocused && isAdjustmentLocked) (onDecrease to onIncrease) else null) + } Column( modifier = Modifier @@ -1023,12 +1032,20 @@ private fun ScreenEffectAdjustmentRow( .onPreviewKeyEvent { keyEvent -> if (keyEvent.nativeKeyEvent.action == KeyEvent.ACTION_DOWN && isFocused) { when { - keyEvent.nativeKeyEvent.keyCode == KeyEvent.KEYCODE_BUTTON_A -> { - isAdjustmentLocked = !isAdjustmentLocked + // A only LOCKS, never toggles off — see QuickMenuAdjustmentRow's identical + // handler for why (a repeated A press must never undo an in-progress + // adjustment). Only BUTTON_B or losing focus unlocks. + !isAdjustmentLocked && keyEvent.nativeKeyEvent.keyCode == KeyEvent.KEYCODE_BUTTON_A -> { + isAdjustmentLocked = true true } - isAdjustmentLocked && keyEvent.nativeKeyEvent.keyCode == KeyEvent.KEYCODE_BUTTON_B -> { + // ImmersiveXrActivity's B handler dispatches KEYCODE_BACK, not + // KEYCODE_BUTTON_B — see QuickMenuAdjustmentRow's identical handler for + // why both are checked here. + isAdjustmentLocked && + (keyEvent.nativeKeyEvent.keyCode == KeyEvent.KEYCODE_BUTTON_B || + keyEvent.nativeKeyEvent.keyCode == KeyEvent.KEYCODE_BACK) -> { isAdjustmentLocked = false true } @@ -1286,6 +1303,17 @@ private fun ScreenEffectRadioRow( ) { val interactionSource = remember { MutableInteractionSource() } val isFocused by interactionSource.collectIsFocusedAsState() + // Reports onSelect up while this row is focused, null otherwise — see + // LocalImmersiveInputBypass's kdoc (QuickMenu.kt): a plain .selectable() relies on Android's + // default "DPAD_CENTER/A clicks whatever holds real view focus" behavior, which the Meta + // Quest immersive activity found to be unreliable for this exact case (real Android view + // focus doesn't always track Compose's own internal focus there — confirmed separately for + // tab-rail buttons and adjustment-row locks earlier this session). Lets that Activity invoke + // onSelect directly instead of gambling on the default click path. + val inputBypass = LocalImmersiveInputBypass.current + LaunchedEffect(isFocused) { + inputBypass.reportActivate(if (isFocused) onSelect else null) + } val accentColor = PluviaTheme.colors.accentPurple val shape = RoundedCornerShape(14.dp) diff --git a/app/src/main/java/app/gamenative/ui/model/MainViewModel.kt b/app/src/main/java/app/gamenative/ui/model/MainViewModel.kt index a70852c2e5..4702c33f14 100644 --- a/app/src/main/java/app/gamenative/ui/model/MainViewModel.kt +++ b/app/src/main/java/app/gamenative/ui/model/MainViewModel.kt @@ -224,6 +224,18 @@ class MainViewModel @Inject constructor( setShowBootingSplash(true) } + // ImmersiveXrActivity hosts its own separate MainViewModel instance (different Activity) — + // its exit/onWindowMapped/onGameLaunchError calls only reset *that* instance's splash, while + // MainActivity's own (backgrounded but still subscribed to SetBootingSplashText) instance + // keeps getting re-armed by every splash-text update XServerScreen emits during the immersive + // session's boot sequence, and is never told the session ended. Broadcasting this on the same + // global bus lets every subscribed instance — whichever Activity it belongs to — clear itself. + private val onClearBootingSplash: (AndroidEvent.ClearBootingSplash) -> Unit = { + bootingSplashTimeoutJob?.cancel() + bootingSplashTimeoutJob = null + setShowBootingSplash(false) + } + private var bootingSplashTimeoutJob: Job? = null private var connectionTimeoutJob: Job? = null @@ -258,6 +270,7 @@ class MainViewModel @Inject constructor( PluviaApp.events.on(onBackPressed) PluviaApp.events.on(onExternalGameLaunch) PluviaApp.events.on(onSetBootingSplashText) + PluviaApp.events.on(onClearBootingSplash) PluviaApp.events.on(onSteamConnected) PluviaApp.events.on(onSteamDisconnected) PluviaApp.events.on(onRemotelyDisconnected) @@ -284,6 +297,7 @@ class MainViewModel @Inject constructor( PluviaApp.events.off(onBackPressed) PluviaApp.events.off(onExternalGameLaunch) PluviaApp.events.off(onSetBootingSplashText) + PluviaApp.events.off(onClearBootingSplash) PluviaApp.events.off(onSteamConnected) PluviaApp.events.off(onSteamDisconnected) PluviaApp.events.off(onRemotelyDisconnected) @@ -559,7 +573,30 @@ class MainViewModel @Inject constructor( apiJob.await() - _uiEvent.send(MainUiEvent.LaunchApp) + if (container.isLaunchImmersiveMode() && app.gamenative.MainActivity.isMetaQuest(context)) { + // Immersive/VR launches always boot straight to the executable — there is no + // "open the Wine desktop" variant for this entry point. Placed after apiJob so + // the Steam DRM/API patch step above (replaceSteamApi/replaceSteamclientDll) + // still runs — skipping it made steamclient_loader_x64.exe fail to find files + // it depends on, for every game, not just DRM-heavy ones. + // + // ImmersiveXrActivity has its own separate MainViewModel instance (different + // Activity), so it can't clear *this* instance's booting splash — MainActivity + // is about to lose focus to that Activity anyway, so just hide it now instead + // of leaving it stuck on "Launching..." forever. + bootingSplashTimeoutJob?.cancel() + bootingSplashTimeoutJob = null + setShowBootingSplash(false) + // MainActivity is about to onStop() once ImmersiveXrActivity takes focus; its + // onStop/onDestroy checks treat !keepAlive as "nothing running, disconnect Steam" + // (see MainActivity.kt onStop/onDestroy), which was silently logging the whole + // session off for the entire immersive session — forcing a full relogin + license + // resync on return. shutdownEnvironment() already clears this back to false on exit. + SteamService.keepAlive = true + app.gamenative.ui.screen.xr.ImmersiveXrActivity.start(context, appId, _offline.value) + } else { + _uiEvent.send(MainUiEvent.LaunchApp) + } } } @@ -570,6 +607,11 @@ class MainViewModel @Inject constructor( bootingSplashTimeoutJob?.cancel() bootingSplashTimeoutJob = null setShowBootingSplash(false) + // Also broadcast globally: when this runs from ImmersiveXrActivity's own separate + // MainViewModel instance, MainActivity's (backgrounded but still subscribed to + // SetBootingSplashText) instance never otherwise learns the session ended — see + // onClearBootingSplash's kdoc. + PluviaApp.events.emit(AndroidEvent.ClearBootingSplash) // Check if we have a temporary override before doing anything val hadTemporaryOverride = IntentLaunchManager.hasTemporaryOverride(appId) @@ -694,6 +736,9 @@ class MainViewModel @Inject constructor( bootingSplashTimeoutJob?.cancel() bootingSplashTimeoutJob = null setShowBootingSplash(false) + // See onClearBootingSplash's kdoc — broadcast so MainActivity's own instance clears + // too when this call is actually running on ImmersiveXrActivity's separate instance. + PluviaApp.events.emit(AndroidEvent.ClearBootingSplash) val gameId = ContainerUtils.extractGameIdFromContainerId(appId) @@ -756,6 +801,9 @@ class MainViewModel @Inject constructor( bootingSplashTimeoutJob?.cancel() bootingSplashTimeoutJob = null setShowBootingSplash(false) + // See onClearBootingSplash's kdoc — broadcast so MainActivity's own instance clears + // too when this call is actually running on ImmersiveXrActivity's separate instance. + PluviaApp.events.emit(AndroidEvent.ClearBootingSplash) // You could also show an error dialog here if needed Timber.tag("MainViewModel").e("Game launch error: $error") diff --git a/app/src/main/java/app/gamenative/ui/screen/library/LibraryAppScreen.kt b/app/src/main/java/app/gamenative/ui/screen/library/LibraryAppScreen.kt index 55b94093b1..ea8a747c1a 100644 --- a/app/src/main/java/app/gamenative/ui/screen/library/LibraryAppScreen.kt +++ b/app/src/main/java/app/gamenative/ui/screen/library/LibraryAppScreen.kt @@ -61,6 +61,8 @@ import androidx.compose.material.icons.filled.Settings import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Checkbox +import androidx.compose.material3.CheckboxDefaults import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.LinearProgressIndicator @@ -552,6 +554,16 @@ private fun formatBytes(bytes: Long): String { } } +// Bundled into one parameter to keep AppScreenContent's own parameter count with headroom +// below the threshold where Compose starts emitting a second "changed" bitmask int — this +// composable has crashed with an ART VerifyError on API < 29 (Meta Quest legacyXr) once it +// crossed that line. +internal data class ImmersiveModeUiState( + val isSupported: Boolean = false, + val isEnabled: Boolean = false, + val onChange: (Boolean) -> Unit = {}, +) + @Composable internal fun AppScreenContent( modifier: Modifier = Modifier, @@ -571,6 +583,7 @@ internal fun AppScreenContent( onBack: () -> Unit = {}, optionsMenu: List, dialogOpen: Boolean = false, + immersiveMode: ImmersiveModeUiState = ImmersiveModeUiState(), ) { val context = LocalContext.current // reactive — recomposes when network state changes @@ -971,6 +984,29 @@ internal fun AppScreenContent( } } + if (immersiveMode.isSupported && isInstalled) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(top = 4.dp) + .clickable { immersiveMode.onChange(!immersiveMode.isEnabled) }, + verticalAlignment = Alignment.CenterVertically, + ) { + Checkbox( + checked = immersiveMode.isEnabled, + onCheckedChange = immersiveMode.onChange, + colors = CheckboxDefaults.colors( + uncheckedColor = Color.White.copy(alpha = 0.7f), + ), + ) + Text( + text = stringResource(R.string.launch_immersive_mode), + style = MaterialTheme.typography.bodyMedium, + color = Color.White, + ) + } + } + if (isDownloading && isPortrait) { Row( modifier = Modifier diff --git a/app/src/main/java/app/gamenative/ui/screen/library/appscreen/BaseAppScreen.kt b/app/src/main/java/app/gamenative/ui/screen/library/appscreen/BaseAppScreen.kt index 7e6521711b..93222e2942 100644 --- a/app/src/main/java/app/gamenative/ui/screen/library/appscreen/BaseAppScreen.kt +++ b/app/src/main/java/app/gamenative/ui/screen/library/appscreen/BaseAppScreen.kt @@ -1052,6 +1052,24 @@ abstract class BaseAppScreen { mutableStateOf(hasLeftoverInstall(context, libraryItem)) } + // Immersive/VR launch mode is only offered on Meta Quest, and only for games running + // through the Wine/Proton path — a native Android (Steam Frame) install never goes + // through ImmersiveXrActivity (MainViewModel returns early for those, before ever + // looking at this flag), so offering the toggle there would be misleading. + val isImmersiveModeSupported = remember(libraryItem.appId) { + app.gamenative.MainActivity.isMetaQuest(context) && + !app.gamenative.service.SteamService.isAndroidPlatform(libraryItem.gameId) + } + var isImmersiveModeEnabledState by remember(libraryItem.appId) { mutableStateOf(false) } + if (isImmersiveModeSupported) { + LaunchedEffect(libraryItem.appId) { + isImmersiveModeEnabledState = withContext(Dispatchers.IO) { + runCatching { ContainerUtils.getContainer(context, libraryItem.appId).isLaunchImmersiveMode() } + .getOrDefault(false) + } + } + } + // hasAndroidVersion() can hit the database (SteamService.getAppInfoOf does a blocking // Room query) — compute it off the main thread once per screen visit, instead of // re-running it synchronously on the main thread every time Edit Container is opened. @@ -1396,6 +1414,20 @@ abstract class BaseAppScreen { hasLeftoverInstall = hasLeftoverInstallState, isUpdatePending = isUpdatePendingState, downloadInfo = downloadInfo, + immersiveMode = app.gamenative.ui.screen.library.ImmersiveModeUiState( + isSupported = isImmersiveModeSupported, + isEnabled = isImmersiveModeEnabledState, + onChange = { enabled -> + isImmersiveModeEnabledState = enabled + uiScope.launch(Dispatchers.IO) { + runCatching { + val container = ContainerUtils.getContainer(context, libraryItem.appId) + container.setLaunchImmersiveMode(enabled) + container.saveData() + } + } + }, + ), onDownloadInstallClick = { if (app.gamenative.launch.LaunchReadiness.pending) { showReadiness = true diff --git a/app/src/main/java/app/gamenative/ui/screen/xr/DirectGLBridge.kt b/app/src/main/java/app/gamenative/ui/screen/xr/DirectGLBridge.kt new file mode 100644 index 0000000000..45fbce78e8 --- /dev/null +++ b/app/src/main/java/app/gamenative/ui/screen/xr/DirectGLBridge.kt @@ -0,0 +1,126 @@ +package app.gamenative.ui.screen.xr + +import android.hardware.HardwareBuffer +import android.opengl.GLES20 +import com.winlator.renderer.GLHardwareBufferImporter +import com.winlator.renderer.GLRenderer +import com.winlator.renderer.XrFrameBridge +import timber.log.Timber + +/** + * [XrFrameBridge] implementation for the legacy GL renderer path: allocates a + * [HardwareBuffer] (Android's cross-process/cross-context shareable GPU memory — the same + * mechanism Camera2/MediaCodec use to hand frames around without a CPU copy), imports it as a + * GL texture wrapped in an FBO *inside GLRenderer's own EGL context* (via + * [GLHardwareBufferImporter] — the public SDK has no usable Java binding for the + * eglGetNativeClientBufferANDROID/eglCreateImageKHR sequence this needs, so that import happens + * in native code), and hands the same buffer to the native OpenXR session so it can import it a + * second time into *its own* context and sample it directly — no PixelCopy, no CPU-side Bitmap, + * no glTexImage2D upload. + * + * Only used for GLRenderer (the legacy GL rendering path). Most containers default to + * dxvk (D3D->Vulkan), which renders through VulkanRenderer instead — a completely separate, + * Vulkan-native presentation path this bridge does not cover. That's a distinct, larger + * follow-up (needs OpenXR's Vulkan interop, not this GLES-based approach) — see the + * conversation this was scoped in. This class exists to validate the whole + * "render directly into a GPU buffer the XR session samples" approach end-to-end first, on the + * simpler of the two renderers. + */ +class DirectGLBridge( + private val onBufferReady: (HardwareBuffer) -> Unit, +) : XrFrameBridge { + + private var width = 0 + private var height = 0 + private var hardwareBuffer: HardwareBuffer? = null + private var texture = 0 + private var framebuffer = 0 + private var initFailed = false + private var announcedReady = false + + /** Must be called from GLRenderer's own GL thread (e.g. via GLSurfaceView.queueEvent) — + * everything here (buffer import, texture/FBO creation) needs the calling EGL context to be + * current. Safe to call again with a new size; the old buffer/resources are released first. */ + fun ensureAllocated(targetWidth: Int, targetHeight: Int) { + if (initFailed) return + if (hardwareBuffer != null && targetWidth == width && targetHeight == height) return + if (targetWidth <= 0 || targetHeight <= 0) return + + release() + width = targetWidth + height = targetHeight + + val buffer = try { + HardwareBuffer.create( + width, + height, + HardwareBuffer.RGBA_8888, + 1, + HardwareBuffer.USAGE_GPU_COLOR_OUTPUT or HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE, + ) + } catch (t: Throwable) { + Timber.e(t, "Immersive: DirectGLBridge failed to allocate HardwareBuffer %dx%d — falling back to PixelCopy", width, height) + initFailed = true + return + } + + val importedTexture = GLHardwareBufferImporter.importAsTexture(buffer) + if (importedTexture == 0) { + Timber.e("Immersive: DirectGLBridge native import failed — falling back to PixelCopy") + buffer.close() + initFailed = true + return + } + + val framebuffers = IntArray(1) + GLES20.glGenFramebuffers(1, framebuffers, 0) + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, framebuffers[0]) + GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, importedTexture, 0) + val fboStatus = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER) + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0) + if (fboStatus != GLES20.GL_FRAMEBUFFER_COMPLETE) { + Timber.e("Immersive: DirectGLBridge FBO incomplete (0x%x) — falling back to PixelCopy", fboStatus) + GLES20.glDeleteTextures(1, intArrayOf(importedTexture), 0) + GLES20.glDeleteFramebuffers(1, framebuffers, 0) + buffer.close() + initFailed = true + return + } + + hardwareBuffer = buffer + texture = importedTexture + framebuffer = framebuffers[0] + Timber.i("Immersive: DirectGLBridge allocated shared buffer %dx%d, fbo=%d texture=%d", width, height, framebuffer, texture) + + if (!announcedReady) { + announcedReady = true + onBufferReady(buffer) + } + } + + override fun beginFrame(): IntArray? { + val fbo = framebuffer + if (fbo == 0) return null + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fbo) + return intArrayOf(width, height) + } + + override fun endFrame() { + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0) + } + + fun release() { + if (texture != 0) GLES20.glDeleteTextures(1, intArrayOf(texture), 0) + if (framebuffer != 0) GLES20.glDeleteFramebuffers(1, intArrayOf(framebuffer), 0) + hardwareBuffer?.close() + texture = 0 + framebuffer = 0 + hardwareBuffer = null + announcedReady = false + } + + companion object { + /** Only meaningful for the legacy GL renderer — see this class's kdoc. */ + fun supportsRenderer(renderer: Any?): Boolean = renderer is GLRenderer + } +} diff --git a/app/src/main/java/app/gamenative/ui/screen/xr/DirectVulkanBridge.kt b/app/src/main/java/app/gamenative/ui/screen/xr/DirectVulkanBridge.kt new file mode 100644 index 0000000000..fdb76cb260 --- /dev/null +++ b/app/src/main/java/app/gamenative/ui/screen/xr/DirectVulkanBridge.kt @@ -0,0 +1,38 @@ +package app.gamenative.ui.screen.xr + +import com.winlator.renderer.VulkanXrFrameBridge +import timber.log.Timber + +/** + * [VulkanXrFrameBridge] implementation for the default (dxvk/Vulkan) renderer path — unlike + * [DirectGLBridge], there's no buffer allocation or import to do here at all: VulkanRenderer + * already turns the game's rendered frame into an AHardwareBuffer on every real frame (see + * VulkanRenderer.onUpdateWindowContentDirect's nativeUpdateWindowContentAHB call — this is the + * method PresentExtension actually routes real DXVK/Vulkan frame presents to, NOT + * onUpdateWindowContent), so this just forwards that same raw pointer to the native OpenXR + * session, which imports it the same way + * [DirectGLBridge]'s shared buffer is imported (see xr_immersive.cpp's + * importSharedBufferIfNeeded — it doesn't care which renderer produced the buffer). + * + * [onFrame] fires on essentially every frame a Vulkan-rendered window updates — there's no + * "only when some other condition is met" gate here (an earlier version assumed one tied to + * VulkanRenderer's "zero-copy scanout" path, which turned out to be unreachable dead code — + * see VulkanXrFrameBridge's kdoc). + */ +class DirectVulkanBridge( + private val onFrame: (ahbPtr: Long, width: Int, height: Int) -> Unit, +) : VulkanXrFrameBridge { + private var announced = false + + override fun onScanoutBuffer(ahbPtr: Long, width: Int, height: Int) { + if (!announced) { + announced = true + Timber.i( + "Immersive: VulkanRenderer AHardwareBuffer observed (%dx%d) — direct-render path active, PixelCopy of the game layer stopped", + width, + height, + ) + } + onFrame(ahbPtr, width, height) + } +} diff --git a/app/src/main/java/app/gamenative/ui/screen/xr/ImmersiveControls.kt b/app/src/main/java/app/gamenative/ui/screen/xr/ImmersiveControls.kt new file mode 100644 index 0000000000..a83eaefbee --- /dev/null +++ b/app/src/main/java/app/gamenative/ui/screen/xr/ImmersiveControls.kt @@ -0,0 +1,44 @@ +package app.gamenative.ui.screen.xr + +/** + * State + callbacks for the "Immersif" quick-menu tab (see QuickMenu.kt). Passed into + * [app.gamenative.ui.screen.xserver.XServerScreen] only when running inside + * [ImmersiveXrActivity] — its presence is what makes the tab show up at all. + */ +data class ImmersiveControls( + val passthroughEnabled: Boolean, + val onPassthroughToggle: (Boolean) -> Unit, + // No slider-based position/size controls here anymore, by explicit request — the + // trigger-driven grab handles (ImmersiveXrActivity's own quadDistance/quadHorizontal/ + // quadVertical/quadScale fields, moved via tryStartPointerGrab/updateActiveGrab) are the ONLY + // way to move/resize the quad now, not an alternative alongside a slider-based one. + // Vulkan's zero-copy direct-render path (better performance) only engages when no screen + // effect/filter/brightness/contrast/gamma adjustment is active — null when the renderer isn't + // VulkanRenderer at all (nothing to reset), so the button only shows when it's actually + // relevant and possible. + val directRenderBlockedByEffects: Boolean? = null, + val onResetScreenEffects: () -> Unit = {}, + // Shows/enables the resize+move handles (drawn just inside the game content's own edges, + // not in an outside margin — see ImmersiveXrActivity's resizeHandlesEnabled kdoc for why: + // an always-present margin band cost a real chunk of the direct-render path's fixed + // swapchain pixel budget, visible as pixelation). + val resizeModeEnabled: Boolean = false, + val onResizeModeToggle: (Boolean) -> Unit = {}, +) { + companion object { + const val MIN_DISTANCE = 1.0f + const val MAX_DISTANCE = 5.0f + const val DEFAULT_DISTANCE = 2.0f + + const val MIN_OFFSET = -2.0f + const val MAX_OFFSET = 2.0f + const val DEFAULT_OFFSET = 0.0f + + const val MIN_SCALE = 0.5f + const val MAX_SCALE = 3.0f + const val DEFAULT_SCALE = 1.0f + + const val BASE_WIDTH_METERS = 1.6f + const val BASE_HEIGHT_METERS = 0.9f + } +} diff --git a/app/src/main/java/app/gamenative/ui/screen/xr/ImmersiveXrActivity.kt b/app/src/main/java/app/gamenative/ui/screen/xr/ImmersiveXrActivity.kt new file mode 100644 index 0000000000..c849faab76 --- /dev/null +++ b/app/src/main/java/app/gamenative/ui/screen/xr/ImmersiveXrActivity.kt @@ -0,0 +1,1768 @@ +package app.gamenative.ui.screen.xr + +import android.content.Context +import android.content.Intent +import android.graphics.Bitmap +import android.os.Bundle +import android.os.Handler +import android.os.HandlerThread +import android.view.PixelCopy +import android.view.SurfaceView +import androidx.activity.compose.setContent +import androidx.activity.viewModels +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Menu +import androidx.compose.material.icons.filled.SportsEsports +import androidx.compose.material.icons.filled.TouchApp +import androidx.compose.material.icons.filled.VisibilityOff +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.lifecycle.lifecycleScope +import kotlinx.coroutines.delay +import app.gamenative.PluviaApp +import app.gamenative.R +import app.gamenative.service.SteamService +import app.gamenative.ui.model.MainViewModel +import app.gamenative.ui.screen.xserver.XServerScreen +import app.gamenative.ui.theme.PluviaTheme +import app.gamenative.utils.ContainerUtils +import com.winlator.container.Container +import com.winlator.core.AppUtils +import com.winlator.renderer.GLRenderer +import com.winlator.winhandler.WinHandler +import dagger.hilt.android.AndroidEntryPoint +import java.util.concurrent.atomic.AtomicBoolean +import kotlin.concurrent.thread +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import timber.log.Timber + +/** + * Dedicated entry point for launching a game directly in Meta Quest immersive mode. + * + * Unlike the normal Play flow (which navigates within [app.gamenative.MainActivity]'s + * nav-graph), this always boots straight to the configured executable + * (bootToContainer = false) — there is no "open the Wine desktop" variant here. + * + * Hosts the same [XServerScreen] composable used by the normal flow (bootstrap, Wine/Box64 + * launch, on-screen controls, quick menu, etc. are all reused as-is) and layers a native + * OpenXR session on top for the actual immersive presentation + Touch controller input — + * see app/src/main/cpp/xrimmersive. The game's actual rendered frame is captured off the + * existing on-screen [PluviaApp.xServerView] via [PixelCopy] (works for both the Vulkan and + * legacy GL renderer, no changes to GLRenderer/XServerView needed) and handed to the native + * module, which uploads it into the OpenXR quad layer every frame — this is not a placeholder, + * it shows the actual running game. + */ +@AndroidEntryPoint +class ImmersiveXrActivity : androidx.activity.ComponentActivity() { + + companion object { + private const val EXTRA_APP_ID = "app_id" + private const val EXTRA_IS_OFFLINE = "is_offline" + private const val POLL_INTERVAL_MS = 11L // ~90Hz, matches the Quest's native refresh rate + // While the game window isn't up yet. Kept well above a tight spin: PixelCopy on a + // Surface that's mid-attach can crash *natively* (confirmed via tombstone — see + // scheduleNextCapture's isValid() comment) in a way no try/catch here can stop, so + // fewer attempts during that startup window means less exposure to the race, not just + // less log spam. + private const val CAPTURE_RETRY_DELAY_MS = 200L + private const val OVERLAY_REFRESH_INTERVAL_MS = 33L // ~30fps — plenty for a menu/HUD, + // and walking the whole Compose tree at full game-frame rate is what caused the crash. + + // Horizon OS hands this Activity a volumetric panel surface at ~4128x2208 physical px but + // only reports density=1.25 (adb: `wm density` -> 200), i.e. a ~3300x1770dp logical canvas. + // QuickMenu/XServerScreen were laid out assuming a normal phone/tablet-sized dp canvas, so + // at that density every fixed-dp element (rail icons, text) renders as a tiny sliver pinned + // to one corner. Overriding density here forces a sane logical resolution instead. + private const val IMMERSIVE_UI_DENSITY = 2.5f + + // Left-stick-as-dpad tuning for menu navigation (see lastMenuDpadKeyCode). + private const val MENU_DPAD_AXIS_THRESHOLD = 0.5f + private const val MENU_DPAD_INITIAL_DELAY_MS = 400L + private const val MENU_DPAD_REPEAT_DELAY_MS = 150L + + // XR pointer-mode grab handles, in meters (same space as the quad transform). + // + // No margin: the game's own displayed size is IDENTICAL in every controller mode, and + // never grows for a margin band either — an earlier version grew the quad by a fixed + // margin on every side to give handles somewhere to live outside the game, but that + // margin came out of the direct-render path's fixed 1280x720 swapchain pixel budget + // (kSwapchainWidth/Height in xr_immersive.cpp) — a real, measured ~45% pixel-area loss at + // default scale, visible as pixelation. Handles now live INSIDE the content instead (see + // drawPointerCursors/nearGrabZone), only while resizeHandlesEnabled is on. + // + // Small visible gap between the game content's own edge and the drawn handle — sitting + // flush against the game's pixels reads as "stuck to the screen" rather than a floating + // system-style indicator. + private const val POINTER_INDICATOR_GAP_METERS = 0.02f + // Small round handles (corners + top/bottom edge midpoints), not long bars or crosses — + // closer to how actual VR headset system UI marks a grabbable edge: a small, elegant, + // consistent marker, not a shape that dominates the edge it's next to. + private const val POINTER_HANDLE_RADIUS_METERS = 0.035f + // Idle stroke is 2x the original 3.5f base, hover/grab is 3x — a thin line staying thin + // was easy to lose track of exactly when a hand was actually aiming at it. + private const val POINTER_HANDLE_STROKE_WIDTH_IDLE_PX = 7.0f + private const val POINTER_HANDLE_STROKE_WIDTH_ACTIVE_PX = 10.5f + // Shrunk from an earlier 0.16f — that radius, sitting right next to the quick menu's own + // buttons now that handles are drawn inside the content, was catching the pointer well + // before it was actually on a handle and blocking clicks on whatever's underneath. Small + // enough now that it takes deliberately aiming at the handle itself, not just its general + // neighborhood. + private const val POINTER_CORNER_RADIUS_METERS = 0.06f + private const val POINTER_BAR_HALF_WIDTH_METERS = 0.08f + private const val POINTER_BAR_RADIUS_METERS = 0.06f + // Hysteresis, not a single threshold: without it, an analog trigger resting near the + // threshold (hand tremor holding it "about half pulled") flaps press/release dozens of + // times a second — logs showed a grab restarting every ~250-300ms while the user held + // still, which is exactly that. Needs a firm pull past PRESS to start, only lets go past + // the much lower RELEASE. + private const val POINTER_GRAB_PRESS_THRESHOLD = 0.65f + private const val POINTER_GRAB_RELEASE_THRESHOLD = 0.3f + private const val MIN_CAPTURE_INTERVAL_MS = 11L // ~90fps cap, matches the HMD's own fixed + // compositor rate — capturing/uploading faster than the display can show is pure waste, + // competing with the game's own rendering for the same GPU for no visible benefit. + + // Container extras — persist the "Immersif" quick-menu tab's settings per game. + private const val EXTRA_QUAD_DISTANCE = "immersiveQuadDistance" + private const val EXTRA_QUAD_SCALE = "immersiveQuadScale" + private const val EXTRA_PASSTHROUGH_ENABLED = "immersivePassthroughEnabled" + + fun start(context: Context, appId: String, isOffline: Boolean) { + val intent = Intent(context, ImmersiveXrActivity::class.java).apply { + putExtra(EXTRA_APP_ID, appId) + putExtra(EXTRA_IS_OFFLINE, isOffline) + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + } + context.startActivity(intent) + } + } + + private val viewModel: MainViewModel by viewModels() + + @Volatile + private var backAction: (() -> Unit)? = null + + // Dedicated open/close toggle for the long-press-Start gesture — see XServerScreen's + // toggleQuickMenu kdoc for why this is used instead of backAction/gameBack. + @Volatile + private var quickMenuToggle: (() -> Unit)? = null + + // See QuickMenu's registerFocusManager kdoc — driving Compose focus directly, bypassing + // Activity.dispatchKeyEvent() for arrow-key navigation (confirmed via logging that dispatched + // synthetic dpad KeyEvents never reach Compose's key dispatch in this Activity at all). + @Volatile + private var quickMenuFocusManager: androidx.compose.ui.focus.FocusManager? = null + + // Same bypass as quickMenuFocusManager, for LB/RB tab-cycling — see QuickMenu's + // registerCycleTab kdoc. + @Volatile + private var quickMenuCycleTab: ((Boolean) -> Unit)? = null + + // Same bypass again, for a locked QuickMenuAdjustmentRow's left/right drag — see QuickMenu's + // registerAdjustmentControl kdoc. Invoking this returns (onDecrease, onIncrease) for whichever + // row is currently focused+lock-toggled, or null if none is. + @Volatile + private var quickMenuAdjustmentControl: (() -> Pair<() -> Unit, () -> Unit>?)? = null + + // Same bypass again, for LEFT specifically — see QuickMenu's registerFocusTabRail kdoc. + // FocusManager.moveFocus(Left) reaching the tab rail from content turned out to depend on the + // focused row's vertical position lining up with a rail icon (confirmed by comparing two + // sessions' logs: identical code, one where Left worked and one where it didn't, differing + // only in which row happened to be focused) — this calls requestFocus() on the CURRENTLY + // SELECTED tab's own rail button directly instead, which doesn't depend on that. + @Volatile + private var quickMenuFocusTabRail: (() -> Unit)? = null + + // Same bypass again, for a plain radio/toggle row (e.g. ScreenEffectsPanel's scaling-mode + // rows) whose default DPAD_CENTER/A click doesn't reliably fire here — see QuickMenu's + // registerFocusedActivate kdoc. Invoking this returns whichever row's onSelect is currently + // focused, or null if none is. + @Volatile + private var quickMenuFocusedActivate: (() -> (() -> Unit)?)? = null + + // Pushes the Menu/Start button's physical-hold state (native flags[2]) into QuickMenu, which + // uses it to suppress the real Android KeyEvents (DPAD_CENTER/BUTTON_A/BUTTON_START) that + // Horizon OS's own gamepad input dispatch generates while that button is held — a completely + // separate path from this Activity's native OpenXR polling, immune to every other suppression + // here. + @Volatile + private var quickMenuSetStartHeld: ((Boolean) -> Unit)? = null + private var lastPushedStartHeld = false + + private var xrSessionHandle: Long = 0L + private var currentAppId: String? = null + + private var pollingThread: Thread? = null + private val pollingActive = AtomicBoolean(false) + private var cachedWinHandler: WinHandler? = null + + // Tracks Surface readiness via the actual SurfaceHolder lifecycle callbacks rather than + // polling isValid() — confirmed by testing (two native tombstones so far, same signature: + // SIGSEGV inside PixelCopy.request()'s own ANativeWindow_acquire) that isValid() can still + // race against the Surface being attached, since it's a point-in-time check with no ordering + // guarantee relative to the native call that follows it. surfaceCreated/surfaceDestroyed are + // real lifecycle events for this exact Surface, so gating on them is a much narrower window, + // even if not a mathematically airtight guarantee against every possible interleaving. + @Volatile + private var surfaceReady = false + private var surfaceCallbackAttachedTo: SurfaceView? = null + private val surfaceReadyCallback = object : android.view.SurfaceHolder.Callback { + override fun surfaceCreated(holder: android.view.SurfaceHolder) { + surfaceReady = true + } + override fun surfaceChanged(holder: android.view.SurfaceHolder, format: Int, width: Int, height: Int) {} + override fun surfaceDestroyed(holder: android.view.SurfaceHolder) { + surfaceReady = false + } + } + private var cachedBridge: XrGamepadBridge? = null + + private var captureThread: HandlerThread? = null + private var captureHandler: Handler? = null + private val captureActive = AtomicBoolean(false) + // Three bitmaps for the two decoupled loops (see startFrameCaptureLoop's kdoc): + // - gameCaptureBitmap: written by PixelCopy, at full game-frame rate. + // - overlayLayerBitmap: written by a real View.draw(), at a much slower throttled rate — + // guarded by overlayLock since it's read by the other (faster, background-thread) loop. + // - finalFrameBitmap: the actual composite of the two, uploaded to the native module. + private var gameCaptureBitmap: Bitmap? = null + private val overlayLock = Any() + private var overlayLayerBitmap: Bitmap? = null + private var finalFrameBitmap: Bitmap? = null + private var overlayCaptureLogCounter = 0 + private var pointerGripHeldLogCounter = 0 + + // Direct GPU-render path — GLRenderer via DirectGLBridge/GLHardwareBufferImporter, or + // VulkanRenderer (the default for most real games/dxvk) via DirectVulkanBridge reusing its + // own per-frame AHardwareBuffer (see VulkanXrFrameBridge's kdoc for why this isn't actually + // tied to VulkanRenderer's own "zero-copy scanout" path, despite the name). Both are attached + // opportunistically and are no-ops + // until (and unless) their renderer type is actually found. directRenderActive only flips + // true once a real frame has actually flowed through — for GLRenderer that's guaranteed on + // the next frame once attached, but VulkanRenderer's scanout is conditional (disabled while + // screen-effect filters force the compositor path), so scheduleNextCapture keeps PixelCopy + // running as a fallback until this is confirmed true, not just whenever a bridge is attached. + // Known limitation: if scanout later deactivates mid-session (e.g. an effect toggled on), + // this does not currently fall back to PixelCopy again — out of scope for this pass. + private var directGLBridge: DirectGLBridge? = null + private var directVulkanBridge: DirectVulkanBridge? = null + @Volatile + private var directRenderActive = false + private var directRenderProbeLogged = false + + // Menu navigation state: while quickMenuVisible, the left stick + A/B are translated into + // Android KeyEvents (dispatchKeyEvent) instead of being fed to Wine, since QuickMenu is + // regular Compose UI navigated via focusable()/FocusRequester like any Android TV-style + // screen — it never sees anything from XrGamepadBridge (that only writes to Wine's shared + // memory gamepad buffer), which is why the controller couldn't drive it at all before. + private var lastMenuDpadKeyCode: Int? = null + private var lastMenuDpadHeldSince = 0L + private var lastMenuDpadEventTime = 0L + private var lastMenuButtonAPressed = false + private var lastMenuButtonBPressed = false + private var lastMenuButtonLBPressed = false + private var lastMenuButtonRBPressed = false + + // XR pointer mode: toggled by double-clicking either thumbstick (see native pointerModeToggled). + // Direct manipulation of the quad's transform via the controllers, like grabbing a flat + // panel window's corner/edge on Horizon OS — grab a corner to resize, grab the top/bottom + // edge to reposition. Independent of quickMenuVisible/isOverlayPaused: usable any time, + // same as a system window. + // mutableStateOf rather than a plain @Volatile var so the mode-change indicator (Compose) + // can react to it — writes from the polling thread are safe, Compose's snapshot system + // handles cross-thread visibility on its own. Reading it from plain (non-@Composable) logic + // code elsewhere in this class works exactly the same as a normal property. + private var xrPointerModeActive by mutableStateOf(false) + // Toggled by a dedicated quick-menu button (Immersive tab), NOT the pointer-mode chord — an + // earlier version made resize/move handles always available (given a margin band to live in) + // in pointer mode, which cost a real, measured chunk of the direct-render path's fixed + // swapchain pixel budget (~45% of the pixel area at default scale) just to have somewhere for + // handles to sit outside the game. Handles now live INSIDE the content instead (see + // drawPointerCursors/nearGrabZone) and only exist at all — visible or grabbable — while this + // is on, so the game gets the full pixel budget the rest of the time. Same mutableStateOf + // cross-thread pattern as xrPointerModeActive above. + private var resizeHandlesEnabled by mutableStateOf(false) + // Either the grip or the index trigger — see handlePointerMode's kdoc for why these were + // unified into one "action" signal per hand instead of assigning grab and click to specific + // buttons. @Volatile since the quick menu's resize-handles toggle (UI thread) now also writes + // these directly, alongside the polling thread — previously only the polling thread ever + // touched them, so plain vars were safe; a raw cross-thread write without this is the exact + // class of visibility bug already found and fixed once this session for xrFrameBridge fields. + @Volatile private var lastLeftActionPressed = false + @Volatile private var lastRightActionPressed = false + @Volatile private var pointerGrabHand: Int? = null // 0 = left, 1 = right + private var pointerGrabIsResize = false + private val pointerGrabStartHandPos = FloatArray(3) + private var pointerGrabStartDistance = 0f + private var pointerGrabStartHorizontal = 0f + private var pointerGrabStartVertical = 0f + private var pointerGrabStartScale = 0f + + // Where each hand's ray currently hits the quad plane, in quad-local meters — read by + // compositeAndSubmitFrame (a different thread) to draw a visible cursor dot, since without + // ANY feedback the user has no way to tell where they're pointing (there's no true 3D laser + // — that needs native GL line rendering, a bigger lift; a 2D dot drawn directly onto the + // composited frame is a much cheaper approximation that still answers "where am I aiming"). + @Volatile private var pointerCursorLeftValid = false + @Volatile private var pointerCursorLeftX = 0f + @Volatile private var pointerCursorLeftY = 0f + @Volatile private var pointerCursorRightValid = false + @Volatile private var pointerCursorRightX = 0f + @Volatile private var pointerCursorRightY = 0f + + private var pointerTouchDownTimeLeft = 0L + private var pointerTouchDownTimeRight = 0L + + // Set by XServerScreen's onQuickMenuVisibilityChanged. SurfaceView content is composited on + // its own layer by the system — PixelCopy(Window) straight up cannot see it (shows a blank/ + // gray hole instead), so the game must be captured via PixelCopy(SurfaceView) directly. The + // quick menu (and other overlay UI) is regular Compose content in the *other* layer, which + // PixelCopy(SurfaceView) can't see either — so this flag switches capture source instead of + // trying to composite both, since the game is paused behind the menu anyway. + @Volatile + private var quickMenuVisible = false + + // Both only ever read/written from the polling thread (startControllerPollingLoop) — see its + // own kdoc for why these exist: suppressing a button that's still held right as menu/pause + // handling hands control back to the game, so the same physical press that dismissed our + // menu doesn't also reach the game as a fresh press. + private var wasInMenuNavigationMode = false + private var buttonSuppressMaskUntilRelease = 0 + + // "Immersif" quick-menu tab state — loaded from the container's extras once available, + // applied to the native quad transform on every change, and persisted back. + private var quadDistance by mutableFloatStateOf(ImmersiveControls.DEFAULT_DISTANCE) + private var quadHorizontal by mutableFloatStateOf(ImmersiveControls.DEFAULT_OFFSET) + private var quadVertical by mutableFloatStateOf(ImmersiveControls.DEFAULT_OFFSET) + private var quadScale by mutableFloatStateOf(ImmersiveControls.DEFAULT_SCALE) + private var passthroughEnabled by mutableStateOf(false) + private var showControlsOnboarding by mutableStateOf(false) + private var cachedContainer: Container? = null + // Refreshed whenever the quick menu opens (see onQuickMenuVisibilityChanged) and right after + // the reset button runs — null when the renderer isn't VulkanRenderer at all (the "Immersif" + // tab's reset button/description only shows when this is non-null). + private var directRenderBlockedByEffects by mutableStateOf(null) + + override fun attachBaseContext(newBase: Context) { + // Horizon OS hands this Activity a volumetric panel surface at ~4128x2208 physical px but + // only reports density=1.25 (adb: `wm density` -> 200), i.e. a ~3300x1770dp logical + // canvas. QuickMenu/XServerScreen's HUD (a plain View added via addView, not Compose — + // LocalDensity overrides in setContent wouldn't reach it) were both built assuming a + // normal phone/tablet-sized dp canvas, so at that density every fixed-dp element renders + // as a tiny sliver. Overriding density on the base Context fixes it for Compose *and* + // legacy Views uniformly, since everything in this Activity inherits from it. + val config = android.content.res.Configuration(newBase.resources.configuration) + config.densityDpi = (IMMERSIVE_UI_DENSITY * android.util.DisplayMetrics.DENSITY_DEFAULT).toInt() + super.attachBaseContext(newBase.createConfigurationContext(config)) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + Timber.i( + "Immersive: density override applied -> %.3f (logical size %dx%d dp)", + resources.displayMetrics.density, + (resources.displayMetrics.widthPixels / resources.displayMetrics.density).toInt(), + (resources.displayMetrics.heightPixels / resources.displayMetrics.density).toInt(), + ) + + val appId = intent.getStringExtra(EXTRA_APP_ID) + val isOffline = intent.getBooleanExtra(EXTRA_IS_OFFLINE, false) + if (appId == null) { + finish() + return + } + currentAppId = appId + + AppUtils.keepScreenOn(this) + loadImmersiveSettings(appId) + + setContent { + PluviaTheme { + val context = LocalContext.current + + androidx.activity.compose.BackHandler(enabled = backAction != null) { + backAction?.invoke() + } + + Box(modifier = Modifier.fillMaxSize()) { + XServerScreen( + appId = appId, + bootToContainer = false, + isOffline = isOffline, + registerBackAction = { cb -> backAction = cb }, + registerQuickMenuToggle = { toggle -> quickMenuToggle = toggle }, + registerQuickMenuStartHeld = { setter -> quickMenuSetStartHeld = setter }, + registerQuickMenuFocusManager = { fm -> quickMenuFocusManager = fm }, + registerQuickMenuCycleTab = { cycle -> quickMenuCycleTab = cycle }, + registerQuickMenuAdjustmentControl = { control -> quickMenuAdjustmentControl = control }, + registerQuickMenuFocusTabRail = { action -> quickMenuFocusTabRail = action }, + registerQuickMenuFocusedActivate = { getter -> quickMenuFocusedActivate = getter }, + navigateBack = { finish() }, + onExit = { onComplete -> + viewModel.exitSteamApp(context, appId) { + onComplete?.invoke() + finish() + } + }, + onWindowMapped = { ctx, window -> + viewModel.onWindowMapped(ctx, window, appId) + showControlsOnboarding = true + }, + onGameLaunchError = { error -> + viewModel.onGameLaunchError(error) + finish() + }, + onQuickMenuVisibilityChanged = { visible -> + Timber.i("Immersive: quick menu visibility changed to %b", visible) + quickMenuVisible = visible + if (visible) { + directRenderBlockedByEffects = (PluviaApp.xServerView?.renderer as? com.winlator.renderer.VulkanRenderer) + ?.isEffectsRequireCompositor() + } + }, + immersiveControls = ImmersiveControls( + passthroughEnabled = passthroughEnabled, + onPassthroughToggle = { enabled -> + passthroughEnabled = enabled + if (xrSessionHandle != 0L) { + XrNative.nativeSetPassthroughEnabled(xrSessionHandle, enabled) + } + persistImmersiveSettings(appId) + }, + directRenderBlockedByEffects = directRenderBlockedByEffects, + onResetScreenEffects = { + val vulkanRenderer = PluviaApp.xServerView?.renderer as? com.winlator.renderer.VulkanRenderer + vulkanRenderer?.resetScreenEffects() + directRenderBlockedByEffects = vulkanRenderer?.isEffectsRequireCompositor() + Timber.i("Immersive: screen effects reset from quick menu, blocked=%s", directRenderBlockedByEffects) + }, + resizeModeEnabled = resizeHandlesEnabled, + onResizeModeToggle = { enabled -> + resizeHandlesEnabled = enabled + // Handles are only usable via the XR laser pointer (grabbing a + // corner/edge), not Xbox mode's stick+dpad navigation — switch modes + // together with the toggle instead of leaving the user to find the + // pointer-mode chord themselves, and always land back on Xbox mode + // when turning handles off (not whichever mode was active before). + xrPointerModeActive = enabled + lastLeftActionPressed = false + lastRightActionPressed = false + if (!enabled) { + pointerGrabHand = null + pointerCursorLeftValid = false + pointerCursorRightValid = false + } + Timber.i("Immersive: resize handles toggled %s from quick menu, pointer mode now %s", enabled, enabled) + }, + ), + ) + + ImmersiveControlsOnboarding( + visible = showControlsOnboarding, + onDismiss = { showControlsOnboarding = false }, + ) + ImmersiveModeChangeIndicator(pointerModeActive = xrPointerModeActive) + } + } + } + } + + private fun loadImmersiveSettings(appId: String) { + lifecycleScope.launch(Dispatchers.IO) { + val container = try { + ContainerUtils.getContainer(this@ImmersiveXrActivity, appId) + } catch (t: Throwable) { + Timber.w(t, "Could not load container for immersive settings, using defaults") + return@launch + } + cachedContainer = container + quadDistance = container.getExtra(EXTRA_QUAD_DISTANCE, ImmersiveControls.DEFAULT_DISTANCE.toString()) + .toFloatOrNull() ?: ImmersiveControls.DEFAULT_DISTANCE + // Always start facing the user, regardless of whatever orbit position was persisted + // from the previous session — only distance/scale carry over. + quadHorizontal = ImmersiveControls.DEFAULT_OFFSET + quadVertical = ImmersiveControls.DEFAULT_OFFSET + quadScale = container.getExtra(EXTRA_QUAD_SCALE, ImmersiveControls.DEFAULT_SCALE.toString()) + .toFloatOrNull() ?: ImmersiveControls.DEFAULT_SCALE + passthroughEnabled = container.getExtra(EXTRA_PASSTHROUGH_ENABLED, "false").toBoolean() + applyQuadTransform() + if (xrSessionHandle != 0L) { + XrNative.nativeSetPassthroughEnabled(xrSessionHandle, passthroughEnabled) + } + } + } + + private fun persistImmersiveSettings(appId: String) { + lifecycleScope.launch(Dispatchers.IO) { + val container = cachedContainer ?: try { + ContainerUtils.getContainer(this@ImmersiveXrActivity, appId).also { cachedContainer = it } + } catch (t: Throwable) { + Timber.w(t, "Could not persist immersive settings") + return@launch + } + container.putExtra(EXTRA_QUAD_DISTANCE, quadDistance.toString()) + container.putExtra(EXTRA_QUAD_SCALE, quadScale.toString()) + container.putExtra(EXTRA_PASSTHROUGH_ENABLED, passthroughEnabled.toString()) + container.saveData() + } + } + + private fun applyQuadTransform() { + if (xrSessionHandle == 0L) return + // No margin — the quad is always exactly the game's own content size, identical in every + // controller mode. See compositeGameFrame's kdoc for why: an earlier version grew the + // quad by a fixed margin band to give resize/move handles a place to live outside the + // game, which cost a real, measured chunk of the direct-render path's fixed swapchain + // pixel budget. contentScaleX/Y are always 1.0 now (no remap needed) — kept as real JNI + // parameters rather than removed so this stays a plain revert-to-1.0, not a signature + // change, should a margin ever come back. + XrNative.nativeSetQuadTransform( + xrSessionHandle, + quadHorizontal, + quadVertical, + -quadDistance, + ImmersiveControls.BASE_WIDTH_METERS * quadScale, + ImmersiveControls.BASE_HEIGHT_METERS * quadScale, + 1f, + 1f, + ) + } + + override fun onResume() { + super.onResume() + // MainActivity is the only thing that normally toggles this flag (see its own + // onResume/onPause). Since this is a separate Activity, MainActivity.onPause() fires + // when we're launched (setting it false) and nothing ever set it back — XServerScreen's + // post-setup check (`if (!PluviaApp.isActivityInForeground && !neverSuspend)`) then + // immediately re-paused the freshly-launched game, which is why immersive launches + // showed a black screen: the game process was running but paused right away. + PluviaApp.isActivityInForeground = true + if (SteamService.keepAlive && PluviaApp.hasValidSuspendPolicyState() && PluviaApp.xEnvironment != null) { + when { + PluviaApp.isNeverSuspendMode() -> Unit + PluviaApp.isOverlayPaused && PluviaApp.isManualSuspendMode() -> Unit + else -> PluviaApp.xEnvironment?.onResume() + } + } + startXrSessionIfNeeded() + } + + override fun onPause() { + PluviaApp.isActivityInForeground = false + Timber.i( + "Immersive: onPause, isFinishing=%b isChangingConfigurations=%b", + isFinishing, + isChangingConfigurations, + ) + if (isFinishing && !isChangingConfigurations) { + // Android guarantees the outgoing activity's onPause() completes before the + // incoming one's onResume() runs, but does NOT guarantee onDestroy() runs that + // early — it can (and did, per testing) land after MainActivity.onResume() has + // already read PluviaApp.xEnvironment/SteamService.keepAlive and decided to show + // "Launching..." again for a game that no longer has a window. Tearing down here + // instead — while we know for sure this pause means we're really exiting, not just + // briefly backgrounding for a system dialog — closes that race. + PluviaApp.shutdownEnvironment() + } else if (SteamService.keepAlive && PluviaApp.hasValidSuspendPolicyState() && + PluviaApp.xEnvironment != null && !PluviaApp.isNeverSuspendMode() + ) { + PluviaApp.xEnvironment?.onPause() + } + super.onPause() + } + + override fun onDestroy() { + stopXrSession() + // Safety net for onPause() not having caught it (e.g. the process/Activity was torn + // down some other way): every step here no-ops if shutdownEnvironment() already ran. + PluviaApp.shutdownEnvironment() + super.onDestroy() + } + + private fun startXrSessionIfNeeded() { + if (xrSessionHandle != 0L) return + xrSessionHandle = try { + XrNative.nativeCreate(this) + } catch (t: Throwable) { + // libxrimmersive.so hasn't been built/bundled yet (see XrNative's kdoc) — degrade to + // a plain direct launch rather than crashing the whole immersive entry point. + Timber.w(t, "Native OpenXR module unavailable — immersive rendering/controller mapping disabled") + return + } + + // Apply whatever settings are already loaded (may still be the defaults if + // loadImmersiveSettings()'s IO hasn't finished yet — it re-applies once it has). + applyQuadTransform() + XrNative.nativeSetPassthroughEnabled(xrSessionHandle, passthroughEnabled) + + startControllerPollingLoop() + startFrameCaptureLoop() + } + + private fun startControllerPollingLoop() { + pollingActive.set(true) + pollingThread = thread(name = "XrGamepadPoll") { + val buttons = IntArray(1) + val axes = FloatArray(6) + val handPoses = FloatArray(12) + val flags = BooleanArray(3) + while (pollingActive.get()) { + val winHandler = PluviaApp.xServerView?.getxServer()?.winHandler + if (winHandler != null) { + if (winHandler !== cachedWinHandler) { + cachedWinHandler = winHandler + cachedBridge = XrGamepadBridge(winHandler) + } + val quickMenuClicked = XrNative.nativePollSnapshot(xrSessionHandle, buttons, axes, handPoses, flags) + if (flags[2] != lastPushedStartHeld) { + lastPushedStartHeld = flags[2] + val setter = quickMenuSetStartHeld + if (setter != null) runOnUiThread { setter(lastPushedStartHeld) } + } + if (flags[1]) { + xrPointerModeActive = !xrPointerModeActive + Timber.i("Immersive: XR pointer mode %s", if (xrPointerModeActive) "enabled" else "disabled") + // The activation chord itself holds down the buttons driving BOTH grab + // and click — seeding these from this SAME poll's snapshot (still held + // from the chord) rather than forcing false avoids a spurious same-poll + // rising edge: an earlier version forced both to false here, which made + // handlePointerMode see a brand-new press on this exact poll and + // immediately act on whatever the hand happened to be aiming at mid-chord + // — logs confirmed this both fired phantom grab attempts right after every + // mode-enable, AND (for the click/trigger case) could dispatch a synthetic + // touch that landed on the quick menu's dismiss backdrop, closing it. The + // user now needs one clean release+press to start a grab/click, same as + // any other button — slightly less "instant" than the original intent, + // but no more phantom presses eating the user's first real one. + lastLeftActionPressed = (axes[4] > POINTER_GRAB_PRESS_THRESHOLD) || + ((buttons[0] and (1 shl XrGamepadBridge.BUTTON_LB)) != 0) + lastRightActionPressed = (axes[5] > POINTER_GRAB_PRESS_THRESHOLD) || + ((buttons[0] and (1 shl XrGamepadBridge.BUTTON_RB)) != 0) + if (!xrPointerModeActive) { + pointerGrabHand = null + pointerCursorLeftValid = false + pointerCursorRightValid = false + } + } + val inMenuMode = quickMenuVisible || PluviaApp.isOverlayPaused + if (wasInMenuNavigationMode && !inMenuMode) { + // Just exited quick-menu/pause handling — e.g. the same A press that just + // dismissed our own "paused" screen and resumed the game is very likely + // still physically held on THIS exact poll. Forwarding it immediately as + // a fresh gamepad press let the GAME's own in-game pause menu (a separate + // thing from our shell's pause state) read that same held button as a + // second, unwanted "confirm" — e.g. pressing A to unpause also activating + // whatever the game's own menu had focused (its Resume item). Snapshot + // whatever's held right now and mask it out of forwarding until released. + buttonSuppressMaskUntilRelease = buttons[0] + } + wasInMenuNavigationMode = inMenuMode + when { + xrPointerModeActive -> handlePointerMode(buttons[0], axes, handPoses, flags[0]) + // While the Menu/Start button is physically held (flags[2]), skip menu + // navigation entirely — same hand that's holding it may still rest on/near + // the thumbstick, and residual leftX/leftY was being read as a genuine dpad + // direction the instant the menu opened (or closed), landing on whatever + // was focused — e.g. dragging an already-locked slider. Start should only + // open/close the menu, nothing else; this makes that true regardless of + // stray input during the hold, and applies symmetrically to both the + // opening AND the closing hold. + inMenuMode && !flags[2] -> handleMenuNavigation(buttons[0], axes) + inMenuMode -> Unit + else -> { + buttonSuppressMaskUntilRelease = buttonSuppressMaskUntilRelease and buttons[0] + cachedBridge?.applySnapshot(buttons[0] and buttonSuppressMaskUntilRelease.inv(), axes) + } + } + if (quickMenuClicked) { + Timber.i( + "Immersive: quick-menu chord fired, quickMenuToggle registered=%b buttons=0x%03x " + + "quickMenuVisible=%b isOverlayPaused=%b xrPointerModeActive=%b", + quickMenuToggle != null, buttons[0], quickMenuVisible, PluviaApp.isOverlayPaused, xrPointerModeActive, + ) + runOnUiThread { quickMenuToggle?.invoke() } + } + } + Thread.sleep(POLL_INTERVAL_MS) + } + } + } + + /** + * Direct manipulation of the quad transform via a hand's aim-pose ray, active only while + * [xrPointerModeActive]. Either the grip OR the index trigger works for BOTH grabbing a + * corner/edge handle and clicking a UI element — whichever the user presses is treated the + * same way. This used to require a SPECIFIC button for each action, and repeated, + * contradictory feedback about which physical button is "top" vs "bottom" on the controller + * never converged on a mapping that felt right — unifying them removes the ambiguity + * entirely instead of guessing a mapping again. Grab a corner (within + * [POINTER_CORNER_RADIUS_METERS]) to resize; grab the top/bottom edge (within + * [POINTER_BAR_RADIUS_METERS]) to reposition. Deltas are computed relative to the hand's + * position when the grab started, not incrementally, so there's no drift from small + * per-frame rounding. + */ + private fun handlePointerMode(buttons: Int, axes: FloatArray, handPoses: FloatArray, posesValid: Boolean) { + val leftGripPressed = (buttons and (1 shl XrGamepadBridge.BUTTON_LB)) != 0 + val rightGripPressed = (buttons and (1 shl XrGamepadBridge.BUTTON_RB)) != 0 + // Trigger uses hysteresis (two thresholds, see their own kdoc) since it's an analog value + // with no clean digital edge; the grip is already a plain digital button. + val leftTriggerPressed = axes[4] > (if (lastLeftActionPressed) POINTER_GRAB_RELEASE_THRESHOLD else POINTER_GRAB_PRESS_THRESHOLD) + val rightTriggerPressed = axes[5] > (if (lastRightActionPressed) POINTER_GRAB_RELEASE_THRESHOLD else POINTER_GRAB_PRESS_THRESHOLD) + val leftActionPressed = leftGripPressed || leftTriggerPressed + val rightActionPressed = rightGripPressed || rightTriggerPressed + val leftReleasedNow = !leftActionPressed && lastLeftActionPressed + val rightReleasedNow = !rightActionPressed && lastRightActionPressed + val leftPressedNow = leftActionPressed && !lastLeftActionPressed + val rightPressedNow = rightActionPressed && !lastRightActionPressed + if ((leftActionPressed && !leftPressedNow && pointerGrabHand != 0) || + (rightActionPressed && !rightPressedNow && pointerGrabHand != 1) + ) { + // The action button is physically down but we didn't see a rising edge for it — means + // it was already held (e.g. still down from the activation chord) when this hand + // started being read as pressed. Logged (rate-limited) so this is distinguishable + // from "we tried and missed the zone". + pointerGripHeldLogCounter++ + if (pointerGripHeldLogCounter % 45 == 0) { + Timber.i( + "Immersive: action button held without a fresh edge — left=%b(pressedNow=%b) right=%b(pressedNow=%b) grabbing=%s", + leftActionPressed, leftPressedNow, rightActionPressed, rightPressedNow, pointerGrabHand, + ) + } + } + lastLeftActionPressed = leftActionPressed + lastRightActionPressed = rightActionPressed + + if (!posesValid) { + pointerCursorLeftValid = false + pointerCursorRightValid = false + return + } + + val leftHit = rayPlaneHit(handPoses, 0, quadHorizontal, quadVertical, quadDistance) + pointerCursorLeftValid = leftHit != null + if (leftHit != null) { + pointerCursorLeftX = leftHit[0] + pointerCursorLeftY = leftHit[1] + } + val rightHit = rayPlaneHit(handPoses, 1, quadHorizontal, quadVertical, quadDistance) + pointerCursorRightValid = rightHit != null + if (rightHit != null) { + pointerCursorRightX = rightHit[0] + pointerCursorRightY = rightHit[1] + } + + handHandAction(0, leftPressedNow, leftReleasedNow, leftActionPressed, leftHit, handPoses) + handHandAction(1, rightPressedNow, rightReleasedNow, rightActionPressed, rightHit, handPoses) + } + + /** One hand's share of handlePointerMode — a fresh press either starts a grab (if aimed at a + * handle) OR dispatches a click, never both for the same press (tryStartPointerGrab's return + * value decides which, so a grab-starting press can't also fire a spurious click underneath + * it — merging grab and click onto the same button made that a real risk that separate + * buttons never had). */ + private fun handHandAction( + hand: Int, + pressedNow: Boolean, + releasedNow: Boolean, + held: Boolean, + hit: FloatArray?, + handPoses: FloatArray, + ) { + if (pointerGrabHand == hand) { + if (releasedNow) { + pointerGrabHand = null + currentAppId?.let { persistImmersiveSettings(it) } + } else { + updateActiveGrab(handPoses) + } + return + } + when { + pressedNow -> { + if (!tryStartPointerGrab(hand, handPoses)) { + dispatchPointerTouch(hand, android.view.MotionEvent.ACTION_DOWN, hit) + } + } + releasedNow -> dispatchPointerTouch(hand, android.view.MotionEvent.ACTION_UP, hit) + held -> dispatchPointerTouch(hand, android.view.MotionEvent.ACTION_MOVE, hit) + } + } + + /** Converts a hand's quad-local hit point (meters) into content-view pixel coordinates and + * dispatches a synthetic touch event there — lets the XR pointer act as a real touch/mouse + * input for whatever Compose UI is on screen (menu buttons, adjustment +/-, the Resume + * button...), the same way tapping a Quest system panel with a controller ray does. */ + private fun dispatchPointerTouch(hand: Int, action: Int, hit: FloatArray?) { + if (hit == null) return + val halfWidth = (ImmersiveControls.BASE_WIDTH_METERS * quadScale) / 2f + val halfHeight = (ImmersiveControls.BASE_HEIGHT_METERS * quadScale) / 2f + if (halfWidth <= 0f || halfHeight <= 0f) return + // hit is already quad-LOCAL meters (rayPlaneHit projects onto the quad's own basis) — no + // further conversion needed. + val localX = hit[0] + val localY = hit[1] + + val now = android.os.SystemClock.uptimeMillis() + if (action == android.view.MotionEvent.ACTION_DOWN) { + if (hand == 0) pointerTouchDownTimeLeft = now else pointerTouchDownTimeRight = now + } + val downTime = (if (hand == 0) pointerTouchDownTimeLeft else pointerTouchDownTimeRight) + .takeIf { it > 0L } ?: now + + runOnUiThread { + val contentView = findViewById(android.R.id.content) + val width = contentView.width + val height = contentView.height + if (width <= 0 || height <= 0) return@runOnUiThread + + val px = ((localX + halfWidth) / (2f * halfWidth)) * width + val py = ((halfHeight - localY) / (2f * halfHeight)) * height + if (px < 0f || px > width || py < 0f || py > height) return@runOnUiThread + + val event = android.view.MotionEvent.obtain(downTime, now, action, px, py, 0) + // obtain() leaves source as SOURCE_UNKNOWN — Compose's pointer input system + // classifies events by source (touch/mouse/stylus) and silently ignores ones it + // can't classify, which is almost certainly why this dispatch had no visible effect. + event.source = android.view.InputDevice.SOURCE_TOUCHSCREEN + try { + dispatchTouchEvent(event) + } finally { + event.recycle() + } + } + } + + private val pointerCursorPaint = android.graphics.Paint().apply { + isAntiAlias = true + style = android.graphics.Paint.Style.FILL + } + + /** Draws a simple 2D dot at each hand's ray/quad hit point — see pointerCursor* fields. */ + private fun toPixel(localX: Float, localY: Float, halfWidth: Float, halfHeight: Float, width: Int, height: Int): FloatArray { + val px = ((localX + halfWidth) / (2f * halfWidth)) * width + val py = ((halfHeight - localY) / (2f * halfHeight)) * height + return floatArrayOf(px, py) + } + + /** Draws the per-hand aiming cursor always (while in pointer mode), and the 6 grab handles + * (4 corners + top/bottom edge) only while [resizeHandlesEnabled] is on — that toggle, and + * drawing handles INSIDE the content instead of in an outside margin band, replaced an + * earlier design that grew the quad by a fixed margin on every side purely to give handles + * somewhere to live outside the game: that cost a real, measured chunk of the direct-render + * path's fixed swapchain pixel budget (~45% of the pixel area at default scale), which + * showed up as pixelation the user explicitly rejected as a tradeoff. [width]/[height] are + * now simply the content's own canvas — no separate "outer" canvas exists anymore. */ + private fun drawPointerCursors(canvas: android.graphics.Canvas, width: Int, height: Int) { + val contentHalfWidth = (ImmersiveControls.BASE_WIDTH_METERS * quadScale) / 2f + val contentHalfHeight = (ImmersiveControls.BASE_HEIGHT_METERS * quadScale) / 2f + if (contentHalfWidth <= 0f || contentHalfHeight <= 0f) return + + fun toContentPixel(localX: Float, localY: Float) = toPixel(localX, localY, contentHalfWidth, contentHalfHeight, width, height) + + // Cursor dot per hand — still a 2D approximation baked into this same texture, not a true + // 3D laser (that needs native geometry rendering — a bigger follow-up, see Nightfall's + // own capsule-mesh laser for the target design). Drawn regardless of resizeHandlesEnabled + // — this is the aiming feedback for clicking the quick menu, independent of whether + // resize/move handles are also on right now. + fun drawCursor(valid: Boolean, localX: Float, localY: Float, isGrabbing: Boolean) { + if (!valid) return + val (rawPx, rawPy) = toContentPixel(localX, localY) + val px = rawPx.coerceIn(0f, width.toFloat()) + val py = rawPy.coerceIn(0f, height.toFloat()) + val (nearCorner, nearEdgeBar) = nearGrabZone(localX, localY, contentHalfWidth, contentHalfHeight) + pointerCursorPaint.style = if (nearCorner || nearEdgeBar || isGrabbing) { + android.graphics.Paint.Style.FILL + } else { + android.graphics.Paint.Style.STROKE + } + pointerCursorPaint.strokeWidth = 4f + pointerCursorPaint.color = android.graphics.Color.WHITE + canvas.drawCircle(px, py, if (isGrabbing) 16f * quadScale else 11f * quadScale, pointerCursorPaint) + } + drawCursor(pointerCursorLeftValid, pointerCursorLeftX, pointerCursorLeftY, pointerGrabHand == 0) + drawCursor(pointerCursorRightValid, pointerCursorRightX, pointerCursorRightY, pointerGrabHand == 1) + + if (!resizeHandlesEnabled) return + + val handleRadiusPx = POINTER_HANDLE_RADIUS_METERS * width / (2f * contentHalfWidth) + + // Each valid hand's current quad-local hit point, paired with which hand it is (needed to + // check pointerGrabHand for the "grabbed" tier specifically, not just "hovering"). + val hands = buildList { + if (pointerCursorLeftValid) add(Triple(pointerCursorLeftX, pointerCursorLeftY, 0)) + if (pointerCursorRightValid) add(Triple(pointerCursorRightX, pointerCursorRightY, 1)) + } + + // (alpha, strokeWidth) together — a hand aiming AT a handle needs to be unmistakable, both + // more opaque AND visibly thicker, not just brighter (a thin line staying thin was easy to + // lose track of exactly when it mattered most: right as you're lining up a grab). + fun zoneStyle(signX: Int, signY: Int, isCorner: Boolean): Pair { + var hovering = false + var grabbing = false + for ((hLocalX, hLocalY, hand) in hands) { + val (nearCorner, nearEdgeBar) = nearGrabZone(hLocalX, hLocalY, contentHalfWidth, contentHalfHeight) + val matchesThisZone = if (isCorner) { + nearCorner && kotlin.math.sign(hLocalX).toInt() == signX && kotlin.math.sign(hLocalY).toInt() == signY + } else { + nearEdgeBar && kotlin.math.sign(hLocalY).toInt() == signY + } + if (matchesThisZone) { + hovering = true + if (pointerGrabHand == hand) grabbing = true + } + } + return when { + grabbing -> 180 to POINTER_HANDLE_STROKE_WIDTH_ACTIVE_PX // ~0.7 opacity, 3x the idle stroke + hovering -> 90 to POINTER_HANDLE_STROKE_WIDTH_ACTIVE_PX // ~0.35 opacity, 3x the idle stroke + else -> 20 to POINTER_HANDLE_STROKE_WIDTH_IDLE_PX // ~0.08 opacity, present but barely there + } + } + + // Thin corner brackets (an "L" hugging each corner, arms pointing inward) and a thin + // pill for each edge handle — NOT filled circles. A filled disc covers far more pixels + // than a thin line at the same opacity, which is what made the circle version look like + // a solid "ball" even at low alpha; a thin stroke stays genuinely subtle at the same tier. + // Anchored INSIDE the content edge by POINTER_INDICATOR_GAP_METERS (the same gap that + // used to sit between the content edge and a handle drawn OUTSIDE it, in the margin band + // that no longer exists) instead of outside it. + val handlePaint = android.graphics.Paint().apply { + isAntiAlias = true + style = android.graphics.Paint.Style.STROKE + strokeWidth = POINTER_HANDLE_STROKE_WIDTH_IDLE_PX + strokeCap = android.graphics.Paint.Cap.ROUND + color = android.graphics.Color.WHITE + } + val armPx = handleRadiusPx * 1.6f + for (signX in intArrayOf(-1, 1)) { + for (signY in intArrayOf(-1, 1)) { + val corner = toContentPixel( + signX * (contentHalfWidth - POINTER_INDICATOR_GAP_METERS), + signY * (contentHalfHeight - POINTER_INDICATOR_GAP_METERS), + ) + val (alpha, strokeWidth) = zoneStyle(signX, signY, isCorner = true) + handlePaint.alpha = alpha + handlePaint.strokeWidth = strokeWidth + // toPixel's Y axis is flipped relative to world-space localY (py DECREASES as + // localY increases), so "inward" (toward center) is corner.y + signY*armPx, not + // corner.y - signY*armPx. + canvas.drawLine(corner[0], corner[1], corner[0] - signX * armPx, corner[1], handlePaint) + canvas.drawLine(corner[0], corner[1], corner[0], corner[1] + signY * armPx, handlePaint) + } + } + for (signY in intArrayOf(-1, 1)) { + val center = toContentPixel(0f, signY * (contentHalfHeight - POINTER_INDICATOR_GAP_METERS)) + val (alpha, strokeWidth) = zoneStyle(0, signY, isCorner = false) + handlePaint.alpha = alpha + handlePaint.strokeWidth = strokeWidth + canvas.drawLine(center[0] - armPx, center[1], center[0] + armPx, center[1], handlePaint) + } + } + + /** Ray/quad-plane intersection, returning quad-LOCAL coordinates (meters, origin at the + * quad's own center, +X along the quad's own right edge, +Y up) directly — not world + * coordinates. The quad orbits the player rather than sliding on a flat, world-axis-aligned + * plane once [horizontal] != 0 (see xr_immersive.cpp's submitQuadLayer for why and the exact + * derivation this mirrors), so "local" is no longer a plain world-coordinate subtraction: this + * recomputes the quad's actual position + rotated right/normal axes from the same + * (horizontal, vertical, distance) triple passed in, then projects the hit point onto them. + * Callers pass either the LIVE quad transform (for hover/click) or a snapshot frozen at + * grab-start (for a drag's fixed reference plane — see updateActiveGrab). */ + private fun rayPlaneHit(handPoses: FloatArray, hand: Int, horizontal: Float, vertical: Float, distance: Float): FloatArray? { + val base = hand * 6 + val originX = handPoses[base] + val originY = handPoses[base + 1] + val originZ = handPoses[base + 2] + val dirX = handPoses[base + 3] + val dirY = handPoses[base + 4] + val dirZ = handPoses[base + 5] + + // Same yaw/pitch/position/basis derivation as xr_immersive.cpp's submitQuadLayer — keep + // the two in sync, since this must describe the SAME quad the player actually sees. The + // quad orbits the player on both axes (see that function's kdoc for the full derivation), + // so "local" here means projected onto the quad's own right/up axes at its orbited + // position, not a plain world-coordinate subtraction. + val yaw = if (distance > 0.0001f) kotlin.math.atan2(horizontal, distance) else 0f + val pitch = if (distance > 0.0001f) kotlin.math.atan2(vertical, distance) else 0f + val sinYaw = kotlin.math.sin(yaw) + val cosYaw = kotlin.math.cos(yaw) + val sinPitch = kotlin.math.sin(pitch) + val cosPitch = kotlin.math.cos(pitch) + + val centerX = distance * sinYaw * cosPitch + val centerY = distance * sinPitch + val centerZ = -distance * cosYaw * cosPitch + + // Right axis is unaffected by pitch (pitch rotates about the quad's own local X, i.e. + // the right axis itself) — only yaw moves it. + val rightX = cosYaw + val rightY = 0f + val rightZ = sinYaw + // Up and normal both pick up the pitch tilt. + val upX = -sinYaw * sinPitch + val upY = cosPitch + val upZ = cosYaw * sinPitch + val normalX = -sinYaw * cosPitch + val normalY = -sinPitch + val normalZ = cosYaw * cosPitch + + val denom = dirX * normalX + dirY * normalY + dirZ * normalZ + if (kotlin.math.abs(denom) < 0.0001f) return null + val t = ((centerX - originX) * normalX + (centerY - originY) * normalY + (centerZ - originZ) * normalZ) / denom + if (t <= 0f) return null + + val hitX = originX + t * dirX + val hitY = originY + t * dirY + val hitZ = originZ + t * dirZ + val relX = hitX - centerX + val relY = hitY - centerY + val relZ = hitZ - centerZ + val localX = relX * rightX + relY * rightY + relZ * rightZ + val localY = relX * upX + relY * upY + relZ * upZ + return floatArrayOf(localX, localY) + } + + /** Whether a quad-local point is near a corner (resize handle) or the top/bottom edge (move + * handle) — shared by the actual grab check and the hover-highlight drawn for the cursor, so + * the user can see which zone they're in before pulling the grip, not just guess. */ + private fun nearGrabZone(localX: Float, localY: Float, halfWidth: Float, halfHeight: Float): Pair { + // Gated entirely on the quick-menu toggle now — see its kdoc for why (no margin band left + // to require "outside" of; handles/grab-zones only exist at all when explicitly asked + // for). Proximity to a corner/edge decides it from here, with the corner/bar "center" + // pinned to the actual corner/edge (±halfWidth/halfHeight, never 0) rather than + // kotlin.math.sign(x)*half (which collapses to 0 exactly at x=0, i.e. dead center) — the + // margin-era version never hit that edge case because it additionally required being + // outside the content, which excluded the center by construction; without that gate, a + // sign()-based center needs this instead to avoid a false match at the exact center. + if (!resizeHandlesEnabled) return false to false + val cornerCenterX = if (localX >= 0f) halfWidth else -halfWidth + val cornerCenterY = if (localY >= 0f) halfHeight else -halfHeight + val nearCorner = kotlin.math.hypot(localX - cornerCenterX, localY - cornerCenterY) < POINTER_CORNER_RADIUS_METERS + val barCenterY = if (localY >= 0f) halfHeight else -halfHeight + val nearEdgeBar = kotlin.math.abs(localX) < POINTER_BAR_HALF_WIDTH_METERS && + kotlin.math.abs(localY - barCenterY) < POINTER_BAR_RADIUS_METERS + return nearCorner to nearEdgeBar + } + + /** Returns true if a grab actually started — callers use this to skip a simultaneous click + * dispatch for the same press (see handlePointerMode's kdoc on the unified action button). */ + private fun tryStartPointerGrab(hand: Int, handPoses: FloatArray): Boolean { + val hit = rayPlaneHit(handPoses, hand, quadHorizontal, quadVertical, quadDistance) + if (hit == null) { + Timber.i("Immersive: action pressed hand=%d but ray never crossed the quad plane (behind the hand, or pointing away)", hand) + return false + } + // BASE_WIDTH/HEIGHT*scale IS the game content's own true half-extent in this "margin is + // ADDED, never shrinks the game" model. hit is already quad-local (rayPlaneHit projects + // onto the quad's own basis) — no further conversion needed. + val contentHalfWidth = (ImmersiveControls.BASE_WIDTH_METERS * quadScale) / 2f + val contentHalfHeight = (ImmersiveControls.BASE_HEIGHT_METERS * quadScale) / 2f + val localX = hit[0] + val localY = hit[1] + + val (nearCorner, nearEdgeBar) = nearGrabZone(localX, localY, contentHalfWidth, contentHalfHeight) + + if (!nearCorner && !nearEdgeBar) { + Timber.i( + "Immersive: action pressed hand=%d but not in a grab zone — local=(%.2f,%.2f) contentHalf=(%.2f,%.2f) cornerRadius=%.2f barHalfWidth=%.2f barRadius=%.2f", + hand, localX, localY, contentHalfWidth, contentHalfHeight, + POINTER_CORNER_RADIUS_METERS, POINTER_BAR_HALF_WIDTH_METERS, POINTER_BAR_RADIUS_METERS, + ) + return false + } + + pointerGrabHand = hand + pointerGrabIsResize = nearCorner + // Grab-start reference is the RAY'S HIT POINT on the quad plane, not the hand's raw 3D + // position — a small wrist rotation moves the hit point far more than it moves the hand + // itself (the further the quad, the bigger that amplification), so tracking the hit point + // is what makes move/resize actually feel like "the window follows the controller" instead + // of a sluggish 1:1 hand-translation mapping that barely responds to aiming/rotation at + // all. Z is the exception — there's no "hit point" analog for push/pull distance, so that + // axis still tracks the hand's raw forward/back position. + pointerGrabStartHandPos[0] = hit[0] + pointerGrabStartHandPos[1] = hit[1] + pointerGrabStartHandPos[2] = handPoses[hand * 6 + 2] + pointerGrabStartDistance = quadDistance + pointerGrabStartHorizontal = quadHorizontal + pointerGrabStartVertical = quadVertical + pointerGrabStartScale = quadScale + Timber.i("Immersive: pointer grab started hand=%d resize=%b", hand, nearCorner) + return true + } + + private fun updateActiveGrab(handPoses: FloatArray) { + val hand = pointerGrabHand ?: return + val base = hand * 6 + // Fixed reference plane frozen at grab-start (position AND orientation, via the frozen + // pointerGrabStart{Horizontal,Vertical,Distance} triple) — recomputing against a plane + // that itself moves/rotates mid-drag would fight the very delta it's trying to measure. + // rayPlaneHit returns coordinates already local to THIS fixed plane, matching + // pointerGrabStartHandPos (captured the same way in tryStartPointerGrab), so both are + // directly comparable without any extra offset math. + val currentHit = rayPlaneHit(handPoses, hand, pointerGrabStartHorizontal, pointerGrabStartVertical, pointerGrabStartDistance) + if (currentHit == null) return // ray no longer crosses the reference plane — hold, don't jump + val dx = currentHit[0] - pointerGrabStartHandPos[0] + val dy = currentHit[1] - pointerGrabStartHandPos[1] + val dz = handPoses[base + 2] - pointerGrabStartHandPos[2] + + if (pointerGrabIsResize) { + // Scale by how far the hit point's distance from the quad's center (fixed during a + // resize — only scale changes, not position) has changed since grab-start — a single + // uniform ratio applied to both width and height via quadScale, so the aspect ratio + // can never change no matter which direction the hand drags. Both hit points are + // already local (center-relative), so distance-from-center is just their own hypot. + val startDist = kotlin.math.hypot(pointerGrabStartHandPos[0], pointerGrabStartHandPos[1]) + val newDist = kotlin.math.hypot(currentHit[0], currentHit[1]) + if (startDist > 0.0001f) { + val newScale = (pointerGrabStartScale * (newDist / startDist)) + .coerceIn(ImmersiveControls.MIN_SCALE, ImmersiveControls.MAX_SCALE) + quadScale = newScale + } + } else { + quadHorizontal = (pointerGrabStartHorizontal + dx) + .coerceIn(ImmersiveControls.MIN_OFFSET, ImmersiveControls.MAX_OFFSET) + quadVertical = (pointerGrabStartVertical + dy) + .coerceIn(ImmersiveControls.MIN_OFFSET, ImmersiveControls.MAX_OFFSET) + quadDistance = (pointerGrabStartDistance - dz) + .coerceIn(ImmersiveControls.MIN_DISTANCE, ImmersiveControls.MAX_DISTANCE) + } + applyQuadTransform() + } + + /** Translates left-stick + A/B into dpad/select/back KeyEvents so QuickMenu's existing + * focusable()/FocusRequester navigation (built for a real gamepad's KeyEvents) works, instead + * of feeding Wine while the menu — which pauses the game anyway — is on screen. */ + private fun handleMenuNavigation(buttons: Int, axes: FloatArray) { + val leftX = axes[0] + val leftY = axes[1] + val direction = when { + // leftY is already sign-flipped upstream (syncControllerInputs) to match XInput's + // "positive = up" convention for Wine — Compose/Android's own dpad convention wants + // the opposite here, so UP/DOWN are swapped back relative to the Wine-facing mapping. + leftY > MENU_DPAD_AXIS_THRESHOLD -> android.view.KeyEvent.KEYCODE_DPAD_DOWN + leftY < -MENU_DPAD_AXIS_THRESHOLD -> android.view.KeyEvent.KEYCODE_DPAD_UP + leftX < -MENU_DPAD_AXIS_THRESHOLD -> android.view.KeyEvent.KEYCODE_DPAD_LEFT + leftX > MENU_DPAD_AXIS_THRESHOLD -> android.view.KeyEvent.KEYCODE_DPAD_RIGHT + else -> null + } + val now = System.currentTimeMillis() + when { + direction == null -> lastMenuDpadKeyCode = null + direction != lastMenuDpadKeyCode -> { + Timber.i( + "Immersive: menu dpad direction=%d leftX=%.2f leftY=%.2f quickMenuVisible=%b isOverlayPaused=%b", + direction, leftX, leftY, quickMenuVisible, PluviaApp.isOverlayPaused, + ) + triggerMenuDirection(direction) + lastMenuDpadKeyCode = direction + lastMenuDpadHeldSince = now + lastMenuDpadEventTime = now + } + now - lastMenuDpadHeldSince > MENU_DPAD_INITIAL_DELAY_MS && + now - lastMenuDpadEventTime > MENU_DPAD_REPEAT_DELAY_MS -> { + triggerMenuDirection(direction) + lastMenuDpadEventTime = now + } + } + + val buttonAPressed = (buttons and (1 shl XrGamepadBridge.BUTTON_A)) != 0 + if (buttonAPressed && !lastMenuButtonAPressed) { + // Radio/toggle rows (ScreenEffectRadioRow, upscale-mode choices) rely on Android's + // default DPAD_CENTER/A-click-on-real-focused-view behavior, which is unreliable here + // since some other View — not Compose's own focus state — holds real view focus. + // quickMenuFocusedActivate reports whichever row is currently focused; invoke it + // directly when present instead of relying on that default click path. + val focusedActivate = quickMenuFocusedActivate?.invoke() + Timber.i("Immersive: A pressed, focusedActivatePresent=%b", focusedActivate != null) + if (focusedActivate != null) { + runOnUiThread { focusedActivate.invoke() } + } else { + // DPAD_CENTER activates plain Modifier.clickable() targets (toggles, the Resume + // button); BUTTON_A specifically is what QuickMenuAdjustmentRow's slider-lock and + // XServerScreen's manualResumeMode handler check for (see their onPreviewKeyEvent / + // onKeyEvent) — send both so either kind of target responds to the same press. + dispatchMenuKeyEvent(android.view.KeyEvent.KEYCODE_DPAD_CENTER) + dispatchMenuKeyEvent(android.view.KeyEvent.KEYCODE_BUTTON_A) + } + } + lastMenuButtonAPressed = buttonAPressed + + val buttonBPressed = (buttons and (1 shl XrGamepadBridge.BUTTON_B)) != 0 + if (buttonBPressed && !lastMenuButtonBPressed) dispatchMenuKeyEvent(android.view.KeyEvent.KEYCODE_BACK) + lastMenuButtonBPressed = buttonBPressed + + // LB/RB cycle the quick-menu's tab rail directly via quickMenuCycleTab — reported as + // unreachable via dpad-left from inside a tab's content, and dispatchMenuKeyEvent's + // KEYCODE_BUTTON_L1/R1 goes through the same broken KeyEvent path as arrow-key movement + // did (confirmed via logging), so this bypasses it the same way moveMenuFocus does. + // stickClickHeld dates back to when XR-pointer-mode activation was a grip+trigger chord, + // which could briefly co-occur with a real LB/RB press and get misread as a deliberate + // tab-cycle pair. That chord is gone — XR pointer mode now toggles via a thumbstick + // double-click (native pointerModeToggled), which never touches the grips — but the gate + // is left in place as a harmless safeguard against any future chord that reintroduces + // the same overlap. + val stickClickHeld = (buttons and ((1 shl XrGamepadBridge.BUTTON_L3) or (1 shl XrGamepadBridge.BUTTON_R3))) != 0 + // Also gated on quickMenuVisible specifically (not just this function running, which + // ALSO happens whenever isOverlayPaused alone is true — e.g. a fullscreen "press A to + // resume" prompt with no tab rail at all) — logs showed LB/RB still "cycling" a tab + // nobody could see in that state, silently leaving the quick menu on a different tab + // than expected the next time it actually opened. + val buttonLBPressed = (buttons and (1 shl XrGamepadBridge.BUTTON_LB)) != 0 + if (buttonLBPressed && !lastMenuButtonLBPressed && !stickClickHeld && quickMenuVisible) { + Timber.i("Immersive: LB pressed, cycleTabPresent=%b", quickMenuCycleTab != null) + runOnUiThread { quickMenuCycleTab?.invoke(false) } + } + lastMenuButtonLBPressed = buttonLBPressed + + val buttonRBPressed = (buttons and (1 shl XrGamepadBridge.BUTTON_RB)) != 0 + if (buttonRBPressed && !lastMenuButtonRBPressed && !stickClickHeld && quickMenuVisible) { + Timber.i("Immersive: RB pressed, cycleTabPresent=%b", quickMenuCycleTab != null) + runOnUiThread { quickMenuCycleTab?.invoke(true) } + } + lastMenuButtonRBPressed = buttonRBPressed + } + + private fun dispatchMenuKeyEvent(keyCode: Int) { + runOnUiThread { + val time = android.os.SystemClock.uptimeMillis() + dispatchMenuKeyEventInternal(android.view.KeyEvent(time, time, android.view.KeyEvent.ACTION_DOWN, keyCode, 0)) + dispatchMenuKeyEventInternal(android.view.KeyEvent(time, time, android.view.KeyEvent.ACTION_UP, keyCode, 0)) + } + } + + /** Routes a dpad direction to a locked slider's decrease/increase if one is active, then to + * the tab-rail bypass for LEFT specifically, otherwise moves focus as normal. + * + * A locked QuickMenuAdjustmentRow's own onPreviewKeyEvent (listening for DPAD_LEFT/RIGHT) + * never sees these presses at all — moveMenuFocus's direct FocusManager bypass intercepts + * arrow keys before they'd ever reach Compose's key dispatch, so without this, a locked + * slider's LEFT/RIGHT just moved focus away mid-adjustment instead of changing its value. + * quickMenuAdjustmentControl (see its own kdoc) is invoked fresh on every call — it's a live + * getter, not a snapshot — so it always reflects whichever row (if any) is currently locked. + * + * LEFT (when no slider is locked) goes through quickMenuFocusTabRail instead of + * moveMenuFocus — moveFocus(Left) reaching the tab rail from content depends on the focused + * row's vertical position lining up with a rail icon in Compose's spatial search, confirmed + * unreliable by comparing two otherwise-identical sessions' logs, so this always jumps + * straight to the current tab's own rail button instead of gambling on that heuristic. */ + private fun triggerMenuDirection(keyCode: Int) { + val active = quickMenuAdjustmentControl?.invoke() + if (active != null && + (keyCode == android.view.KeyEvent.KEYCODE_DPAD_LEFT || keyCode == android.view.KeyEvent.KEYCODE_DPAD_RIGHT) + ) { + val (onDecrease, onIncrease) = active + // Logged specifically to distinguish this branch from the moveFocus one — with no + // log here, a press silently landing in this branch vs. silently landing in + // moveMenuFocus (e.g. because the lock had already dropped) look IDENTICAL from the + // outside, which is exactly the ambiguity that made the "one tick works, then nothing" + // reports hard to pin down from logs alone. + Timber.i("Immersive: adjustment bypass triggered keyCode=%d (locked row's decrease/increase)", keyCode) + // Must run on the UI thread — this drives real Compose/ViewModel state (fps limiter, + // HUD config, etc.) whose recomposition is tied to the UI thread's frame clock. Calling + // it straight from this polling thread was a real bug: the resulting recomposition + // could race with Compose's own focus-commit step, occasionally unfocusing the row + // right as it recomposed — which this row's own .onFocusChanged then read as "lost + // focus" and force-unlocked (isAdjustmentLocked = false), matching exactly what showed + // up as "one adjustment works, then LEFT/RIGHT do nothing until B then A again." + runOnUiThread { if (keyCode == android.view.KeyEvent.KEYCODE_DPAD_LEFT) onDecrease() else onIncrease() } + } else if (keyCode == android.view.KeyEvent.KEYCODE_DPAD_LEFT) { + runOnUiThread { quickMenuFocusTabRail?.invoke() } + } else { + moveMenuFocus(keyCode) + } + } + + /** Moves Compose focus directly via QuickMenu's registered FocusManager, instead of + * dispatching a synthetic dpad KeyEvent through dispatchMenuKeyEvent()/Activity.dispatchKeyEvent() + * — logging confirmed those never reach Compose's key dispatch at all in this Activity (some + * other Android View holds real view-level focus throughout), so DPAD_CENTER/BUTTON_A/BACK + * still go through dispatchMenuKeyEvent (confirmed working), but arrow-key focus movement + * needs this direct bypass instead. */ + private fun moveMenuFocus(keyCode: Int) { + val direction = when (keyCode) { + android.view.KeyEvent.KEYCODE_DPAD_DOWN -> androidx.compose.ui.focus.FocusDirection.Down + android.view.KeyEvent.KEYCODE_DPAD_UP -> androidx.compose.ui.focus.FocusDirection.Up + android.view.KeyEvent.KEYCODE_DPAD_LEFT -> androidx.compose.ui.focus.FocusDirection.Left + android.view.KeyEvent.KEYCODE_DPAD_RIGHT -> androidx.compose.ui.focus.FocusDirection.Right + else -> return + } + runOnUiThread { + val moved = quickMenuFocusManager?.moveFocus(direction) + Timber.i("Immersive: moveFocus direction=%s focusManagerPresent=%b result=%s", direction, quickMenuFocusManager != null, moved) + } + } + + // XServerScreen's key handling (manualResumeMode's BUTTON_A/ENTER check, WinHandler gamepad + // forwarding, the ESC-menu handler) all subscribe to PluviaApp.events' AndroidEvent.KeyEvent + // bus, which only MainActivity's dispatchKeyEvent() feeds — ImmersiveXrActivity has no + // equivalent override, so without this, none of that logic ever sees our synthetic events. + // Mirror MainActivity's dispatchKeyEvent: emit on the bus first, fall back to the normal + // Android/Compose focus dispatch (dpad nav, plain clickable()) if nothing consumed it. + private fun dispatchMenuKeyEventInternal(event: android.view.KeyEvent) { + val consumed = PluviaApp.events.emit(app.gamenative.events.AndroidEvent.KeyEvent(event)) { results -> + results.any { it } + } == true + val fallbackHandled = if (!consumed) dispatchKeyEvent(event) else false + if (event.action == android.view.KeyEvent.ACTION_DOWN) { + Timber.i( + "Immersive: menu key dispatch keyCode=%d consumedByBus=%b dispatchKeyEvent=%b", + event.keyCode, consumed, fallbackHandled, + ) + } + } + + /** + * Two decoupled rates, not one unconditional per-frame path: + * + * - The GAME frame: PixelCopy self-chains as fast as it completes (same as the original + * gameplay-only capture) on a background thread. Compositing it with the cached overlay + * layer below it is plain bitmap-to-bitmap drawing — cheap, no View access needed — so it + * stays on that same background thread and nativeSubmitFrame() runs at full game-frame + * rate, same as before the overlay was ever unified in. + * - The OVERLAY layer (quick menu, Resume button, performance HUD): re-rendered separately + * on a timer (~[OVERLAY_REFRESH_INTERVAL_MS]) via a real View.draw(Canvas) on the UI + * thread into its own cached bitmap — transparent everywhere except whatever overlay UI is + * actually showing. + * + * An earlier version ran the View.draw() step on *every* game frame, which (confirmed by + * testing) tanked performance badly enough to crash — walking the whole Compose tree at + * native-game-frame-rate is far more than a menu/HUD actually needs. Splitting the rates + * keeps the HUD/menu always visible (the thing that motivated unifying the two capture modes + * in the first place) without paying a per-frame Compose-tree cost on the game's own rate. + * + * [overlayLock] guards [overlayLayerBitmap] since the fast (background) and slow (UI) loops + * touch it from different threads. + */ + private fun startFrameCaptureLoop() { + captureThread = HandlerThread("XrFrameCapture").apply { start() } + captureHandler = Handler(captureThread!!.looper) + captureActive.set(true) + scheduleNextCapture() + scheduleNextOverlayRefresh() + } + + /** Attaches a direct-render bridge to the running container's renderer, whichever it turns + * out to be — called (idempotently, see the guard below) from [scheduleNextCapture]'s retry + * loop rather than once at session start, since the game's XServerView/renderer isn't created + * yet at that point for a typical launch. */ + private fun setupDirectRenderBridgeIfSupported() { + if (directGLBridge != null || directVulkanBridge != null) return + val actualRenderer = PluviaApp.xServerView?.renderer + + val glRenderer = actualRenderer as? GLRenderer + if (glRenderer != null) { + val bridge = DirectGLBridge( + onBufferReady = { buffer -> + if (xrSessionHandle != 0L) XrNative.nativeSetSharedGameBuffer(xrSessionHandle, buffer) + directRenderActive = true + applyQuadTransform() + Timber.i("Immersive: direct GL render path active — game frame now shared via GPU buffer, PixelCopy of the game layer stopped") + }, + ) + directGLBridge = bridge + glRenderer.setXrFrameBridge(bridge) + Timber.i("Immersive: GLRenderer detected — direct-render bridge attached, buffer import pending first frame") + return + } + + val vulkanRenderer = actualRenderer as? com.winlator.renderer.VulkanRenderer + if (vulkanRenderer != null) { + val bridge = DirectVulkanBridge( + onFrame = { ahbPtr, _, _ -> + if (xrSessionHandle != 0L) { + XrNative.nativeSetSharedGameBufferPtr(xrSessionHandle, ahbPtr) + if (!directRenderActive) { + directRenderActive = true + applyQuadTransform() + } + } + }, + ) + directVulkanBridge = bridge + vulkanRenderer.setVulkanXrFrameBridge(bridge) + Timber.i("Immersive: VulkanRenderer detected — direct-render bridge attached, waiting for its first AHardwareBuffer (PixelCopy stays as fallback until then)") + return + } + + if (actualRenderer != null && !directRenderProbeLogged) { + directRenderProbeLogged = true + Timber.i("Immersive: renderer is %s (unrecognized) — direct GPU render path unavailable, staying on PixelCopy", actualRenderer.javaClass.simpleName) + } + } + + private fun teardownDirectRenderBridge() { + directVulkanBridge = null + (PluviaApp.xServerView?.renderer as? com.winlator.renderer.VulkanRenderer)?.setVulkanXrFrameBridge(null) + val bridge = directGLBridge + if (bridge != null) { + directGLBridge = null + (PluviaApp.xServerView?.renderer as? GLRenderer)?.setXrFrameBridge(null) + PluviaApp.xServerView?.queueEvent { bridge.release() } + } + directRenderActive = false + } + + private fun scheduleNextCapture() { + if (!captureActive.get()) return + val handler = captureHandler ?: return + + if (directGLBridge == null && directVulkanBridge == null) setupDirectRenderBridgeIfSupported() + + val surfaceView = PluviaApp.xServerView as? SurfaceView + val width = surfaceView?.width ?: 0 + val height = surfaceView?.height ?: 0 + + val glBridge = directGLBridge + if (glBridge != null) { + // Direct-render path: GLRenderer writes the game frame straight into the shared GPU + // buffer from its own thread — no PixelCopy/CPU readback needed for the game layer + // at all. Just keep the shared buffer sized to the surface (ensureAllocated no-ops + // once already the right size) and skip everything below. + if (width > 0 && height > 0) { + PluviaApp.xServerView?.queueEvent { glBridge.ensureAllocated(width, height) } + } + handler.postDelayed({ scheduleNextCapture() }, CAPTURE_RETRY_DELAY_MS) + return + } + + if (directVulkanBridge != null && directRenderActive) { + // Confirmed: VulkanRenderer is already feeding frames straight to the + // native session on its own cadence (see onScanoutBuffer) — nothing to poll here. + // Until directRenderActive is actually confirmed true, fall through to PixelCopy + // below instead (unexpected if it never fires, but PixelCopy staying up is a safe fallback). + handler.postDelayed({ scheduleNextCapture() }, CAPTURE_RETRY_DELAY_MS) + return + } + + if (surfaceView != null && surfaceView !== surfaceCallbackAttachedTo) { + surfaceCallbackAttachedTo?.holder?.removeCallback(surfaceReadyCallback) + // addCallback() does NOT retroactively fire surfaceCreated() if the Surface already + // existed at attach time — seed from isValid() once, then let the callback maintain + // it from here on (see the surfaceReady kdoc for why the callback, not polling + // isValid() on every attempt, is what actually narrows the crash window). + surfaceReady = surfaceView.holder?.surface?.isValid == true + surfaceView.holder.addCallback(surfaceReadyCallback) + surfaceCallbackAttachedTo = surfaceView + } + + if (surfaceView == null || width <= 0 || height <= 0 || !surfaceReady) { + handler.postDelayed({ scheduleNextCapture() }, CAPTURE_RETRY_DELAY_MS) + return + } + + val bitmap = gameCaptureBitmap?.takeIf { it.width == width && it.height == height } + ?: Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { + // PixelCopy from this (opaque) SurfaceView leaves every pixel's alpha byte at 0 + // (confirmed via a diagnostic pixel sample during development) even though the + // RGB content is real — Canvas.drawBitmap's default SRC_OVER blend then treats + // alpha=0 as "draw nothing", silently no-op'ing any attempt to use this as a + // background (see compositeGameFrame). setHasAlpha(false) tells the renderer to + // ignore that channel and treat every pixel as fully opaque. + it.setHasAlpha(false) + gameCaptureBitmap = it + } + + // Re-check right before the call too — belt and suspenders, cheap, and narrows the + // window a little further given the bitmap allocation above can take a moment. + if (!surfaceReady || surfaceView.holder?.surface?.isValid != true) { + handler.postDelayed({ scheduleNextCapture() }, CAPTURE_RETRY_DELAY_MS) + return + } + + val captureStartTime = android.os.SystemClock.uptimeMillis() + try { + PixelCopy.request(surfaceView, bitmap, { result -> + if (result == PixelCopy.SUCCESS) { + compositeGameFrame(width, height) + } + // No cap here previously — this self-chained as fast as PixelCopy completed, + // which can be much faster than the game itself is actually rendering new frames + // (PixelCopy will happily hand back the same unchanged buffer instantly). Every + // one of those "wasted" captures is still a real GPU readback competing with the + // game's own rendering for the same GPU, on top of the XR compositor being fixed + // at the headset's own refresh rate regardless (so capturing faster than that is + // pure waste on that end too). Capping to that rate removes the wasted work + // without capping anything the display could actually show. + val elapsed = android.os.SystemClock.uptimeMillis() - captureStartTime + val delay = (MIN_CAPTURE_INTERVAL_MS - elapsed).coerceAtLeast(0L) + if (delay > 0L) handler.postDelayed({ scheduleNextCapture() }, delay) else scheduleNextCapture() + }, handler) + } catch (t: Throwable) { + Timber.w(t, "PixelCopy request failed") + handler.postDelayed({ scheduleNextCapture() }, CAPTURE_RETRY_DELAY_MS) + } + } + + /** Fast path: cheap bitmap composite (game frame + cached overlay layer), runs on the capture + * thread at full game-frame rate. No View access here — see the capture-loop kdoc above. + * This path only runs when NOT direct-rendering (see scheduleNextCapture). + * + * No pointer-border margin here (or anywhere else in this class) — an earlier version grew + * the quad by a fixed margin band on every side, purely to give resize/move handles a place + * to live outside the game content. That cost a real, measured chunk of the direct-render + * path's fixed swapchain pixel budget (~45% of the pixel area at default scale, since the + * margin was a FIXED size taken out of the SAME fixed-resolution swapchain the game itself + * has to share) — visible as pixelation the user explicitly did not accept as a tradeoff. + * Handles now live INSIDE the game content instead (see drawPointerCursors/nearGrabZone), + * gated behind [resizeHandlesEnabled] so they're only ever drawn/grabbable when the user has + * explicitly asked for them via the quick menu's toggle — the game's own displayed size is + * therefore identical in every controller mode AND never grows for a margin that no longer + * exists, at full sharpness always. */ + private fun compositeGameFrame(width: Int, height: Int) { + if (!captureActive.get() || xrSessionHandle == 0L) return + val gameFrame = gameCaptureBitmap ?: return + + val finalBitmap = finalFrameBitmap?.takeIf { it.width == width && it.height == height } + ?: Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { finalFrameBitmap = it } + + val canvas = android.graphics.Canvas(finalBitmap) + canvas.drawBitmap(gameFrame, 0f, 0f, null) + synchronized(overlayLock) { + overlayLayerBitmap?.let { canvas.drawBitmap(it, 0f, 0f, null) } + } + if (xrPointerModeActive) { + drawPointerCursors(canvas, width, height) + } + XrNative.nativeSubmitFrame(xrSessionHandle, finalBitmap) + } + + /** Slow path: re-renders just the Compose overlay (transparent where the game is) into + * [overlayLayerBitmap], on its own throttled timer, on the UI thread (required for + * View.draw). Self-chaining like the capture loop, independent of it. */ + private fun scheduleNextOverlayRefresh() { + if (!captureActive.get()) return + val handler = captureHandler ?: return + handler.postDelayed({ refreshOverlayLayer() }, OVERLAY_REFRESH_INTERVAL_MS) + } + + private fun refreshOverlayLayer() { + runOnUiThread { + if (!captureActive.get()) { + return@runOnUiThread + } + + val contentView = findViewById(android.R.id.content) + val width = contentView.width + val height = contentView.height + if (width <= 0 || height <= 0) { + scheduleNextOverlayRefresh() + return@runOnUiThread + } + + // SurfaceView reserves a "hole" wherever it sits in the view tree — drawing an + // ancestor via View.draw(Canvas) (not the real windowed rendering pipeline) paints + // that hole as solid BLACK, since there's no separate compositor layer for it to show + // through in an offscreen bitmap. + // + // A previous version toggled the SurfaceView's *visibility* around this draw call, + // which turned out to be the actual cause of two native crashes (SIGSEGV inside + // PixelCopy's own ANativeWindow_acquire, both starting right at game launch): + // visibility changes are a well-known trigger for Android to tear down and recreate + // a SurfaceView's underlying Surface, and this call runs every ~33ms for the entire + // session — so that "fix" was destroying and recreating the game's real rendering + // surface ~30 times a second, directly underneath the capture thread's PixelCopy + // calls. The next attempt, clipOutRect(), avoided touching the Surface at all but + // over-corrected: it excludes that whole screen-sized rect from the canvas, which + // also excludes the menu/HUD, since they're drawn at the same screen coordinates + // (clipping works in coordinates, not "this specific child view"). + // + // Alpha instead of visibility: unlike visibility, alpha is a pure compositing + // property — it doesn't go through updateSurface()/the attach-detach path that + // destroys the Surface — so it should make this View's own contribution (the hole) + // draw as fully transparent without ever affecting the real Surface, while the + // menu/HUD (their own alpha untouched) still draw normally on top. + val surfaceView = PluviaApp.xServerView as? SurfaceView + val previousAlpha = surfaceView?.alpha ?: 1f + + try { + synchronized(overlayLock) { + val bitmap = overlayLayerBitmap?.takeIf { it.width == width && it.height == height } + ?: Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { overlayLayerBitmap = it } + val canvas = android.graphics.Canvas(bitmap) + canvas.drawColor(android.graphics.Color.TRANSPARENT, android.graphics.PorterDuff.Mode.CLEAR) + if (surfaceView != null) surfaceView.alpha = 0f + try { + // No margin anymore (see compositeGameFrame's kdoc) — screen == the whole + // visible quad, in both render paths, so this raw capture can be submitted + // as-is with no inset/scaling step. + contentView.draw(canvas) + if (directRenderActive && xrPointerModeActive) { + drawPointerCursors(canvas, width, height) + } + } finally { + if (surfaceView != null) surfaceView.alpha = previousAlpha + } + } + if (directRenderActive && xrSessionHandle != 0L) { + // The shared GPU buffer already carries the game frame straight from + // GLRenderer/VulkanRenderer — this overlay-only bitmap (transparent everywhere + // except menu/HUD/cursors) is all nativeSubmitFrame needs to feed the native + // dual-sampler blend (see xr_immersive.cpp's directQuadProgram_ branch). + synchronized(overlayLock) { + overlayLayerBitmap?.let { XrNative.nativeSubmitFrame(xrSessionHandle, it) } + } + } + overlayCaptureLogCounter++ + if (overlayCaptureLogCounter % 30 == 0) { + val centerPx = synchronized(overlayLock) { overlayLayerBitmap?.getPixel(width / 2, height / 2) } ?: 0 + Timber.i( + "Immersive: overlay layer refreshed size=%dx%d centerPx=#%08X sessionHandle=%d", + width, + height, + centerPx, + xrSessionHandle, + ) + } + } catch (t: Throwable) { + Timber.w(t, "Overlay layer refresh failed") + } + scheduleNextOverlayRefresh() + } + } + + private fun stopFrameCaptureLoop() { + captureActive.set(false) + captureThread?.quitSafely() + captureThread = null + captureHandler = null + gameCaptureBitmap = null + finalFrameBitmap = null + synchronized(overlayLock) { overlayLayerBitmap = null } + surfaceCallbackAttachedTo?.holder?.removeCallback(surfaceReadyCallback) + surfaceCallbackAttachedTo = null + surfaceReady = false + } + + private fun stopXrSession() { + pollingActive.set(false) + pollingThread?.join(500) + pollingThread = null + stopFrameCaptureLoop() + teardownDirectRenderBridge() + if (xrSessionHandle != 0L) { + XrNative.nativeRequestStop(xrSessionHandle) + XrNative.nativeJoinAndDestroy(xrSessionHandle) + xrSessionHandle = 0L + } + } +} + +/** Shown for a few seconds once the game's window actually appears, explaining the two chords + * that have no other affordance to discover them (quick menu, XR-pointer-mode toggle). */ +@Composable +private fun ImmersiveControlsOnboarding(visible: Boolean, onDismiss: () -> Unit) { + LaunchedEffect(visible) { + if (visible) { + delay(7000) + onDismiss() + } + } + // contentAlignment on the Box itself, not .align() on the child — same pattern as + // ImmersiveModeChangeIndicator below; this was pinned to the top instead of centered. + Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + androidx.compose.animation.AnimatedVisibility( + visible = visible, + enter = fadeIn(), + exit = fadeOut(), + ) { + Surface( + shape = RoundedCornerShape(20.dp), + color = Color.Black.copy(alpha = 0.65f), + ) { + Column( + modifier = Modifier.padding(horizontal = 28.dp, vertical = 18.dp), + verticalArrangement = Arrangement.spacedBy(10.dp), + ) { + Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(14.dp)) { + Icon(Icons.Default.VisibilityOff, contentDescription = null, tint = Color.White, modifier = Modifier.size(28.dp)) + Text(stringResource(R.string.immersive_onboarding_disable_passthrough), color = Color.White, style = MaterialTheme.typography.bodyLarge) + } + Spacer(modifier = Modifier.height(4.dp)) + Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(14.dp)) { + Icon(Icons.Default.Menu, contentDescription = null, tint = Color.White, modifier = Modifier.size(28.dp)) + Text("Fais un appui long sur Start pour ouvrir/fermer le menu rapide", color = Color.White, style = MaterialTheme.typography.bodyLarge) + } + Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(14.dp)) { + Icon(Icons.Default.SportsEsports, contentDescription = null, tint = Color.White, modifier = Modifier.size(28.dp)) + Text("Double-clique un joystick pour basculer manette Xbox / manette XR", color = Color.White, style = MaterialTheme.typography.bodyLarge) + } + } + } + } + } +} + +/** Brief centered indicator, fading in/out, whenever the controller interpretation mode changes + * (Xbox gamepad <-> XR pointer) — the only other feedback for that switch is native log output. */ +@Composable +private fun ImmersiveModeChangeIndicator(pointerModeActive: Boolean) { + var visible by remember { mutableStateOf(false) } + var shownForPointerMode by remember { mutableStateOf(pointerModeActive) } + LaunchedEffect(pointerModeActive) { + shownForPointerMode = pointerModeActive + visible = true + delay(1800) + visible = false + } + // contentAlignment on the Box itself, not .align() on the child — more foolproof against + // whatever was pinning this to the top-left before (reported after testing). + Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + androidx.compose.animation.AnimatedVisibility( + visible = visible, + enter = fadeIn(), + exit = fadeOut(), + ) { + Surface( + shape = RoundedCornerShape(24.dp), + color = Color.Black.copy(alpha = 0.65f), + ) { + Row( + modifier = Modifier.padding(horizontal = 32.dp, vertical = 20.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(16.dp), + ) { + Icon( + imageVector = if (shownForPointerMode) Icons.Default.TouchApp else Icons.Default.SportsEsports, + contentDescription = null, + tint = Color.White, + modifier = Modifier.size(36.dp), + ) + Text( + text = if (shownForPointerMode) "Mode manette XR" else "Mode manette Xbox", + color = Color.White, + style = MaterialTheme.typography.titleMedium, + ) + } + } + } + } +} diff --git a/app/src/main/java/app/gamenative/ui/screen/xr/XrGamepadBridge.kt b/app/src/main/java/app/gamenative/ui/screen/xr/XrGamepadBridge.kt new file mode 100644 index 0000000000..13f9f44f24 --- /dev/null +++ b/app/src/main/java/app/gamenative/ui/screen/xr/XrGamepadBridge.kt @@ -0,0 +1,89 @@ +package app.gamenative.ui.screen.xr + +import com.winlator.inputcontrols.GamepadState +import com.winlator.winhandler.WinHandler + +/** + * Feeds Meta Quest Touch controller input into Wine as a standard Xbox-layout gamepad. + * + * This does not touch Android's InputDevice pipeline at all — [WinHandler.sendVirtualGamepadState] + * writes straight into the same shared-memory buffer used for real Bluetooth/USB controllers + * (see [com.winlator.inputcontrols.ExternalController]), so from the Windows game's point of + * view this is indistinguishable from a physical Xbox controller on slot [slot]. + * + * The native OpenXR session calls [updateButton]/[updateThumbstick]/[updateTrigger] once per + * frame for whatever changed, then [commit] to push the accumulated state to Wine. No d-pad: + * Touch controllers have no physical one, and deriving a fake one from the thumbstick direction + * fought with the real analog values (games saw the stick as a d-pad instead of an Xbox stick). + */ +class XrGamepadBridge( + private val winHandler: WinHandler, + private val slot: Int = 0, +) { + companion object { + // Bit indices, matching the layout WinHandler.sendVirtualGamepadState already assumes. + const val BUTTON_A = 0 + const val BUTTON_B = 1 + const val BUTTON_X = 2 + const val BUTTON_Y = 3 + const val BUTTON_LB = 4 + const val BUTTON_RB = 5 + const val BUTTON_BACK = 6 + const val BUTTON_START = 7 + const val BUTTON_L3 = 8 + const val BUTTON_R3 = 9 + } + + enum class Stick { LEFT, RIGHT } + enum class Trigger { LEFT, RIGHT } + + private val state = GamepadState() + + fun updateButton(buttonIndex: Int, pressed: Boolean) { + state.setPressed(buttonIndex, pressed) + } + + fun updateThumbstick(stick: Stick, x: Float, y: Float) { + val cx = x.coerceIn(-1f, 1f) + val cy = y.coerceIn(-1f, 1f) + when (stick) { + Stick.LEFT -> { + state.thumbLX = cx + state.thumbLY = cy + } + Stick.RIGHT -> { + state.thumbRX = cx + state.thumbRY = cy + } + } + } + + fun updateTrigger(trigger: Trigger, value: Float) { + val clamped = value.coerceIn(0f, 1f) + when (trigger) { + Trigger.LEFT -> state.triggerL = clamped + Trigger.RIGHT -> state.triggerR = clamped + } + } + + /** Pushes the accumulated state to Wine. Call once per frame after the update* calls above. */ + fun commit() { + winHandler.sendVirtualGamepadState(state, slot) + } + + /** + * Applies one raw snapshot as read from [XrNative.nativePollSnapshot] and commits it. + * buttonBitmask bit layout matches [BUTTON_A]..[BUTTON_R3]; axes is + * [leftX, leftY, rightX, rightY, triggerL, triggerR]. + */ + fun applySnapshot(buttonBitmask: Int, axes: FloatArray) { + for (bit in BUTTON_A..BUTTON_R3) { + updateButton(bit, (buttonBitmask and (1 shl bit)) != 0) + } + updateThumbstick(Stick.LEFT, axes[0], axes[1]) + updateThumbstick(Stick.RIGHT, axes[2], axes[3]) + updateTrigger(Trigger.LEFT, axes[4]) + updateTrigger(Trigger.RIGHT, axes[5]) + commit() + } +} diff --git a/app/src/main/java/app/gamenative/ui/screen/xr/XrNative.kt b/app/src/main/java/app/gamenative/ui/screen/xr/XrNative.kt new file mode 100644 index 0000000000..73ef517365 --- /dev/null +++ b/app/src/main/java/app/gamenative/ui/screen/xr/XrNative.kt @@ -0,0 +1,94 @@ +package app.gamenative.ui.screen.xr + +import android.content.Context +import android.graphics.Bitmap +import android.hardware.HardwareBuffer + +/** + * Thin JNI wrapper around the native OpenXR session (app/src/main/cpp/xrimmersive). + * + * Not wired into the Gradle build by default — see the comment at the top of + * app/src/main/cpp/xrimmersive/CMakeLists.txt. Until libxrimmersive.so is built and placed in + * jniLibs/, loading this class will throw UnsatisfiedLinkError. + */ +object XrNative { + init { + System.loadLibrary("xrimmersive") + } + + /** Starts the OpenXR session on its own native thread. Returns an opaque session handle. */ + external fun nativeCreate(activity: Context): Long + + /** Signals the native frame-loop thread to stop; does not block. */ + external fun nativeRequestStop(handle: Long) + + /** Blocks until the native thread has exited, then frees native resources. */ + external fun nativeJoinAndDestroy(handle: Long) + + /** + * Polls the latest controller snapshot. + * outButtons must be an IntArray(1): [0] = Xbox-layout button bitmask. + * outAxes must be a FloatArray(6): [leftX, leftY, rightX, rightY, triggerL, triggerR]. + * outHandPoses must be a FloatArray(12): [leftX,leftY,leftZ, leftFwdX,leftFwdY,leftFwdZ, + * rightX,rightY,rightZ, rightFwdX,rightFwdY,rightFwdZ] — aim-pose ray per hand in the quad's + * local space, meaningful only when outFlags[0] is true. + * outFlags must be a BooleanArray(3): [0] = hand poses valid, [1] = the XR-pointer-mode toggle + * (double-click of either thumbstick) fired since the last poll, [2] = Menu/Start button is + * currently physically held (level, not edge — true for the whole hold). + * Returns true if the quick-menu trigger (Start held 600ms) fired since the last poll. + */ + external fun nativePollSnapshot( + handle: Long, + outButtons: IntArray, + outAxes: FloatArray, + outHandPoses: FloatArray, + outFlags: BooleanArray, + ): Boolean + + /** + * Hands off one PixelCopy'd frame of the game's actual rendered output (ARGB_8888) to be + * drawn into the immersive quad layer. See ImmersiveXrActivity's capture loop. + */ + external fun nativeSubmitFrame(handle: Long, bitmap: Bitmap) + + /** + * Repositions/resizes the virtual screen quad. x/y are tangential (left/right, up/down) + * offsets and z is always -distance — native reinterprets these as orbiting the player on + * both axes at that distance (not plain Cartesian coordinates), so the quad stays facing the + * player as x and/or y move away from 0 (see xr_immersive.cpp's submitQuadLayer). width/ + * height are the quad's real-world size in meters. contentScaleX/Y are content-half-extent / + * quad-half-extent per axis (1.0 if there's no margin band beyond the content) — only + * consumed by the direct-render shader, see kDirectQuadFragmentShader. + */ + external fun nativeSetQuadTransform( + handle: Long, + x: Float, + y: Float, + z: Float, + width: Float, + height: Float, + contentScaleX: Float, + contentScaleY: Float, + ) + + /** + * Toggles the XR_FB_passthrough layer behind the quad. No-op if the runtime/manifest + * doesn't support it (logged from native, see xr_immersive.cpp's setupPassthrough()). + */ + external fun nativeSetPassthroughEnabled(handle: Long, enabled: Boolean) + + /** + * Direct-render path (see DirectGLBridge) — hands the native session the same + * [HardwareBuffer] GLRenderer is writing the game's frame into on its own thread, so it can + * be imported and sampled directly instead of going through PixelCopy. Pass the same buffer + * object every frame; only needs calling again if the buffer identity itself changes. + */ + external fun nativeSetSharedGameBuffer(handle: Long, hardwareBuffer: HardwareBuffer) + + /** + * Same direct-render path, for VulkanRenderer's zero-copy scanout output — ahbPtr is the raw + * AHardwareBuffer* (as a long) VulkanRenderer already has, with no HardwareBuffer Java object + * involved at all. See DirectVulkanBridge/VulkanXrFrameBridge. + */ + external fun nativeSetSharedGameBufferPtr(handle: Long, ahbPtr: Long) +} 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 377a99097f..5a6a735243 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 @@ -29,8 +29,11 @@ import app.gamenative.BuildConfig import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.focusable import androidx.compose.foundation.layout.* import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.* import androidx.compose.material3.DropdownMenu @@ -351,6 +354,39 @@ fun XServerScreen( onWindowMapped: ((Context, Window) -> Unit)? = null, onWindowUnmapped: ((Window) -> Unit)? = null, onGameLaunchError: ((String) -> Unit)? = null, + // Non-null only when hosted by ImmersiveXrActivity — presence alone is what makes the + // QuickMenu's "Immersif" tab show up. + immersiveControls: app.gamenative.ui.screen.xr.ImmersiveControls? = null, + // ImmersiveXrActivity needs to know when the quick menu (or any other non-SurfaceView + // overlay) is showing, because it captures the game's SurfaceView directly (the only way + // to reliably get real frames out of it — capturing the whole Window can't see SurfaceView + // content at all, since it's composited on its own layer) and switches to a whole-window + // capture only while such an overlay is up, since the game is paused underneath anyway. + onQuickMenuVisibilityChanged: (Boolean) -> Unit = {}, + // See QuickMenu's registerFocusManager kdoc — only meaningful when hosted by + // ImmersiveXrActivity, which needs to drive Compose focus directly since dispatching + // synthetic dpad KeyEvents through Activity.dispatchKeyEvent() doesn't reach Compose there. + registerQuickMenuFocusManager: ((androidx.compose.ui.focus.FocusManager) -> Unit)? = null, + // See QuickMenu's registerCycleTab kdoc — same bypass, for LB/RB tab switching. + registerQuickMenuCycleTab: (((Boolean) -> Unit) -> Unit)? = null, + // See QuickMenu's registerAdjustmentControl kdoc — same bypass, for a locked slider's + // left/right drag. + registerQuickMenuAdjustmentControl: ((() -> Pair<() -> Unit, () -> Unit>?) -> Unit)? = null, + // See QuickMenu's registerFocusTabRail kdoc — same bypass, for LEFT reliably reaching the tab + // rail regardless of the focused row's vertical position. + registerQuickMenuFocusTabRail: ((() -> Unit) -> Unit)? = null, + // See QuickMenu's registerFocusedActivate kdoc — same bypass, for a plain radio/toggle row + // whose default DPAD_CENTER/A click doesn't reliably fire in the immersive activity. + registerQuickMenuFocusedActivate: ((() -> (() -> Unit)?) -> Unit)? = null, + // Registers toggleQuickMenu (open/close only, no IME/controller-rescan/touchpad side + // effects) — see its own kdoc. Used by ImmersiveXrActivity's long-press-Start gesture instead + // of the shared, side-effect-heavy registerBackAction handler. + registerQuickMenuToggle: ((() -> Unit) -> Unit)? = null, + // See QuickMenu's registerStartHeld kdoc — pushes the Menu/Start button's physical-hold + // state so QuickMenu can suppress the real Android KeyEvents Horizon OS's own gamepad input + // dispatch generates while it's held (a separate path from this Activity's native OpenXR + // polling/bypasses, immune to all of them). + registerQuickMenuStartHeld: (((Boolean) -> Unit) -> Unit)? = null, ) { Timber.i("Starting up XServerScreen") val context = LocalContext.current @@ -522,6 +558,9 @@ fun XServerScreen( var keyboardRequestedFromOverlay by remember { mutableStateOf(false) } var shouldForceResumeOnMenuClose by remember { mutableStateOf(false) } var showQuickMenu by remember { mutableStateOf(false) } + LaunchedEffect(showQuickMenu) { + onQuickMenuVisibilityChanged(showQuickMenu) + } var quickMenuToolsVisible by remember { mutableStateOf(false) } var quickMenuWineProcesses by remember { mutableStateOf>(emptyList()) } var quickMenuWineProcessesLoading by remember { mutableStateOf(false) } @@ -1026,6 +1065,20 @@ fun XServerScreen( showQuickMenu = false } + // Dedicated open/close toggle for the immersive activity's long-press-Start gesture — + // deliberately NOT gameBack(): that handler also does IME checks, a controller rescan, and + // releases touchpad pointer capture on open, none of which belong to "just open/close the + // quick menu" and were suspected of causing Start to behave like a confirm/select press. + // This does nothing but flip showQuickMenu. + val toggleQuickMenu: () -> Unit = { + Timber.i("toggleQuickMenu: invoked, showQuickMenu=%b", showQuickMenu) + if (showQuickMenu) { + dismissOverlayMenu() + } else { + showQuickMenu = true + } + } + LaunchedEffect(showQuickMenu, quickMenuToolsVisible, xServerView) { if (!showQuickMenu || !quickMenuToolsVisible) { quickMenuWineProcesses = emptyList() @@ -1392,6 +1445,7 @@ fun XServerScreen( DisposableEffect(container) { registerBackAction(gameBack) + registerQuickMenuToggle?.invoke(toggleQuickMenu) onDispose { Timber.d("XServerScreen leaving, clearing back action") removePerformanceHud() @@ -1404,6 +1458,7 @@ fun XServerScreen( PluviaApp.isOverlayPaused = false } registerBackAction { } + registerQuickMenuToggle?.invoke {} } // preserve suspend state across activity recreation while a game is still running } @@ -2628,6 +2683,14 @@ fun XServerScreen( onLsfgMultiplierChanged = ::applyLsfgMultiplier, onLsfgFlowScaleChanged = ::applyLsfgFlowScale, onLsfgPerformanceModeChanged = ::applyLsfgPerformanceMode, + // Immersive tab (tab only visible when hosted by ImmersiveXrActivity) + immersiveControls = immersiveControls, + registerFocusManager = registerQuickMenuFocusManager, + registerCycleTab = registerQuickMenuCycleTab ?: {}, + registerAdjustmentControl = registerQuickMenuAdjustmentControl ?: {}, + registerFocusTabRail = registerQuickMenuFocusTabRail ?: {}, + registerFocusedActivate = registerQuickMenuFocusedActivate ?: {}, + registerStartHeld = registerQuickMenuStartHeld ?: {}, onAnimationComplete = { isMenuVisible -> if (isMenuVisible) { pauseForOverlayIfAllowed() @@ -2654,6 +2717,13 @@ fun XServerScreen( } if (manualResumeMode && PluviaApp.isOverlayPaused && !showQuickMenu && !keepPausedForEditor) { + val resumeButtonFocusRequester = remember { FocusRequester() } + // Not focusable by default touch-only flows don't need it, but a gamepad's + // DPAD_CENTER only activates a clickable() that currently has focus — request it + // as soon as this button appears so it's reachable without a prior focus target. + LaunchedEffect(Unit) { + runCatching { resumeButtonFocusRequester.requestFocus() } + } Box( modifier = Modifier .fillMaxSize() @@ -2671,6 +2741,8 @@ fun XServerScreen( color = androidx.compose.ui.graphics.Color.White, shape = androidx.compose.foundation.shape.CircleShape, ) + .focusRequester(resumeButtonFocusRequester) + .focusable() .clickable(onClick = ::resumeFromManualButton), contentAlignment = Alignment.Center, ) { diff --git a/app/src/main/java/com/winlator/container/Container.java b/app/src/main/java/com/winlator/container/Container.java index d49a22ce7b..ecf86e7df8 100644 --- a/app/src/main/java/com/winlator/container/Container.java +++ b/app/src/main/java/com/winlator/container/Container.java @@ -94,6 +94,7 @@ public enum XrControllerMapping { private String drives = DEFAULT_DRIVES; private String wineVersion = WineInfo.MAIN_WINE_VERSION.identifier(); private boolean showFPS; + private boolean launchImmersiveMode; private boolean launchRealSteam; private boolean launchBionicSteam; private boolean allowSteamUpdates; @@ -354,6 +355,14 @@ public void setShowFPS(boolean showFPS) { this.showFPS = showFPS; } + public boolean isLaunchImmersiveMode() { + return launchImmersiveMode; + } + + public void setLaunchImmersiveMode(boolean launchImmersiveMode) { + this.launchImmersiveMode = launchImmersiveMode; + } + public boolean isLaunchRealSteam() { return launchRealSteam; } @@ -708,6 +717,7 @@ public void saveData() { data.put("wincomponents", wincomponents); data.put("drives", drives); data.put("showFPS", showFPS); + data.put("launchImmersiveMode", launchImmersiveMode); data.put("launchRealSteam", launchRealSteam); data.put("launchBionicSteam", launchBionicSteam); data.put("allowSteamUpdates", allowSteamUpdates); @@ -843,6 +853,9 @@ public void loadData(JSONObject data) throws JSONException { case "showFPS" : setShowFPS(data.getBoolean(key)); break; + case "launchImmersiveMode" : + setLaunchImmersiveMode(data.getBoolean(key)); + break; case "launchRealSteam" : setLaunchRealSteam(data.getBoolean(key)); break; diff --git a/app/src/main/java/com/winlator/renderer/GLHardwareBufferImporter.java b/app/src/main/java/com/winlator/renderer/GLHardwareBufferImporter.java new file mode 100644 index 0000000000..11054d523c --- /dev/null +++ b/app/src/main/java/com/winlator/renderer/GLHardwareBufferImporter.java @@ -0,0 +1,26 @@ +package com.winlator.renderer; + +import android.hardware.HardwareBuffer; + +/** + * Imports an {@link HardwareBuffer} as a GL texture usable in the *calling thread's* current EGL + * context — the public Android SDK has no clean Java binding for the + * eglGetNativeClientBufferANDROID / eglCreateImageKHR / glEGLImageTargetTexture2DOES sequence + * this needs (GLES11Ext's binding still takes a raw java.nio.Buffer, a legacy pre-HardwareBuffer + * calling convention), so this is a thin JNI call into the same native library the Quest + * immersive session already uses (libxrimmersive.so) rather than anything renderer-specific. + * + * Must be called from a thread with a current EGL context (e.g. GLRenderer's own GL thread, + * from inside onDrawFrame/queueEvent) — the resulting texture is only valid in that context. + */ +public final class GLHardwareBufferImporter { + static { + System.loadLibrary("xrimmersive"); + } + + private GLHardwareBufferImporter() {} + + /** Returns a GL texture name bound (GL_TEXTURE_2D) to the given buffer's contents in the + * calling thread's current context, or 0 on failure. */ + public static native int importAsTexture(HardwareBuffer buffer); +} diff --git a/app/src/main/java/com/winlator/renderer/GLRenderer.java b/app/src/main/java/com/winlator/renderer/GLRenderer.java index b397fa28f0..8833eb4489 100644 --- a/app/src/main/java/com/winlator/renderer/GLRenderer.java +++ b/app/src/main/java/com/winlator/renderer/GLRenderer.java @@ -61,6 +61,12 @@ public class GLRenderer implements GLSurfaceView.Renderer, WindowManager.OnWindo private boolean sceneInitialized = false; private final EffectComposer effectComposer; private FrameRating frameRating; + // Null on every launch except the Meta Quest immersive path — see XrFrameBridge's kdoc. + // volatile: set from a background capture thread, read from the GL render thread — see the + // identical field in VulkanRenderer for why a plain field is a real cross-thread visibility + // bug here, not just a theoretical one (confirmed: the equivalent Vulkan bridge never fired + // despite attaching successfully, until this was added there). + private volatile XrFrameBridge xrFrameBridge = null; public GLRenderer(XServerViewGL xServerView, XServer xServer) { this.xServerView = xServerView; @@ -148,8 +154,20 @@ public void onDrawFrame(GL10 gl) { } void drawScene() { + // xrFrameBridge is null on every launch except the Quest immersive path (see + // XrFrameBridge's kdoc) — beginFrame() binds its own FBO (wrapping a GPU buffer the + // immersive session samples directly, no CPU readback needed) and reports the + // resolution to render at; null means it didn't bind anything, so rendering proceeds + // into whatever's already bound (the SurfaceView's own surface) exactly as if this + // bridge didn't exist at all. boolean xrFrame = false; - // if (XrActivity.isSupported()) xrFrame = XrActivity.getInstance().beginFrame(XrActivity.getImmersive(), XrActivity.getSBS()); + if (xrFrameBridge != null) { + int[] xrTargetSize = xrFrameBridge.beginFrame(); + if (xrTargetSize != null) { + xrFrame = true; + setRenderTargetSizeOverride(xrTargetSize[0], xrTargetSize[1]); + } + } int targetWidth = renderTargetWidthOverride > 0 ? renderTargetWidthOverride : surfaceWidth; int targetHeight = renderTargetHeightOverride > 0 ? renderTargetHeightOverride : surfaceHeight; @@ -222,8 +240,10 @@ else if (magnifierEnabled) { if ((!magnifierEnabled && !fullscreen) || renderingToOffscreenTarget) GLES20.glDisable(GLES20.GL_SCISSOR_TEST); if (xrFrame) { - // XrActivity.getInstance().endFrame(); - // XrActivity.updateControllers(); + xrFrameBridge.endFrame(); + // GLSurfaceView normally only redraws when something changes (RENDERMODE_WHEN_DIRTY) + // — the immersive session needs a continuous stream of frames regardless, same as + // GameNativeXR drives its own render loop this way. xServerView.requestRender(); } } @@ -341,6 +361,13 @@ public void setOnFrameRenderedListener(Runnable onFrameRenderedListener) { this.onFrameRenderedListener = onFrameRenderedListener; } + /** See {@link XrFrameBridge}. Pass null to detach (e.g. when leaving immersive mode). */ + public void setXrFrameBridge(XrFrameBridge xrFrameBridge) { + this.xrFrameBridge = xrFrameBridge; + viewportNeedsUpdate = true; + xServerView.requestRender(); + } + private Drawable createRootCursorDrawable() { Context context = xServerView.getContext(); BitmapFactory.Options options = new BitmapFactory.Options(); diff --git a/app/src/main/java/com/winlator/renderer/VulkanRenderer.java b/app/src/main/java/com/winlator/renderer/VulkanRenderer.java index 14f2e857a3..0b2cbb6082 100644 --- a/app/src/main/java/com/winlator/renderer/VulkanRenderer.java +++ b/app/src/main/java/com/winlator/renderer/VulkanRenderer.java @@ -66,6 +66,16 @@ public class VulkanRenderer implements WindowManager.OnWindowModificationListene private Drawable rootCursorDrawable; private Cursor lastCursor = null; private boolean xRenderingPausedForScanout = false; + // volatile: set from ImmersiveXrActivity's own background capture thread, read from + // whichever thread drives onUpdateWindowContentDirect (a real gamepad/game-rendering + // thread, not that capture thread) — without this the write may never become visible on the + // other thread at all (the JIT is free to cache the field's value once the read loop is hot). + private volatile VulkanXrFrameBridge xrFrameBridge = null; + + /** See VulkanXrFrameBridge's kdoc — null except for the Meta Quest immersive path. */ + public void setVulkanXrFrameBridge(VulkanXrFrameBridge xrFrameBridge) { + this.xrFrameBridge = xrFrameBridge; + } private volatile ArrayList renderableWindows = new ArrayList<>(); private static final java.util.concurrent.atomic.AtomicLong ID_GEN = @@ -450,6 +460,15 @@ public void onUpdateWindowContentDirect(Window window, Drawable pixmap, short xO rx, ry, pixmap.width, pixmap.height, fenceFd); g.lock(); } else { + // THIS is the method PresentExtension actually calls for every real + // DXVK/Vulkan frame present (see PresentExtension.java's pixmapPresent — + // it routes VulkanRenderer specifically to onUpdateWindowContentDirect, + // not onUpdateWindowContent, which an earlier version of this hook was + // wrongly placed in and confirmed via logging to never be called at all + // for this renderer/path). This is the one live per-frame call site. + if (xrFrameBridge != null) { + xrFrameBridge.onScanoutBuffer(ahbPtr, pixmap.width, pixmap.height); + } nativeUpdateWindowContentAHB(nativeHandle, targetId, ahbPtr, pixmap.width, pixmap.height, rx, ry); } @@ -506,6 +525,11 @@ public void onUpdateWindowContent(Window window) { xRenderingPausedForScanout = true; } } else if (!scanoutNow) { + // No XR bridge hook here (an earlier version had one) — confirmed via + // logging that this method is never even called for a real DXVK/Vulkan + // game: PresentExtension routes frame presents for VulkanRenderer to + // onUpdateWindowContentDirect below instead (see its own kdoc), not here. + // This one only runs for other WindowManager listener callbacks. nativeUpdateWindowContentAHB(handle, drawableId, ahbPtr, drawable.width, drawable.height, rx, ry); } @@ -754,6 +778,19 @@ private boolean computeEffectsRequireCompositor() { || Math.abs(pendingGamma - 1.0f) > 1e-3f || pendingFilterMode != 0; } + + /** Whether any active effect/filter/color adjustment is currently forcing the compositor + * path, blocking the zero-copy scanout fast-path. See {@link #resetScreenEffects()}. */ + public boolean isEffectsRequireCompositor() { return effectsRequireCompositor; } + + /** Turns off every effect, filter, and color adjustment (back to defaults), letting scanout + * re-establish itself if nothing else is blocking it. Used by the immersive quick-menu tab's + * "reset" action — screen effects are the single most common reason a user would otherwise + * need to hunt through several unrelated settings to get the zero-copy path back. */ + public void resetScreenEffects() { + setFilterMode(0); + setEffect(EFFECT_NONE, 0.0f, SCALE_FIT, 0, 0.0f, 0.0f, 1.0f); + } public int getEffectId() { return pendingEffectId; } public float getSharpness() { return pendingSharpness; } diff --git a/app/src/main/java/com/winlator/renderer/VulkanXrFrameBridge.java b/app/src/main/java/com/winlator/renderer/VulkanXrFrameBridge.java new file mode 100644 index 0000000000..6c5f9dab8c --- /dev/null +++ b/app/src/main/java/com/winlator/renderer/VulkanXrFrameBridge.java @@ -0,0 +1,31 @@ +package com.winlator.renderer; + +/** + * Null except for the Meta Quest immersive path (see ImmersiveXrActivity's DirectVulkanBridge). + * VulkanRenderer already turns the game's rendered frame into an AHardwareBuffer on every frame + * (see onUpdateWindowContentDirect's nativeUpdateWindowContentAHB call) — this hook observes + * that exact same buffer pointer, at the exact same call site, so the OpenXR session can import + * it too instead of falling back to PixelCopy screen-scraping. VulkanRenderer's own behavior is + * unchanged whether or not a bridge is attached. + * + * Two things had to be found and fixed before this actually fired for a real game, in order: + * 1. Named onScanoutBuffer for historical reasons — it was originally attached to + * VulkanRenderer's "zero-copy scanout" path (nativeScanoutSetBuffer), which turned out to be + * unreachable: {@code Drawable.isDirectScanout()} is hardcoded false and never overridden + * anywhere in this codebase, so that branch never executes. + * 2. Moved to the AHB-upload branch instead (nativeUpdateWindowContentAHB) — but in the WRONG + * method at first ({@code onUpdateWindowContent}, called via the generic WindowManager + * listener path). Confirmed via logging that method is never even invoked for a real + * DXVK/Vulkan game at all: {@code PresentExtension}'s pixmapPresent routes VulkanRenderer + * specifically to {@code onUpdateWindowContentDirect} for every frame present instead — that + * is the one actually-live call site this hook now lives in. + */ +public interface VulkanXrFrameBridge { + /** + * ahbPtr is the same raw AHardwareBuffer* (as a jlong) VulkanRenderer is handing to + * nativeUpdateWindowContentAHB for on-screen presentation — read-only, do not release/free it. + * Called on every real frame update for a Vulkan-rendered window (see this interface's kdoc + * for why "scanout" in the name is no longer literally accurate). + */ + void onScanoutBuffer(long ahbPtr, int width, int height); +} diff --git a/app/src/main/java/com/winlator/renderer/XrFrameBridge.java b/app/src/main/java/com/winlator/renderer/XrFrameBridge.java new file mode 100644 index 0000000000..e5151d88c0 --- /dev/null +++ b/app/src/main/java/com/winlator/renderer/XrFrameBridge.java @@ -0,0 +1,27 @@ +package com.winlator.renderer; + +/** + * Optional collaborator for {@link GLRenderer}, set only by the Meta Quest immersive launch path + * (see app.gamenative.ui.screen.xr) — null (the default) for every other launch, on every other + * platform, with zero behavior change to GLRenderer's normal rendering. + * + * The point of this is to let the game render directly into a GPU buffer that the immersive + * session's OpenXR quad can sample from, instead of screen-scraping the finished frame back off + * the SurfaceView (PixelCopy) — which was measurably the dominant cost of that pipeline (a GPU + * readback plus multiple full-frame CPU memory copies, every frame). GLRenderer keeps rendering + * exactly the same draw calls either way; only the framebuffer it targets changes. + */ +public interface XrFrameBridge { + /** + * Called at the very start of drawScene(), before any GL state is touched for this frame. + * Implementations bind their own target framebuffer (wrapping a shared GPU buffer) here. + * Returns the [width, height] GLRenderer should render at this frame, or null if the bridge + * isn't ready yet (e.g. the shared buffer hasn't been set up) — GLRenderer falls back to + * rendering into its own surface as normal for that frame. + */ + int[] beginFrame(); + + /** Called right after GLRenderer finishes issuing draw calls for this frame (still with the + * bridge's framebuffer bound) — implementations do any per-frame teardown/flush here. */ + void endFrame(); +} diff --git a/app/src/main/jniLibs/arm64-v8a/libxrimmersive.so b/app/src/main/jniLibs/arm64-v8a/libxrimmersive.so new file mode 100755 index 0000000000000000000000000000000000000000..7ea189e2d19dd84ab578491d590758ed91b0be48 GIT binary patch literal 1572960 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
    85;Pd->K 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

    e?nzFCZ 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≦-m90396_TDQ%#~1lGiDO~ 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%0D*D35d~81MpaRXP|jHj-tUWT_0XP%t13 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 z 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 Dg9ruR literal 0 HcmV?d00001 diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index fe622d987e..60c9d19a1e 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -76,6 +76,7 @@ Branche débloquée ! Code d\'accès invalide Jouer + Lancer en mode immersif (VR) Installer l\'application Calcul des besoins en espace… Supprimer l\'application @@ -310,6 +311,19 @@ Fermer Général Contrôles + Immersif + Passthrough + Voir ton environnement réel à travers l\'écran virtuel + Activer le passthrough + Poignées de redimensionnement / déplacement + Affiche des poignées juste à l\'intérieur des bords du jeu quand c\'est activé — désactivé par défaut, pour garder l\'image nette sans marge autour. + Afficher les poignées + Performance + Un effet, filtre ou réglage contraste/luminosité/gamma actif bloque le mode de rendu le plus rapide. + Rien ne bloque actuellement le mode de rendu le plus rapide. + Réinitialiser les effets d\'écran + Désactive tous les effets, filtres et réglages contraste/luminosité/gamma. Effet immédiat — aucun redémarrage nécessaire. + Désactive le passthrough pour de meilleures performances HUD de performance Afficher ou masquer la surcouche en jeu. Limiteur de FPS diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 409678657e..2f8b091744 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -85,6 +85,7 @@ Branch unlocked! Invalid access code Play + Launch in immersive mode (VR) Install App Calculating space requirements... Delete App @@ -307,6 +308,19 @@ Close General Controller + Immersive + Passthrough + See your real surroundings through the virtual screen + Enable passthrough + Resize / move handles + Shows grab handles just inside the game\'s own edges while on — off by default, since keeping the screen border-free avoids losing sharpness to a margin. + Show resize/move handles + Performance + An active screen effect, filter, or brightness/contrast/gamma adjustment is blocking the fastest rendering path. + Nothing is currently blocking the fastest rendering path. + Reset screen effects + Turns off every effect, filter, and brightness/contrast/gamma adjustment. Takes effect immediately — no restart needed. + Disable passthrough for better performance %1$d running, tap/click a process to close No running EXEs detected. Performance HUD