diff --git a/app/src/main/feature/library/GameSettings.kt b/app/src/main/feature/library/GameSettings.kt index 1108541e3..86edf9c91 100644 --- a/app/src/main/feature/library/GameSettings.kt +++ b/app/src/main/feature/library/GameSettings.kt @@ -408,6 +408,10 @@ class GameSettingsStateHolder { val containerEntries = mutableStateOf>(emptyList()) val selectedContainer = mutableIntStateOf(0) val screenSizeEntries = mutableStateOf>(emptyList()) + val standardScreenSizeEntries = mutableStateOf>(emptyList()) + val deviceScreenSizeEntries = mutableStateOf>(emptyList()) + val devicePanelSummary = mutableStateOf("") + val showDeviceResolutions = mutableStateOf(false) val selectedScreenSize = mutableIntStateOf(0) val customWidth = mutableStateOf("") val customHeight = mutableStateOf("") @@ -609,6 +613,51 @@ class GameSettingsStateHolder { val drives = mutableStateOf("") val isLoaded = mutableStateOf(false) + + fun applyScreenSizeEntries(useDeviceResolutions: Boolean) { + val device = deviceScreenSizeEntries.value + val standard = standardScreenSizeEntries.value + val useDevice = useDeviceResolutions && device.size > 1 + showDeviceResolutions.value = useDevice + val next = if (useDevice) device else standard + if (next.isEmpty()) return + + val current = selectedScreenSizeValue() + screenSizeEntries.value = next + val index = next.indexOfFirst { StringUtils.parseIdentifier(it) == current } + if (index >= 0) { + selectedScreenSize.intValue = index + return + } + val currentHeight = heightOf(current) + if (currentHeight > 0) { + val tierIndex = next.indexOfFirst { heightOf(StringUtils.parseIdentifier(it)) == currentHeight } + if (tierIndex >= 0) { + selectedScreenSize.intValue = tierIndex + return + } + } + selectedScreenSize.intValue = 0 + val parts = current.split("x") + if (parts.size == 2) { + customWidth.value = parts[0] + customHeight.value = parts[1] + } + } + + private fun heightOf(screenSize: String): Int { + val parts = screenSize.split("x") + return if (parts.size == 2) parts[1].toIntOrNull() ?: 0 else 0 + } + + private fun selectedScreenSizeValue(): String { + val entries = screenSizeEntries.value + val index = selectedScreenSize.intValue + if (index > 0 && index in entries.indices) return StringUtils.parseIdentifier(entries[index]) + val width = customWidth.value.trim() + val height = customHeight.value.trim() + return if (width.isEmpty() || height.isEmpty()) "" else "${width}x$height" + } } interface GameSettingsCallbacks { @@ -1591,6 +1640,26 @@ private fun GeneralSection( } } + if (state.deviceScreenSizeEntries.value.size > 1) { + Spacer(Modifier.height(SettingItemGap)) + Column(verticalArrangement = Arrangement.spacedBy(6.dp)) { + SettingSwitch( + label = stringResource(R.string.container_config_show_device_resolutions), + checked = state.showDeviceResolutions.value, + onCheckedChange = { on -> state.applyScreenSizeEntries(on) } + ) + Text( + stringResource( + R.string.container_config_show_device_resolutions_help, + state.devicePanelSummary.value + ), + color = TextDim, + fontSize = 11.sp, + lineHeight = 16.sp + ) + } + } + // Custom resolution fields when "Custom" is selected (index 0) if (state.selectedScreenSize.intValue == 0) { Spacer(Modifier.height(SettingItemGap)) diff --git a/app/src/main/feature/settings/containers/ContainerSettingsComposeDialog.kt b/app/src/main/feature/settings/containers/ContainerSettingsComposeDialog.kt index 37ca4f6a3..e6ee234f9 100644 --- a/app/src/main/feature/settings/containers/ContainerSettingsComposeDialog.kt +++ b/app/src/main/feature/settings/containers/ContainerSettingsComposeDialog.kt @@ -37,6 +37,7 @@ import com.winlator.cmod.feature.library.GameSettingsStateHolder import com.winlator.cmod.feature.library.WinComponentItem import com.winlator.cmod.feature.library.parseEnvVarItems import com.winlator.cmod.runtime.audio.directaudio.DirectAudioDriver +import com.winlator.cmod.shared.android.DeviceResolutions import com.winlator.cmod.runtime.compat.box64.Box64Preset import com.winlator.cmod.runtime.compat.box64.Box64PresetManager import com.winlator.cmod.runtime.container.Container @@ -522,7 +523,14 @@ class ContainerSettingsComposeDialog @JvmOverloads constructor( val c = container val screenSizeArr = context.resources.getStringArray(R.array.screen_size_entries).toList() + state.standardScreenSizeEntries.value = screenSizeArr + state.deviceScreenSizeEntries.value = + DeviceResolutions.screenSizeEntries(activity, screenSizeArr.firstOrNull() ?: "Custom") + state.devicePanelSummary.value = DeviceResolutions.panelSummary(activity) state.screenSizeEntries.value = screenSizeArr + state.applyScreenSizeEntries( + DeviceResolutions.isEnabled(c?.getExtra(DeviceResolutions.EXTRA_ENABLED)) + ) selectScreenSize(c?.getScreenSize() ?: Container.DEFAULT_SCREEN_SIZE) try { @@ -863,6 +871,10 @@ class ContainerSettingsComposeDialog @JvmOverloads constructor( c.setDXWrapper(dxwrapper) c.setDXWrapperConfig(dxwrapperConfig) c.putExtra("swapRB", if (state.selectedSurfaceEffect.intValue == 1) "1" else "0") + c.putExtra( + DeviceResolutions.EXTRA_ENABLED, + DeviceResolutions.extraValue(state.showDeviceResolutions.value) + ) c.putExtra("refreshRate", getRefreshRateFromState()) writeFrameGenExtras(c) writeNetworkingExtras(c) @@ -956,6 +968,10 @@ class ContainerSettingsComposeDialog @JvmOverloads constructor( "swapRB", if (state.selectedSurfaceEffect.intValue == 1) "1" else "0" ) + newContainer.putExtra( + DeviceResolutions.EXTRA_ENABLED, + DeviceResolutions.extraValue(state.showDeviceResolutions.value) + ) getRefreshRateFromState()?.let { newContainer.putExtra("refreshRate", it) } newContainer.putExtra( DirectAudioDriver.EXTRA_MIC, diff --git a/app/src/main/feature/shortcuts/ShortcutSettingsComposeDialog.kt b/app/src/main/feature/shortcuts/ShortcutSettingsComposeDialog.kt index 4168ca14d..14fa25ca7 100644 --- a/app/src/main/feature/shortcuts/ShortcutSettingsComposeDialog.kt +++ b/app/src/main/feature/shortcuts/ShortcutSettingsComposeDialog.kt @@ -54,6 +54,7 @@ import com.winlator.cmod.feature.settings.WineD3DConfigUtils import com.winlator.cmod.feature.setup.SetupWizardActivity import com.winlator.cmod.feature.stores.steam.events.AndroidEvent import com.winlator.cmod.runtime.audio.directaudio.DirectAudioDriver +import com.winlator.cmod.shared.android.DeviceResolutions import com.winlator.cmod.runtime.compat.box64.Box64Preset import com.winlator.cmod.runtime.compat.box64.Box64PresetManager import com.winlator.cmod.runtime.container.Container @@ -480,7 +481,19 @@ class ShortcutSettingsComposeDialog private constructor( // Screen sizes val screenSizeArr = context.resources.getStringArray(R.array.screen_size_entries).toList() + state.standardScreenSizeEntries.value = screenSizeArr + state.deviceScreenSizeEntries.value = + DeviceResolutions.screenSizeEntries(activity, screenSizeArr.firstOrNull() ?: "Custom") + state.devicePanelSummary.value = DeviceResolutions.panelSummary(activity) state.screenSizeEntries.value = screenSizeArr + state.applyScreenSizeEntries( + DeviceResolutions.isEnabled( + getShortcutSetting( + DeviceResolutions.EXTRA_ENABLED, + container.getExtra(DeviceResolutions.EXTRA_ENABLED) + ) + ) + ) val screenSize = getShortcutSetting("screenSize", container.getScreenSize()) selectScreenSize(screenSize) @@ -1093,6 +1106,13 @@ class ShortcutSettingsComposeDialog private constructor( val screenSize = getScreenSizeFromState() hasContainerOverride = hasContainerOverride or saveOverride("screenSize", screenSize, container.getScreenSize()) + hasContainerOverride = hasContainerOverride or saveOverride( + DeviceResolutions.EXTRA_ENABLED, + DeviceResolutions.extraValue(state.showDeviceResolutions.value), + DeviceResolutions.extraValue( + DeviceResolutions.isEnabled(container.getExtra(DeviceResolutions.EXTRA_ENABLED)) + ) + ) // Graphics driver val graphicsDriver = getIdentifierFromEntries( diff --git a/app/src/main/res/values-b+es+419/strings.xml b/app/src/main/res/values-b+es+419/strings.xml index df21aea20..55cc94f24 100644 --- a/app/src/main/res/values-b+es+419/strings.xml +++ b/app/src/main/res/values-b+es+419/strings.xml @@ -404,6 +404,8 @@ Tamaño de pantalla + Mostrar resoluciones del dispositivo + Ajusta cada resolución estándar a esta pantalla para que nada se estire ni tenga bordes negros (%1$s). Nueva variable de entorno Variables de entorno Afinidad del procesador diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index d8993af42..282ee1e14 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -344,6 +344,8 @@ Skærmstørrelse + Vis enhedens opløsninger + Skalerer hver standardopløsning til denne skærm, så intet strækkes eller får sorte bjælker (%1$s). Ny miljøvariabel Miljøvariabler Processoraffinitet diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 44860fe54..866eb9287 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -344,6 +344,8 @@ Bildschirmgröße + Geräteauflösungen anzeigen + Skaliert jede Standardauflösung auf diesen Bildschirm, sodass nichts verzerrt oder mit schwarzen Balken dargestellt wird (%1$s). Neue Umgebungsvariable Umgebungsvariablen Prozessoraffinität diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 38a2fb7d7..40a0174b6 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -344,6 +344,8 @@ Tamaño de pantalla + Mostrar resoluciones del dispositivo + Ajusta cada resolución estándar a esta pantalla para que nada se estire ni tenga bordes negros (%1$s). Nueva variable de entorno Variables de entorno Afinidad de procesador diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 52c2d203a..d752b794d 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -404,6 +404,8 @@ Näytön koko + Näytä laitteen tarkkuudet + Skaalaa jokaisen vakiotarkkuuden tälle näytölle, jotta mikään ei veny tai jää mustien palkkien väliin (%1$s). Uusi ympäristömuuttuja Ympäristömuuttujat Suorittimen affiniteetti diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 3315f1e87..3e00ceadd 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -344,6 +344,8 @@ Taille de l\'écran + Afficher les résolutions de l\'appareil + Adapte chaque résolution standard à cet écran afin que rien ne soit étiré ni bordé de bandes noires (%1$s). Nouvelle variable d\'environnement Variables d\'environnement Affinité processeur diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index 8a7f6167a..3a4543f8a 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -380,6 +380,8 @@ ऑन-स्क्रीन नियंत्रण पारदर्शिता ऑन-स्क्रीन नियंत्रणों की पारदर्शिता को समायोजित करें। स्क्रीन आकार + डिवाइस रिज़ॉल्यूशन दिखाएँ + हर मानक रिज़ॉल्यूशन को इस स्क्रीन के अनुसार बदलता है, ताकि कुछ भी खिंचे या काली पट्टियों के साथ न दिखे (%1$s)। नया पर्यावरण वैरिएबल पर्यावरण वैरिएबल प्रोसेसर अफ़िनिटी diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index f544aba12..1b12f853b 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -344,6 +344,8 @@ Dimensione schermo + Mostra risoluzioni del dispositivo + Adatta ogni risoluzione standard a questo schermo, così nulla viene stirato o mostrato con bande nere (%1$s). Nuova variabile d\'ambiente Variabili d\'ambiente Affinità processore diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 359521b35..4a04b27d0 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -404,6 +404,8 @@ 画面サイズ + デバイスの解像度を表示 + 各標準解像度をこの画面に合わせて調整し、引き伸ばしや黒帯が出ないようにします (%1$s)。 新しい環境変数 環境変数 プロセッサアフィニティ diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 76c7aff11..2c7be2f67 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -344,6 +344,8 @@ 화면 크기 + 기기 해상도 표시 + 각 표준 해상도를 이 화면에 맞게 조정해 늘어나거나 검은 여백이 생기지 않도록 합니다 (%1$s). 새 환경 변수 환경 변수 프로세서 선호도 diff --git a/app/src/main/res/values-no/strings.xml b/app/src/main/res/values-no/strings.xml index cf3477825..8d46cbb6c 100644 --- a/app/src/main/res/values-no/strings.xml +++ b/app/src/main/res/values-no/strings.xml @@ -404,6 +404,8 @@ Skjermstørrelse + Vis enhetens oppløsninger + Skalerer hver standardoppløsning til denne skjermen, slik at ingenting strekkes eller får svarte kanter (%1$s). Ny miljøvariabel Miljøvariabler Prosessortilknytning diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 859771a86..434572fa6 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -348,6 +348,8 @@ Rozmiar ekranu + Pokaż rozdzielczości urządzenia + Dopasowuje każdą standardową rozdzielczość do tego ekranu, aby nic nie było rozciągnięte ani otoczone czarnymi pasami (%1$s). Nowa zmienna środowiskowa Zmienne środowiskowe Powinowactwo procesora diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 8121b7bc3..23eb9936a 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -344,6 +344,8 @@ Tamanho da Tela + Mostrar Resoluções do Dispositivo + Ajusta cada resolução padrão a esta tela para que nada fique esticado ou com barras pretas (%1$s). Nova Variavel de Ambiente Variaveis de Ambiente Afinidade do Processador diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 366a5a1a3..720abb740 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -404,6 +404,8 @@ Tamanho do ecrã + Mostrar resoluções do dispositivo + Ajusta cada resolução padrão a este ecrã para que nada fique esticado ou com barras pretas (%1$s). Nova variável de ambiente Variáveis de ambiente Afinidade do processador diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 59a0582d7..96d5cb8ab 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -344,6 +344,8 @@ Dimensiune ecran + Afișează rezoluțiile dispozitivului + Adaptează fiecare rezoluție standard la acest ecran, astfel încât nimic să nu fie întins sau cu benzi negre (%1$s). Variabila de mediu noua Variabile de mediu Afinitate procesor diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 0628503ef..f336e0d8a 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -393,6 +393,8 @@ Настройте прозрачность экранных элементов управления. Размер экрана + Показывать разрешения устройства + Подгоняет каждое стандартное разрешение под этот экран, чтобы изображение не растягивалось и не появлялись чёрные полосы (%1$s). Новая переменная среды Переменные среды Привязка процессора diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 4733d6ef8..42f325f78 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -404,6 +404,8 @@ Skärmstorlek + Visa enhetens upplösningar + Skalar varje standardupplösning till den här skärmen så att inget sträcks ut eller får svarta kanter (%1$s). Ny miljövariabel Miljövariabler Processoraffinitet diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml index 466b92865..496c09323 100644 --- a/app/src/main/res/values-th/strings.xml +++ b/app/src/main/res/values-th/strings.xml @@ -404,6 +404,8 @@ ขนาดหน้าจอ + แสดงความละเอียดของอุปกรณ์ + ปรับความละเอียดมาตรฐานแต่ละค่าให้พอดีกับหน้าจอนี้ เพื่อไม่ให้ภาพยืดหรือมีแถบดำ (%1$s) ตัวแปรสภาพแวดล้อมใหม่ ตัวแปรสภาพแวดล้อม ความสัมพันธ์ของโปรเซสเซอร์ diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index fff78c0dc..4c45fbd22 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -404,6 +404,8 @@ Ekran Boyutu + Cihaz Çözünürlüklerini Göster + Her standart çözünürlüğü bu ekrana göre ölçekler, böylece hiçbir şey esnemez veya siyah bantlarla gösterilmez (%1$s). Yeni Ortam Değişkeni Ortam Değişkenleri İşlemci Benzeşimi diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index aea000fe3..093ee8051 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -348,6 +348,8 @@ Розмір екрана + Показувати роздільності пристрою + Підлаштовує кожну стандартну роздільність під цей екран, щоб зображення не розтягувалося та не з’являлися чорні смуги (%1$s). Нова змінна середовища Змінні середовища Прив\'язка до процесора diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 2df634453..46b7f7f2f 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -344,6 +344,8 @@ 屏幕大小 + 显示设备分辨率 + 将每个标准分辨率缩放到此屏幕,避免画面被拉伸或出现黑边 (%1$s)。 新建环境变量 环境变量 处理器亲和性 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index bea3615c4..64b50a332 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -344,6 +344,8 @@ 螢幕大小 + 顯示裝置解析度 + 將每個標準解析度縮放至此螢幕,避免畫面被拉伸或出現黑邊 (%1$s)。 新增環境變數 環境變數 處理器親和性 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1e0fe2249..6691ff2bc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -471,6 +471,8 @@ Screen Size + Show Device Resolutions + Scales each standard resolution to this screen so nothing is stretched or letterboxed (%1$s). New Environment Variable Environment Variables Processor Affinity diff --git a/app/src/main/runtime/container/Container.java b/app/src/main/runtime/container/Container.java index 38c864945..957b5a522 100644 --- a/app/src/main/runtime/container/Container.java +++ b/app/src/main/runtime/container/Container.java @@ -20,11 +20,13 @@ import java.util.Locale; public class Container { - public static final String DEFAULT_ENV_VARS = "WRAPPER_MAX_IMAGE_COUNT=0 VKD3D_SHADER_MODEL=6_6 ZINK_DESCRIPTORS=lazy ZINK_DEBUG=compact MESA_SHADER_CACHE_DISABLE=false MESA_SHADER_CACHE_MAX_SIZE=512MB mesa_glthread=true TU_DEBUG=noconform,sysmem"; + public static final String FAST_YIELD_ENV_VAR = "WINE_FAST_YIELD"; + public static final String FAST_YIELD_ENV_VALUE = "1"; + public static final String DEFAULT_ENV_VARS = "WRAPPER_MAX_IMAGE_COUNT=0 VKD3D_SHADER_MODEL=6_6 ZINK_DESCRIPTORS=lazy ZINK_DEBUG=compact MESA_SHADER_CACHE_DISABLE=false MESA_SHADER_CACHE_MAX_SIZE=512MB mesa_glthread=true TU_DEBUG=noconform,sysmem " + FAST_YIELD_ENV_VAR + "=" + FAST_YIELD_ENV_VALUE; public static final String DEFAULT_SCREEN_SIZE = "1280x720"; public static final String DEFAULT_GRAPHICS_DRIVER = "wrapper"; public static final String DEFAULT_ZINK_MODE = "unix"; - public static final String DEFAULT_AUDIO_DRIVER = "alsa"; + public static final String DEFAULT_AUDIO_DRIVER = "pulseaudio"; public static final String DEFAULT_EMULATOR = "Box64"; public static final String DEFAULT_EMULATOR64 = "Box64"; public static final String DEFAULT_DXWRAPPER = "dxvk+vkd3d"; @@ -668,7 +670,7 @@ else if (graphicsDriver.equals("llvmpipe")) { if (appVersion < 16) { EnvVars defaultEnvVars = new EnvVars(DEFAULT_ENV_VARS); EnvVars envVars = new EnvVars(data.getString("envVars")); - for (String name : defaultEnvVars) if (!name.equals("VKD3D_SHADER_MODEL") && !envVars.has(name)) envVars.put(name, defaultEnvVars.get(name)); + for (String name : defaultEnvVars) if (!name.equals("VKD3D_SHADER_MODEL") && !name.equals(FAST_YIELD_ENV_VAR) && !envVars.has(name)) envVars.put(name, defaultEnvVars.get(name)); data.put("envVars", envVars.toString()); } } diff --git a/app/src/main/shared/android/DeviceResolutions.kt b/app/src/main/shared/android/DeviceResolutions.kt new file mode 100644 index 000000000..45001114d --- /dev/null +++ b/app/src/main/shared/android/DeviceResolutions.kt @@ -0,0 +1,113 @@ +package com.winlator.cmod.shared.android + +import android.app.Activity +import android.os.Build +import android.util.DisplayMetrics +import android.view.Display +import kotlin.math.abs +import kotlin.math.roundToInt + +object DeviceResolutions { + const val EXTRA_ENABLED = "showDeviceResolutions" + private const val MAX_RATIO_TERM = 64 + private val TIER_HEIGHTS = intArrayOf(360, 480, 540, 600, 720, 900, 1080, 1200, 1440, 2160) + + @JvmStatic + fun isEnabled(value: String?): Boolean = value == "1" + + @JvmStatic + fun extraValue(enabled: Boolean): String = if (enabled) "1" else "0" + + @JvmStatic + fun panelSize(activity: Activity): Pair? { + var width = 0 + var height = 0 + displayOf(activity)?.mode?.let { + width = it.physicalWidth + height = it.physicalHeight + } + if (width <= 0 || height <= 0) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + val bounds = activity.windowManager.currentWindowMetrics.bounds + width = bounds.width() + height = bounds.height() + } else { + val metrics = DisplayMetrics() + @Suppress("DEPRECATION") + activity.windowManager.defaultDisplay.getRealMetrics(metrics) + width = metrics.widthPixels + height = metrics.heightPixels + } + } + if (width <= 0 || height <= 0) return null + return maxOf(width, height) to minOf(width, height) + } + + @JvmStatic + fun screenSizeEntries(activity: Activity, customEntry: String): List { + val (longSide, shortSide) = panelSize(activity) ?: return emptyList() + val nativeWidth = toEven(longSide) + val nativeHeight = toEven(shortSide) + if (nativeWidth <= 0 || nativeHeight <= 0) return emptyList() + + val entries = ArrayList() + entries.add(customEntry) + for (tier in TIER_HEIGHTS) { + if (tier >= nativeHeight) continue + val width = toEven((tier.toDouble() * longSide / shortSide).roundToInt()) + if (width < tier) continue + entries.add("${width}x$tier (${tierLabel(tier)})") + } + if (nativeWidth < nativeHeight) return emptyList() + entries.add("${nativeWidth}x$nativeHeight (Native)") + return entries + } + + @JvmStatic + fun panelSummary(activity: Activity): String { + val (longSide, shortSide) = panelSize(activity) ?: return "" + return "${longSide}x$shortSide, ${aspectRatioLabel(longSide, shortSide)}" + } + + private fun displayOf(activity: Activity): Display? { + activity.window?.decorView?.display?.let { return it } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) return activity.display + @Suppress("DEPRECATION") + return activity.windowManager.defaultDisplay + } + + private fun tierLabel(height: Int): String = + when (height) { + 1440 -> "2K" + 2160 -> "4K" + else -> "${height}p" + } + + private fun toEven(value: Int): Int = (value / 2) * 2 + + private fun aspectRatioLabel(longSide: Int, shortSide: Int): String { + val divisor = gcd(longSide, shortSide) + val terms = longSide / divisor to shortSide / divisor + if (terms.first <= MAX_RATIO_TERM && terms.second <= MAX_RATIO_TERM) { + return "${terms.first}:${terms.second}" + } + val ratio = longSide.toDouble() / shortSide + val rounded = (ratio * 100).roundToInt() / 100.0 + return if (abs(rounded - rounded.toInt()) < 0.005) { + "${rounded.toInt()}:1" + } else { + "${rounded}:1" + } + } + + private fun gcd(a: Int, b: Int): Int { + var x = a + var y = b + while (y != 0) { + val t = y + y = x % y + x = t + } + return if (x == 0) 1 else x + } +}