From 95f6764bdcd3952c4123a7206f94baddc7a1968a Mon Sep 17 00:00:00 2001 From: xch1523480 Date: Wed, 2 Sep 2026 17:39:09 +0800 Subject: [PATCH 1/2] wine-peek-limitewine-peek-limiter --- app/src/main/feature/library/GameSettings.kt | 8 ++++++++ .../feature/shortcuts/ShortcutSettingsComposeDialog.kt | 3 ++- app/src/main/res/values/strings.xml | 1 + .../components/GuestProgramLauncherComponent.java | 6 ++++++ 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/app/src/main/feature/library/GameSettings.kt b/app/src/main/feature/library/GameSettings.kt index 0e5aa9e1c..6cbdd2fdc 100644 --- a/app/src/main/feature/library/GameSettings.kt +++ b/app/src/main/feature/library/GameSettings.kt @@ -552,6 +552,7 @@ class GameSettingsStateHolder { val numControllersEntries = mutableStateOf>(emptyList()) val selectedNumControllers = mutableIntStateOf(0) val disableXInput = mutableStateOf(false) + val peekMessageLimiter = mutableStateOf(false) val simTouchScreen = mutableStateOf(false) val screenTouchMode = mutableIntStateOf(0) val gestureProfileEntries = mutableStateOf>(emptyList()) @@ -4915,6 +4916,13 @@ private fun InputSection(state: GameSettingsStateHolder) { } ) } + + Spacer(Modifier.height(4.dp)) + SettingCheckbox( + label = stringResource(R.string.shortcuts_properties_peekmessage_limiter), + checked = state.peekMessageLimiter.value, + onCheckedChange = { state.peekMessageLimiter.value = it } + ) } } diff --git a/app/src/main/feature/shortcuts/ShortcutSettingsComposeDialog.kt b/app/src/main/feature/shortcuts/ShortcutSettingsComposeDialog.kt index 7cd29d128..24ec65141 100644 --- a/app/src/main/feature/shortcuts/ShortcutSettingsComposeDialog.kt +++ b/app/src/main/feature/shortcuts/ShortcutSettingsComposeDialog.kt @@ -386,6 +386,7 @@ 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" + state.peekMessageLimiter.value = shortcut.getExtra("peekMessageLimiter", "0") == "1" state.shortcutExclusiveXInput.value = shortcut.getExtra("exclusiveXInput", "").let { if (it.isEmpty()) container.isExclusiveXInput() else it == "1" } @@ -1251,7 +1252,7 @@ class ShortcutSettingsComposeDialog private constructor( val disableXinputValue = if (state.disableXInput.value) "1" else null shortcut.putExtra("disableXinput", disableXinputValue) if (disableXinputValue != null) hasContainerOverride = true - + shortcut.putExtra("peekMessageLimiter", if (state.peekMessageLimiter.value) "1" else "0") 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 610d689b9..d607a1b4b 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) + Limit PeekMessage idle spin (reduce CPU usage in old games) 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 0e3e76f15..bdd065e24 100644 --- a/app/src/main/runtime/display/environment/components/GuestProgramLauncherComponent.java +++ b/app/src/main/runtime/display/environment/components/GuestProgramLauncherComponent.java @@ -776,6 +776,12 @@ 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) + if (shortcut != null && "1".equals(shortcut.getExtra("peekMessageLimiter", "0"))) { + envVars.put("WINE_PEEK_LIMITER", "4"); + Log.d("GuestLauncher", "PeekMessage limiter enabled, delay=4ms"); + } } private void prepareFakeInputUdevMetadata(ImageFs imageFs, File devInputDir, EnvVars envVars) { From ee06b338ccdb8ebfe7d63bfe665a44e5e6551091 Mon Sep 17 00:00:00 2001 From: xch1523480 Date: Wed, 2 Sep 2026 18:06:59 +0800 Subject: [PATCH 2/2] Add a PeekMessage Idle Limiter control option in the shortcut wineD3D configuration. This option is only valid for wineD3D, and DXVK remains unchanged --- app/src/main/feature/library/GameSettings.kt | 33 +++++++++++++++---- .../ShortcutSettingsComposeDialog.kt | 6 ++-- app/src/main/res/values/strings.xml | 2 +- .../GuestProgramLauncherComponent.java | 11 +++++-- 4 files changed, 39 insertions(+), 13 deletions(-) diff --git a/app/src/main/feature/library/GameSettings.kt b/app/src/main/feature/library/GameSettings.kt index 6cbdd2fdc..6576b524b 100644 --- a/app/src/main/feature/library/GameSettings.kt +++ b/app/src/main/feature/library/GameSettings.kt @@ -552,7 +552,16 @@ class GameSettingsStateHolder { val numControllersEntries = mutableStateOf>(emptyList()) val selectedNumControllers = mutableIntStateOf(0) val disableXInput = mutableStateOf(false) - val peekMessageLimiter = 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()) @@ -2604,6 +2613,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 } + ) + } + } + } } } } @@ -4917,12 +4942,6 @@ private fun InputSection(state: GameSettingsStateHolder) { ) } - Spacer(Modifier.height(4.dp)) - SettingCheckbox( - label = stringResource(R.string.shortcuts_properties_peekmessage_limiter), - checked = state.peekMessageLimiter.value, - onCheckedChange = { state.peekMessageLimiter.value = it } - ) } } diff --git a/app/src/main/feature/shortcuts/ShortcutSettingsComposeDialog.kt b/app/src/main/feature/shortcuts/ShortcutSettingsComposeDialog.kt index 24ec65141..a87db791c 100644 --- a/app/src/main/feature/shortcuts/ShortcutSettingsComposeDialog.kt +++ b/app/src/main/feature/shortcuts/ShortcutSettingsComposeDialog.kt @@ -386,7 +386,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" - state.peekMessageLimiter.value = shortcut.getExtra("peekMessageLimiter", "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" } @@ -1252,7 +1253,8 @@ class ShortcutSettingsComposeDialog private constructor( val disableXinputValue = if (state.disableXInput.value) "1" else null shortcut.putExtra("disableXinput", disableXinputValue) if (disableXinputValue != null) hasContainerOverride = true - shortcut.putExtra("peekMessageLimiter", if (state.peekMessageLimiter.value) "1" else "0") + 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 d607a1b4b..62158b479 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -394,7 +394,7 @@ Exec Arguments Target Path Disable Xinput (For Exclusive M/KB Control) - Limit PeekMessage idle spin (reduce CPU usage in old games) + 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 bdd065e24..3fb97f00c 100644 --- a/app/src/main/runtime/display/environment/components/GuestProgramLauncherComponent.java +++ b/app/src/main/runtime/display/environment/components/GuestProgramLauncherComponent.java @@ -778,9 +778,14 @@ private void mergeExternalEnvVars( } // PeekMessage idle-spin limiter (Wine patch, per-shortcut opt-in) - if (shortcut != null && "1".equals(shortcut.getExtra("peekMessageLimiter", "0"))) { - envVars.put("WINE_PEEK_LIMITER", "4"); - Log.d("GuestLauncher", "PeekMessage limiter enabled, delay=4ms"); + 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"); + } } }