diff --git a/app/src/main/feature/library/GameSettings.kt b/app/src/main/feature/library/GameSettings.kt index eb0554fa3..42b42d0dc 100644 --- a/app/src/main/feature/library/GameSettings.kt +++ b/app/src/main/feature/library/GameSettings.kt @@ -561,6 +561,16 @@ class GameSettingsStateHolder { val numControllersEntries = mutableStateOf>(emptyList()) val selectedNumControllers = mutableIntStateOf(0) val disableXInput = mutableStateOf(false) + val peekMessageLimiterDelayValues = listOf(0, 1, 2, 4, 8, 16) + val peekMessageLimiterEntries = mutableStateOf(listOf( + "Off", + "1ms (Light, ~150fps)", + "2ms (Moderate, ~120fps)", + "4ms (Noticeable, ~80fps)", + "8ms (Strong, ~50fps)", + "16ms (Very Strong, ~30fps)" + )) + val peekMessageLimiterIndex = mutableIntStateOf(0) val simTouchScreen = mutableStateOf(false) val screenTouchMode = mutableIntStateOf(0) val gestureProfileEntries = mutableStateOf>(emptyList()) @@ -2615,6 +2625,22 @@ private fun WineD3DConfigCard(state: GameSettingsStateHolder) { ) } } + + val isContainer = state.isContainerEditMode.value + + if (!isContainer) { + Spacer(Modifier.height(SettingItemGap)) + SettingPairRow { + Box(Modifier.weight(1f)) { + SettingDropdown( + label = stringResource(R.string.shortcuts_properties_peekmessage_limiter), + entries = state.peekMessageLimiterEntries.value, + selectedIndex = state.peekMessageLimiterIndex.intValue, + onSelected = { state.peekMessageLimiterIndex.intValue = it } + ) + } + } + } } } } @@ -4969,6 +4995,7 @@ private fun InputSection(state: GameSettingsStateHolder) { } ) } + } } diff --git a/app/src/main/feature/shortcuts/ShortcutSettingsComposeDialog.kt b/app/src/main/feature/shortcuts/ShortcutSettingsComposeDialog.kt index bb90b8166..809c3a581 100644 --- a/app/src/main/feature/shortcuts/ShortcutSettingsComposeDialog.kt +++ b/app/src/main/feature/shortcuts/ShortcutSettingsComposeDialog.kt @@ -387,6 +387,8 @@ class ShortcutSettingsComposeDialog private constructor( state.selectedDInputMapperType.intValue = if ((inputType and WinHandler.FLAG_DINPUT_MAPPER_STANDARD.toInt()) == WinHandler.FLAG_DINPUT_MAPPER_STANDARD.toInt()) 0 else 1 state.disableXInput.value = shortcut.getExtra("disableXinput", "0") == "1" + val peekDelay = shortcut.getExtra("peekMessageLimiter", "0").toIntOrNull() ?: 0 + state.peekMessageLimiterIndex.intValue = state.peekMessageLimiterDelayValues.indexOf(peekDelay).coerceAtLeast(0) state.shortcutExclusiveXInput.value = shortcut.getExtra("exclusiveXInput", "").let { if (it.isEmpty()) container.isExclusiveXInput() else it == "1" } @@ -1267,7 +1269,8 @@ class ShortcutSettingsComposeDialog private constructor( val disableXinputValue = if (state.disableXInput.value) "1" else null shortcut.putExtra("disableXinput", disableXinputValue) if (disableXinputValue != null) hasContainerOverride = true - + val peekDelay = state.peekMessageLimiterDelayValues.getOrElse(state.peekMessageLimiterIndex.intValue) { 0 } + shortcut.putExtra("peekMessageLimiter", peekDelay.toString()) shortcut.putExtra("exclusiveXInput", if (state.shortcutExclusiveXInput.value) "1" else "0") if (state.shortcutExclusiveXInput.value != container.isExclusiveXInput()) hasContainerOverride = true diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 74974e697..381bded28 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -394,6 +394,7 @@ Exec Arguments Target Path Disable Xinput (For Exclusive M/KB Control) + CPU Limiter Number of Controllers Exclusive Input Custom Game Settings diff --git a/app/src/main/runtime/display/environment/components/GuestProgramLauncherComponent.java b/app/src/main/runtime/display/environment/components/GuestProgramLauncherComponent.java index 997129e32..f964b5bc0 100644 --- a/app/src/main/runtime/display/environment/components/GuestProgramLauncherComponent.java +++ b/app/src/main/runtime/display/environment/components/GuestProgramLauncherComponent.java @@ -776,6 +776,17 @@ private void mergeExternalEnvVars( } else if (overrideFakeUdevDataDir != null && !overrideFakeUdevDataDir.isEmpty()) { envVars.put("FAKE_UDEV_DATA_DIR", overrideFakeUdevDataDir); } + + // PeekMessage idle-spin limiter (Wine patch, per-shortcut opt-in) + String peekDelay = shortcut != null ? shortcut.getExtra("peekMessageLimiter", "0") : "0"; + if (!"0".equals(peekDelay)) { + // Only apply when using WineD3D (not DXVK/VKD3D) + String dxWrapper = shortcut.getExtra("dxwrapper", ""); + if (dxWrapper.toLowerCase().contains("wined3d")) { + envVars.put("WINE_PEEK_LIMITER", peekDelay); + Log.d("GuestLauncher", "PeekMessage limiter enabled, delay=" + peekDelay + "ms"); + } + } } private void prepareFakeInputUdevMetadata(ImageFs imageFs, File devInputDir, EnvVars envVars) {