mirror of
https://git.citron-emu.org/citron/emulator
synced 2025-12-21 11:33:35 +00:00
Merge branch 'android-advanced-emulation-settings' into 'master'
cmake(android): Optimize Vulkan Validation Layer handling See merge request citron/rewrite!21
This commit is contained in:
@@ -85,19 +85,28 @@ option(ENABLE_OPENSSL "Enable OpenSSL backend for ISslConnection" ${DEFAULT_ENAB
|
|||||||
if (ANDROID AND CITRON_DOWNLOAD_ANDROID_VVL)
|
if (ANDROID AND CITRON_DOWNLOAD_ANDROID_VVL)
|
||||||
set(vvl_version "sdk-1.3.261.1")
|
set(vvl_version "sdk-1.3.261.1")
|
||||||
set(vvl_zip_file "${CMAKE_BINARY_DIR}/externals/vvl-android.zip")
|
set(vvl_zip_file "${CMAKE_BINARY_DIR}/externals/vvl-android.zip")
|
||||||
|
set(vvl_lib_path "${CMAKE_CURRENT_SOURCE_DIR}/src/android/app/src/main/jniLibs/arm64-v8a/")
|
||||||
|
set(vvl_lib_file "${vvl_lib_path}/libVkLayer_khronos_validation.so")
|
||||||
|
|
||||||
|
# Only download and extract if the final library file doesn't exist
|
||||||
|
if (NOT EXISTS "${vvl_lib_file}")
|
||||||
if (NOT EXISTS "${vvl_zip_file}")
|
if (NOT EXISTS "${vvl_zip_file}")
|
||||||
# Download and extract validation layer release to externals directory
|
# Download validation layer release to externals directory
|
||||||
set(vvl_base_url "https://github.com/KhronosGroup/Vulkan-ValidationLayers/releases/download")
|
set(vvl_base_url "https://github.com/KhronosGroup/Vulkan-ValidationLayers/releases/download")
|
||||||
file(DOWNLOAD "${vvl_base_url}/${vvl_version}/android-binaries-${vvl_version}-android.zip"
|
file(DOWNLOAD "${vvl_base_url}/${vvl_version}/android-binaries-${vvl_version}-android.zip"
|
||||||
"${vvl_zip_file}" SHOW_PROGRESS)
|
"${vvl_zip_file}" SHOW_PROGRESS)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Extract if not already extracted
|
||||||
|
if (NOT EXISTS "${CMAKE_BINARY_DIR}/externals/android-binaries-${vvl_version}")
|
||||||
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf "${vvl_zip_file}"
|
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf "${vvl_zip_file}"
|
||||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/externals")
|
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/externals")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Copy the arm64 binary to src/android/app/main/jniLibs
|
# Copy the arm64 binary to src/android/app/main/jniLibs
|
||||||
set(vvl_lib_path "${CMAKE_CURRENT_SOURCE_DIR}/src/android/app/src/main/jniLibs/arm64-v8a/")
|
|
||||||
file(COPY "${CMAKE_BINARY_DIR}/externals/android-binaries-${vvl_version}/arm64-v8a/libVkLayer_khronos_validation.so"
|
file(COPY "${CMAKE_BINARY_DIR}/externals/android-binaries-${vvl_version}/arm64-v8a/libVkLayer_khronos_validation.so"
|
||||||
DESTINATION "${vvl_lib_path}")
|
DESTINATION "${vvl_lib_path}")
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (ANDROID)
|
if (ANDROID)
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ val autoVersion = (((System.currentTimeMillis() / 1000) - 1451606400) / 10).toIn
|
|||||||
android {
|
android {
|
||||||
namespace = "org.citron.citron_emu"
|
namespace = "org.citron.citron_emu"
|
||||||
|
|
||||||
compileSdkVersion = "android-34"
|
compileSdkVersion = "android-35"
|
||||||
ndkVersion = "26.1.10909125"
|
ndkVersion = "27.2.12479018" // "26.1.10909125"
|
||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
viewBinding = true
|
viewBinding = true
|
||||||
@@ -57,7 +57,8 @@ android {
|
|||||||
// TODO If this is ever modified, change application_id in strings.xml
|
// TODO If this is ever modified, change application_id in strings.xml
|
||||||
applicationId = "org.citron.citron_emu"
|
applicationId = "org.citron.citron_emu"
|
||||||
minSdk = 30
|
minSdk = 30
|
||||||
targetSdk = 34
|
//noinspection EditedTargetSdkVersion
|
||||||
|
targetSdk = 35
|
||||||
versionName = getGitVersion()
|
versionName = getGitVersion()
|
||||||
|
|
||||||
versionCode = if (System.getenv("AUTO_VERSIONED") == "true") {
|
versionCode = if (System.getenv("AUTO_VERSIONED") == "true") {
|
||||||
@@ -106,12 +107,12 @@ android {
|
|||||||
|
|
||||||
resValue("string", "app_name_suffixed", "citron")
|
resValue("string", "app_name_suffixed", "citron")
|
||||||
isDefault = true
|
isDefault = true
|
||||||
// isShrinkResources = true
|
isShrinkResources = true
|
||||||
isMinifyEnabled = true
|
isMinifyEnabled = true
|
||||||
isDebuggable = false
|
isDebuggable = false
|
||||||
// isJniDebuggable = false
|
isJniDebuggable = false
|
||||||
proguardFiles(
|
proguardFiles(
|
||||||
getDefaultProguardFile("proguard-android.txt"),
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||||
"proguard-rules.pro"
|
"proguard-rules.pro"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -124,7 +125,7 @@ android {
|
|||||||
isMinifyEnabled = true
|
isMinifyEnabled = true
|
||||||
isDebuggable = true
|
isDebuggable = true
|
||||||
proguardFiles(
|
proguardFiles(
|
||||||
getDefaultProguardFile("proguard-android.txt"),
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||||
"proguard-rules.pro"
|
"proguard-rules.pro"
|
||||||
)
|
)
|
||||||
versionNameSuffix = "-relWithDebInfo"
|
versionNameSuffix = "-relWithDebInfo"
|
||||||
@@ -161,7 +162,7 @@ android {
|
|||||||
|
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
version = "3.22.1"
|
version = "3.31.7"
|
||||||
path = file("../../../CMakeLists.txt")
|
path = file("../../../CMakeLists.txt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -178,10 +179,12 @@ android {
|
|||||||
"-DCITRON_USE_BUNDLED_VCPKG=ON",
|
"-DCITRON_USE_BUNDLED_VCPKG=ON",
|
||||||
"-DCITRON_USE_BUNDLED_FFMPEG=ON",
|
"-DCITRON_USE_BUNDLED_FFMPEG=ON",
|
||||||
"-DCITRON_ENABLE_LTO=ON",
|
"-DCITRON_ENABLE_LTO=ON",
|
||||||
"-DCMAKE_EXPORT_COMPILE_COMMANDS=ON"
|
"-DCMAKE_EXPORT_COMPILE_COMMANDS=ON",
|
||||||
|
"-DCMAKE_POLICY_VERSION_MINIMUM=3.5",
|
||||||
|
"-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON",
|
||||||
)
|
)
|
||||||
|
|
||||||
abiFilters("arm64-v8a", "x86_64")
|
abiFilters("arm64-v8a") // , "x86_64")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -239,7 +242,6 @@ dependencies {
|
|||||||
implementation("io.coil-kt:coil:2.2.2")
|
implementation("io.coil-kt:coil:2.2.2")
|
||||||
implementation("androidx.core:core-splashscreen:1.0.1")
|
implementation("androidx.core:core-splashscreen:1.0.1")
|
||||||
implementation("androidx.window:window:1.2.0-beta03")
|
implementation("androidx.window:window:1.2.0-beta03")
|
||||||
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
|
||||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||||
implementation("androidx.navigation:navigation-fragment-ktx:2.7.4")
|
implementation("androidx.navigation:navigation-fragment-ktx:2.7.4")
|
||||||
implementation("androidx.navigation:navigation-ui-ktx:2.7.4")
|
implementation("androidx.navigation:navigation-ui-ktx:2.7.4")
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-FileCopyrightText: 2025 citron Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
package org.citron.citron_emu.features.settings.model
|
package org.citron.citron_emu.features.settings.model
|
||||||
@@ -26,7 +27,8 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
|||||||
SHOW_PERFORMANCE_OVERLAY("show_performance_overlay"),
|
SHOW_PERFORMANCE_OVERLAY("show_performance_overlay"),
|
||||||
SHOW_INPUT_OVERLAY("show_input_overlay"),
|
SHOW_INPUT_OVERLAY("show_input_overlay"),
|
||||||
TOUCHSCREEN("touchscreen"),
|
TOUCHSCREEN("touchscreen"),
|
||||||
SHOW_THERMAL_OVERLAY("show_thermal_overlay");
|
SHOW_THERMAL_OVERLAY("show_thermal_overlay"),
|
||||||
|
SHOW_RAM_METER("show_ram_meter");
|
||||||
|
|
||||||
override fun getBoolean(needsGlobal: Boolean): Boolean =
|
override fun getBoolean(needsGlobal: Boolean): Boolean =
|
||||||
NativeConfig.getBoolean(key, needsGlobal)
|
NativeConfig.getBoolean(key, needsGlobal)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-FileCopyrightText: 2025 citron Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
package org.citron.citron_emu.features.settings.model
|
package org.citron.citron_emu.features.settings.model
|
||||||
@@ -26,7 +27,14 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
|
|||||||
OVERLAY_OPACITY("control_opacity"),
|
OVERLAY_OPACITY("control_opacity"),
|
||||||
LOCK_DRAWER("lock_drawer"),
|
LOCK_DRAWER("lock_drawer"),
|
||||||
VERTICAL_ALIGNMENT("vertical_alignment"),
|
VERTICAL_ALIGNMENT("vertical_alignment"),
|
||||||
FSR_SHARPENING_SLIDER("fsr_sharpening_slider");
|
FSR_SHARPENING_SLIDER("fsr_sharpening_slider"),
|
||||||
|
|
||||||
|
// Zep Zone settings
|
||||||
|
MEMORY_LAYOUT_MODE("memory_layout_mode"),
|
||||||
|
ASTC_DECODE_MODE("accelerate_astc"),
|
||||||
|
ASTC_RECOMPRESSION("astc_recompression"),
|
||||||
|
SHADER_BACKEND("shader_backend"),
|
||||||
|
VRAM_USAGE_MODE("vram_usage_mode");
|
||||||
|
|
||||||
override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal)
|
override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-FileCopyrightText: 2025 citron Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
package org.citron.citron_emu.features.settings.model
|
package org.citron.citron_emu.features.settings.model
|
||||||
@@ -22,7 +23,8 @@ object Settings {
|
|||||||
SECTION_INPUT_PLAYER_SEVEN,
|
SECTION_INPUT_PLAYER_SEVEN,
|
||||||
SECTION_INPUT_PLAYER_EIGHT,
|
SECTION_INPUT_PLAYER_EIGHT,
|
||||||
SECTION_THEME(R.string.preferences_theme),
|
SECTION_THEME(R.string.preferences_theme),
|
||||||
SECTION_DEBUG(R.string.preferences_debug);
|
SECTION_DEBUG(R.string.preferences_debug),
|
||||||
|
SECTION_ZEP_ZONE(R.string.preferences_zep_zone);
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getPlayerString(player: Int): String =
|
fun getPlayerString(player: Int): String =
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-FileCopyrightText: 2025 citron Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
package org.citron.citron_emu.features.settings.model.view
|
package org.citron.citron_emu.features.settings.model.view
|
||||||
@@ -386,6 +387,53 @@ abstract class SettingsItem(
|
|||||||
override fun reset() = setBoolean(defaultValue)
|
override fun reset() = setBoolean(defaultValue)
|
||||||
}
|
}
|
||||||
put(SwitchSetting(fastmem, R.string.fastmem))
|
put(SwitchSetting(fastmem, R.string.fastmem))
|
||||||
|
|
||||||
|
// Zep Zone Settings
|
||||||
|
put(
|
||||||
|
SingleChoiceSetting(
|
||||||
|
IntSetting.MEMORY_LAYOUT_MODE,
|
||||||
|
titleId = R.string.memory_layout_mode,
|
||||||
|
descriptionId = R.string.memory_layout_mode_description,
|
||||||
|
choicesId = R.array.memoryLayoutNames,
|
||||||
|
valuesId = R.array.memoryLayoutValues
|
||||||
|
)
|
||||||
|
)
|
||||||
|
put(
|
||||||
|
SingleChoiceSetting(
|
||||||
|
IntSetting.ASTC_DECODE_MODE,
|
||||||
|
titleId = R.string.astc_decode_mode,
|
||||||
|
descriptionId = R.string.astc_decode_mode_description,
|
||||||
|
choicesId = R.array.astcDecodeModeNames,
|
||||||
|
valuesId = R.array.astcDecodeModeValues
|
||||||
|
)
|
||||||
|
)
|
||||||
|
put(
|
||||||
|
SingleChoiceSetting(
|
||||||
|
IntSetting.ASTC_RECOMPRESSION,
|
||||||
|
titleId = R.string.astc_recompression,
|
||||||
|
descriptionId = R.string.astc_recompression_description,
|
||||||
|
choicesId = R.array.astcRecompressionNames,
|
||||||
|
valuesId = R.array.astcRecompressionValues
|
||||||
|
)
|
||||||
|
)
|
||||||
|
put(
|
||||||
|
SingleChoiceSetting(
|
||||||
|
IntSetting.SHADER_BACKEND,
|
||||||
|
titleId = R.string.shader_backend,
|
||||||
|
descriptionId = R.string.shader_backend_description,
|
||||||
|
choicesId = R.array.shaderBackendNames,
|
||||||
|
valuesId = R.array.shaderBackendValues
|
||||||
|
)
|
||||||
|
)
|
||||||
|
put(
|
||||||
|
SingleChoiceSetting(
|
||||||
|
IntSetting.VRAM_USAGE_MODE,
|
||||||
|
titleId = R.string.vram_usage_mode,
|
||||||
|
descriptionId = R.string.vram_usage_mode_description,
|
||||||
|
choicesId = R.array.vramUsageModeNames,
|
||||||
|
valuesId = R.array.vramUsageModeValues
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-FileCopyrightText: 2025 citron Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
package org.citron.citron_emu.features.settings.ui
|
package org.citron.citron_emu.features.settings.ui
|
||||||
@@ -98,6 +99,7 @@ class SettingsFragmentPresenter(
|
|||||||
MenuTag.SECTION_INPUT_PLAYER_EIGHT -> addInputPlayer(sl, 7)
|
MenuTag.SECTION_INPUT_PLAYER_EIGHT -> addInputPlayer(sl, 7)
|
||||||
MenuTag.SECTION_THEME -> addThemeSettings(sl)
|
MenuTag.SECTION_THEME -> addThemeSettings(sl)
|
||||||
MenuTag.SECTION_DEBUG -> addDebugSettings(sl)
|
MenuTag.SECTION_DEBUG -> addDebugSettings(sl)
|
||||||
|
MenuTag.SECTION_ZEP_ZONE -> addZepZoneSettings(sl)
|
||||||
}
|
}
|
||||||
settingsList = sl
|
settingsList = sl
|
||||||
adapter.submitList(settingsList) {
|
adapter.submitList(settingsList) {
|
||||||
@@ -141,6 +143,14 @@ class SettingsFragmentPresenter(
|
|||||||
menuKey = MenuTag.SECTION_DEBUG
|
menuKey = MenuTag.SECTION_DEBUG
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
add(
|
||||||
|
SubmenuSetting(
|
||||||
|
titleId = R.string.preferences_zep_zone,
|
||||||
|
descriptionId = R.string.preferences_zep_zone_description,
|
||||||
|
iconId = R.drawable.ic_settings,
|
||||||
|
menuKey = MenuTag.SECTION_ZEP_ZONE
|
||||||
|
)
|
||||||
|
)
|
||||||
add(
|
add(
|
||||||
RunnableSetting(
|
RunnableSetting(
|
||||||
titleId = R.string.reset_to_default,
|
titleId = R.string.reset_to_default,
|
||||||
@@ -972,4 +982,19 @@ class SettingsFragmentPresenter(
|
|||||||
add(SettingsItem.FASTMEM_COMBINED)
|
add(SettingsItem.FASTMEM_COMBINED)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun addZepZoneSettings(sl: ArrayList<SettingsItem>) {
|
||||||
|
sl.apply {
|
||||||
|
add(HeaderSetting(R.string.memory_layout_header))
|
||||||
|
add(IntSetting.MEMORY_LAYOUT_MODE.key)
|
||||||
|
|
||||||
|
add(HeaderSetting(R.string.astc_settings_header))
|
||||||
|
add(IntSetting.ASTC_DECODE_MODE.key)
|
||||||
|
add(IntSetting.ASTC_RECOMPRESSION.key)
|
||||||
|
|
||||||
|
add(HeaderSetting(R.string.advanced_graphics_header))
|
||||||
|
add(IntSetting.SHADER_BACKEND.key)
|
||||||
|
add(IntSetting.VRAM_USAGE_MODE.key)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,12 @@ import android.annotation.SuppressLint
|
|||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.IntentFilter
|
||||||
import android.content.pm.ActivityInfo
|
import android.content.pm.ActivityInfo
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.os.BatteryManager
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
@@ -66,6 +69,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
private var emulationActivity: EmulationActivity? = null
|
private var emulationActivity: EmulationActivity? = null
|
||||||
private var perfStatsUpdater: (() -> Unit)? = null
|
private var perfStatsUpdater: (() -> Unit)? = null
|
||||||
private var thermalStatsUpdater: (() -> Unit)? = null
|
private var thermalStatsUpdater: (() -> Unit)? = null
|
||||||
|
private var ramStatsUpdater: (() -> Unit)? = null
|
||||||
|
|
||||||
private var _binding: FragmentEmulationBinding? = null
|
private var _binding: FragmentEmulationBinding? = null
|
||||||
private val binding get() = _binding!!
|
private val binding get() = _binding!!
|
||||||
@@ -374,6 +378,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
// Setup overlays
|
// Setup overlays
|
||||||
updateShowFpsOverlay()
|
updateShowFpsOverlay()
|
||||||
updateThermalOverlay()
|
updateThermalOverlay()
|
||||||
|
updateRamMeterOverlay()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
emulationViewModel.isEmulationStopping.collect(viewLifecycleOwner) {
|
emulationViewModel.isEmulationStopping.collect(viewLifecycleOwner) {
|
||||||
@@ -381,7 +386,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
binding.loadingText.setText(R.string.shutting_down)
|
binding.loadingText.setText(R.string.shutting_down)
|
||||||
ViewUtils.showView(binding.loadingIndicator)
|
ViewUtils.showView(binding.loadingIndicator)
|
||||||
ViewUtils.hideView(binding.inputContainer)
|
ViewUtils.hideView(binding.inputContainer)
|
||||||
ViewUtils.hideView(binding.showFpsText)
|
ViewUtils.hideView(binding.fpsIndicatorView)
|
||||||
|
ViewUtils.hideView(binding.thermalIndicatorView)
|
||||||
|
ViewUtils.hideView(binding.ramMeterView)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
emulationViewModel.drawerOpen.collect(viewLifecycleOwner) {
|
emulationViewModel.drawerOpen.collect(viewLifecycleOwner) {
|
||||||
@@ -486,22 +493,16 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
|
|
||||||
private fun updateShowFpsOverlay() {
|
private fun updateShowFpsOverlay() {
|
||||||
val showOverlay = BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()
|
val showOverlay = BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()
|
||||||
binding.showFpsText.setVisible(showOverlay)
|
binding.fpsIndicatorView.setVisible(showOverlay)
|
||||||
if (showOverlay) {
|
if (showOverlay) {
|
||||||
val SYSTEM_FPS = 0
|
|
||||||
val FPS = 1
|
val FPS = 1
|
||||||
val FRAMETIME = 2
|
|
||||||
val SPEED = 3
|
|
||||||
perfStatsUpdater = {
|
perfStatsUpdater = {
|
||||||
if (emulationViewModel.emulationStarted.value &&
|
if (emulationViewModel.emulationStarted.value &&
|
||||||
!emulationViewModel.isEmulationStopping.value
|
!emulationViewModel.isEmulationStopping.value
|
||||||
) {
|
) {
|
||||||
val perfStats = NativeLibrary.getPerfStats()
|
val perfStats = NativeLibrary.getPerfStats()
|
||||||
val cpuBackend = NativeLibrary.getCpuBackend()
|
|
||||||
val gpuDriver = NativeLibrary.getGpuDriver()
|
|
||||||
if (_binding != null) {
|
if (_binding != null) {
|
||||||
binding.showFpsText.text =
|
binding.fpsIndicatorView.updateFps(perfStats[FPS].toFloat())
|
||||||
String.format("FPS: %.1f\n%s/%s", perfStats[FPS], cpuBackend, gpuDriver)
|
|
||||||
}
|
}
|
||||||
perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 800)
|
perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 800)
|
||||||
}
|
}
|
||||||
@@ -516,26 +517,17 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
|
|
||||||
private fun updateThermalOverlay() {
|
private fun updateThermalOverlay() {
|
||||||
val showOverlay = BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean()
|
val showOverlay = BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean()
|
||||||
binding.showThermalsText.setVisible(showOverlay)
|
binding.thermalIndicatorView.setVisible(showOverlay)
|
||||||
if (showOverlay) {
|
if (showOverlay) {
|
||||||
thermalStatsUpdater = {
|
thermalStatsUpdater = {
|
||||||
if (emulationViewModel.emulationStarted.value &&
|
if (emulationViewModel.emulationStarted.value &&
|
||||||
!emulationViewModel.isEmulationStopping.value
|
!emulationViewModel.isEmulationStopping.value
|
||||||
) {
|
) {
|
||||||
val thermalStatus = when (powerManager.currentThermalStatus) {
|
|
||||||
PowerManager.THERMAL_STATUS_LIGHT -> "😥"
|
|
||||||
PowerManager.THERMAL_STATUS_MODERATE -> "🥵"
|
|
||||||
PowerManager.THERMAL_STATUS_SEVERE -> "🔥"
|
|
||||||
PowerManager.THERMAL_STATUS_CRITICAL,
|
|
||||||
PowerManager.THERMAL_STATUS_EMERGENCY,
|
|
||||||
PowerManager.THERMAL_STATUS_SHUTDOWN -> "☢️"
|
|
||||||
|
|
||||||
else -> "🙂"
|
|
||||||
}
|
|
||||||
if (_binding != null) {
|
if (_binding != null) {
|
||||||
binding.showThermalsText.text = thermalStatus
|
val temperature = getBatteryTemperature(requireContext())
|
||||||
|
binding.thermalIndicatorView.updateTemperature(temperature)
|
||||||
}
|
}
|
||||||
thermalStatsUpdateHandler.postDelayed(thermalStatsUpdater!!, 1000)
|
thermalStatsUpdateHandler.postDelayed(thermalStatsUpdater!!, 2000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
thermalStatsUpdateHandler.post(thermalStatsUpdater!!)
|
thermalStatsUpdateHandler.post(thermalStatsUpdater!!)
|
||||||
@@ -546,6 +538,42 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updateRamMeterOverlay() {
|
||||||
|
val showOverlay = BooleanSetting.SHOW_RAM_METER.getBoolean()
|
||||||
|
binding.ramMeterView.setVisible(showOverlay)
|
||||||
|
if (showOverlay) {
|
||||||
|
ramStatsUpdater = {
|
||||||
|
if (emulationViewModel.emulationStarted.value &&
|
||||||
|
!emulationViewModel.isEmulationStopping.value
|
||||||
|
) {
|
||||||
|
if (_binding != null) {
|
||||||
|
binding.ramMeterView.updateRamUsage()
|
||||||
|
}
|
||||||
|
ramStatsUpdateHandler.postDelayed(ramStatsUpdater!!, 1500)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ramStatsUpdateHandler.post(ramStatsUpdater!!)
|
||||||
|
} else {
|
||||||
|
if (ramStatsUpdater != null) {
|
||||||
|
ramStatsUpdateHandler.removeCallbacks(ramStatsUpdater!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getBatteryTemperature(context: Context): Float {
|
||||||
|
return try {
|
||||||
|
val batteryIntent = context.registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
|
||||||
|
if (batteryIntent != null) {
|
||||||
|
val temperature = batteryIntent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 250)
|
||||||
|
temperature / 10f // Convert from tenths of degrees to degrees
|
||||||
|
} else {
|
||||||
|
25f // Fallback temperature
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
25f // Fallback temperature
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressLint("SourceLockedOrientationActivity")
|
@SuppressLint("SourceLockedOrientationActivity")
|
||||||
private fun updateOrientation() {
|
private fun updateOrientation() {
|
||||||
emulationActivity?.let {
|
emulationActivity?.let {
|
||||||
@@ -676,6 +704,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()
|
BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()
|
||||||
findItem(R.id.thermal_indicator).isChecked =
|
findItem(R.id.thermal_indicator).isChecked =
|
||||||
BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean()
|
BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean()
|
||||||
|
findItem(R.id.ram_meter).isChecked =
|
||||||
|
BooleanSetting.SHOW_RAM_METER.getBoolean()
|
||||||
findItem(R.id.menu_rel_stick_center).isChecked =
|
findItem(R.id.menu_rel_stick_center).isChecked =
|
||||||
BooleanSetting.JOYSTICK_REL_CENTER.getBoolean()
|
BooleanSetting.JOYSTICK_REL_CENTER.getBoolean()
|
||||||
findItem(R.id.menu_dpad_slide).isChecked = BooleanSetting.DPAD_SLIDE.getBoolean()
|
findItem(R.id.menu_dpad_slide).isChecked = BooleanSetting.DPAD_SLIDE.getBoolean()
|
||||||
@@ -702,6 +732,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
R.id.ram_meter -> {
|
||||||
|
it.isChecked = !it.isChecked
|
||||||
|
BooleanSetting.SHOW_RAM_METER.setBoolean(it.isChecked)
|
||||||
|
updateRamMeterOverlay()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
R.id.menu_edit_overlay -> {
|
R.id.menu_edit_overlay -> {
|
||||||
binding.drawerLayout.close()
|
binding.drawerLayout.close()
|
||||||
binding.surfaceInputOverlay.requestFocus()
|
binding.surfaceInputOverlay.requestFocus()
|
||||||
@@ -1046,5 +1083,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
companion object {
|
companion object {
|
||||||
private val perfStatsUpdateHandler = Handler(Looper.myLooper()!!)
|
private val perfStatsUpdateHandler = Handler(Looper.myLooper()!!)
|
||||||
private val thermalStatsUpdateHandler = Handler(Looper.myLooper()!!)
|
private val thermalStatsUpdateHandler = Handler(Looper.myLooper()!!)
|
||||||
|
private val ramStatsUpdateHandler = Handler(Looper.myLooper()!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,141 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2025 citron Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
package org.citron.citron_emu.views
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.Paint
|
||||||
|
import android.graphics.RectF
|
||||||
|
import android.graphics.Typeface
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.View
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
class FpsIndicatorView @JvmOverloads constructor(
|
||||||
|
context: Context,
|
||||||
|
attrs: AttributeSet? = null,
|
||||||
|
defStyleAttr: Int = 0
|
||||||
|
) : View(context, attrs, defStyleAttr) {
|
||||||
|
|
||||||
|
private val backgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
|
color = Color.parseColor("#80000000") // Semi-transparent black
|
||||||
|
style = Paint.Style.FILL
|
||||||
|
}
|
||||||
|
|
||||||
|
private val borderPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
|
color = Color.WHITE
|
||||||
|
style = Paint.Style.STROKE
|
||||||
|
strokeWidth = 2f
|
||||||
|
}
|
||||||
|
|
||||||
|
private val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
|
color = Color.WHITE
|
||||||
|
textSize = 22f
|
||||||
|
typeface = Typeface.DEFAULT_BOLD
|
||||||
|
textAlign = Paint.Align.CENTER
|
||||||
|
setShadowLayer(2f, 1f, 1f, Color.BLACK)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val smallTextPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
|
color = Color.WHITE
|
||||||
|
textSize = 16f
|
||||||
|
typeface = Typeface.DEFAULT
|
||||||
|
textAlign = Paint.Align.CENTER
|
||||||
|
setShadowLayer(2f, 1f, 1f, Color.BLACK)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val iconPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
|
color = Color.WHITE
|
||||||
|
textSize = 24f
|
||||||
|
textAlign = Paint.Align.CENTER
|
||||||
|
setShadowLayer(2f, 1f, 1f, Color.BLACK)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var currentFps: Float = 0f
|
||||||
|
private val fpsIcon: String = "📊"
|
||||||
|
|
||||||
|
private val backgroundRect = RectF()
|
||||||
|
|
||||||
|
fun updateFps(fps: Float) {
|
||||||
|
try {
|
||||||
|
currentFps = fps
|
||||||
|
Log.d("FpsIndicator", "FPS updated: $currentFps")
|
||||||
|
|
||||||
|
// Update color based on FPS performance
|
||||||
|
val fpsColor = when {
|
||||||
|
currentFps >= 55f -> Color.parseColor("#4CAF50") // Green - Good performance
|
||||||
|
currentFps >= 45f -> Color.parseColor("#FF9800") // Orange - Moderate performance
|
||||||
|
currentFps >= 30f -> Color.parseColor("#FF5722") // Red orange - Poor performance
|
||||||
|
else -> Color.parseColor("#F44336") // Red - Very poor performance
|
||||||
|
}
|
||||||
|
|
||||||
|
textPaint.color = fpsColor
|
||||||
|
smallTextPaint.color = fpsColor
|
||||||
|
borderPaint.color = fpsColor
|
||||||
|
|
||||||
|
// Always invalidate to trigger a redraw
|
||||||
|
invalidate()
|
||||||
|
Log.d("FpsIndicator", "View invalidated, FPS: $currentFps")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// Fallback in case of any errors
|
||||||
|
currentFps = 0f
|
||||||
|
Log.e("FpsIndicator", "Error updating FPS", e)
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||||
|
val desiredWidth = 120
|
||||||
|
val desiredHeight = 60
|
||||||
|
|
||||||
|
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
|
||||||
|
val widthSize = MeasureSpec.getSize(widthMeasureSpec)
|
||||||
|
val heightMode = MeasureSpec.getMode(heightMeasureSpec)
|
||||||
|
val heightSize = MeasureSpec.getSize(heightMeasureSpec)
|
||||||
|
|
||||||
|
val width = when (widthMode) {
|
||||||
|
MeasureSpec.EXACTLY -> widthSize
|
||||||
|
MeasureSpec.AT_MOST -> minOf(desiredWidth, widthSize)
|
||||||
|
else -> desiredWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
val height = when (heightMode) {
|
||||||
|
MeasureSpec.EXACTLY -> heightSize
|
||||||
|
MeasureSpec.AT_MOST -> minOf(desiredHeight, heightSize)
|
||||||
|
else -> desiredHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
setMeasuredDimension(width, height)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
|
||||||
|
super.onSizeChanged(w, h, oldw, oldh)
|
||||||
|
backgroundRect.set(4f, 4f, w - 4f, h - 4f)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDraw(canvas: Canvas) {
|
||||||
|
super.onDraw(canvas)
|
||||||
|
|
||||||
|
// Draw background with rounded corners
|
||||||
|
canvas.drawRoundRect(backgroundRect, 12f, 12f, backgroundPaint)
|
||||||
|
canvas.drawRoundRect(backgroundRect, 12f, 12f, borderPaint)
|
||||||
|
|
||||||
|
val centerX = width / 2f
|
||||||
|
val centerY = height / 2f
|
||||||
|
|
||||||
|
// Draw FPS icon on the left
|
||||||
|
canvas.drawText(fpsIcon, 18f, centerY - 8f, iconPaint)
|
||||||
|
|
||||||
|
// Draw FPS value (main text)
|
||||||
|
val fpsText = "${currentFps.roundToInt()}"
|
||||||
|
canvas.drawText(fpsText, centerX, centerY - 8f, textPaint)
|
||||||
|
|
||||||
|
// Draw "FPS" label (smaller text below)
|
||||||
|
canvas.drawText("FPS", centerX, centerY + 12f, smallTextPaint)
|
||||||
|
|
||||||
|
Log.d("FpsIndicator", "onDraw called - FPS: $fpsText")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,195 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2025 citron Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
package org.citron.citron_emu.views
|
||||||
|
|
||||||
|
import android.app.ActivityManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.Paint
|
||||||
|
import android.graphics.RectF
|
||||||
|
import android.graphics.Typeface
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.View
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
class RamMeterView @JvmOverloads constructor(
|
||||||
|
context: Context,
|
||||||
|
attrs: AttributeSet? = null,
|
||||||
|
defStyleAttr: Int = 0
|
||||||
|
) : View(context, attrs, defStyleAttr) {
|
||||||
|
|
||||||
|
private val backgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
|
color = Color.parseColor("#80000000") // Semi-transparent black
|
||||||
|
style = Paint.Style.FILL
|
||||||
|
}
|
||||||
|
|
||||||
|
private val borderPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
|
color = Color.WHITE
|
||||||
|
style = Paint.Style.STROKE
|
||||||
|
strokeWidth = 2f
|
||||||
|
}
|
||||||
|
|
||||||
|
private val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
|
color = Color.WHITE
|
||||||
|
textSize = 20f
|
||||||
|
typeface = Typeface.DEFAULT_BOLD
|
||||||
|
textAlign = Paint.Align.CENTER
|
||||||
|
setShadowLayer(2f, 1f, 1f, Color.BLACK)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val smallTextPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
|
color = Color.WHITE
|
||||||
|
textSize = 14f
|
||||||
|
typeface = Typeface.DEFAULT
|
||||||
|
textAlign = Paint.Align.CENTER
|
||||||
|
setShadowLayer(2f, 1f, 1f, Color.BLACK)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val iconPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
|
color = Color.WHITE
|
||||||
|
textSize = 24f
|
||||||
|
textAlign = Paint.Align.CENTER
|
||||||
|
setShadowLayer(2f, 1f, 1f, Color.BLACK)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val meterBackgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
|
color = Color.parseColor("#40FFFFFF") // Semi-transparent white
|
||||||
|
style = Paint.Style.FILL
|
||||||
|
}
|
||||||
|
|
||||||
|
private val meterFillPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
|
color = Color.GREEN
|
||||||
|
style = Paint.Style.FILL
|
||||||
|
}
|
||||||
|
|
||||||
|
private var ramUsagePercent: Float = 0f
|
||||||
|
private var usedRamMB: Long = 0L
|
||||||
|
private var totalRamMB: Long = 0L
|
||||||
|
private var ramIcon: String = "🧠"
|
||||||
|
|
||||||
|
private val backgroundRect = RectF()
|
||||||
|
private val meterBackgroundRect = RectF()
|
||||||
|
private val meterFillRect = RectF()
|
||||||
|
|
||||||
|
fun updateRamUsage() {
|
||||||
|
try {
|
||||||
|
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
||||||
|
val memoryInfo = ActivityManager.MemoryInfo()
|
||||||
|
activityManager.getMemoryInfo(memoryInfo)
|
||||||
|
|
||||||
|
totalRamMB = memoryInfo.totalMem / (1024 * 1024)
|
||||||
|
val availableRamMB = memoryInfo.availMem / (1024 * 1024)
|
||||||
|
usedRamMB = totalRamMB - availableRamMB
|
||||||
|
ramUsagePercent = (usedRamMB.toFloat() / totalRamMB.toFloat()) * 100f
|
||||||
|
|
||||||
|
// Update meter color based on usage
|
||||||
|
val meterColor = when {
|
||||||
|
ramUsagePercent < 50f -> Color.parseColor("#4CAF50") // Green
|
||||||
|
ramUsagePercent < 75f -> Color.parseColor("#FF9800") // Orange
|
||||||
|
ramUsagePercent < 90f -> Color.parseColor("#FF5722") // Red orange
|
||||||
|
else -> Color.parseColor("#F44336") // Red
|
||||||
|
}
|
||||||
|
|
||||||
|
meterFillPaint.color = meterColor
|
||||||
|
textPaint.color = meterColor
|
||||||
|
smallTextPaint.color = meterColor
|
||||||
|
borderPaint.color = meterColor
|
||||||
|
|
||||||
|
// Update icon based on usage
|
||||||
|
ramIcon = when {
|
||||||
|
ramUsagePercent < 50f -> "🧠" // Normal brain
|
||||||
|
ramUsagePercent < 75f -> "⚡" // Warning
|
||||||
|
ramUsagePercent < 90f -> "🔥" // Hot
|
||||||
|
else -> "💥" // Critical
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidate()
|
||||||
|
Log.d("RamMeter", "RAM usage updated: ${ramUsagePercent.roundToInt()}% (${usedRamMB}MB/${totalRamMB}MB)")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("RamMeter", "Error updating RAM usage", e)
|
||||||
|
ramUsagePercent = 0f
|
||||||
|
usedRamMB = 0L
|
||||||
|
totalRamMB = 0L
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||||
|
val desiredWidth = 140
|
||||||
|
val desiredHeight = 60
|
||||||
|
|
||||||
|
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
|
||||||
|
val widthSize = MeasureSpec.getSize(widthMeasureSpec)
|
||||||
|
val heightMode = MeasureSpec.getMode(heightMeasureSpec)
|
||||||
|
val heightSize = MeasureSpec.getSize(heightMeasureSpec)
|
||||||
|
|
||||||
|
val width = when (widthMode) {
|
||||||
|
MeasureSpec.EXACTLY -> widthSize
|
||||||
|
MeasureSpec.AT_MOST -> minOf(desiredWidth, widthSize)
|
||||||
|
else -> desiredWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
val height = when (heightMode) {
|
||||||
|
MeasureSpec.EXACTLY -> heightSize
|
||||||
|
MeasureSpec.AT_MOST -> minOf(desiredHeight, heightSize)
|
||||||
|
else -> desiredHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
setMeasuredDimension(width, height)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
|
||||||
|
super.onSizeChanged(w, h, oldw, oldh)
|
||||||
|
backgroundRect.set(4f, 4f, w - 4f, h - 4f)
|
||||||
|
|
||||||
|
// Setup meter rectangles - compact horizontal bar at the bottom
|
||||||
|
val meterLeft = 30f
|
||||||
|
val meterTop = h - 18f
|
||||||
|
val meterRight = w - 10f
|
||||||
|
val meterBottom = h - 10f
|
||||||
|
|
||||||
|
meterBackgroundRect.set(meterLeft, meterTop, meterRight, meterBottom)
|
||||||
|
meterFillRect.set(meterLeft, meterTop, meterRight, meterBottom)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDraw(canvas: Canvas) {
|
||||||
|
super.onDraw(canvas)
|
||||||
|
|
||||||
|
// Draw background with rounded corners
|
||||||
|
canvas.drawRoundRect(backgroundRect, 12f, 12f, backgroundPaint)
|
||||||
|
canvas.drawRoundRect(backgroundRect, 12f, 12f, borderPaint)
|
||||||
|
|
||||||
|
val centerX = width / 2f
|
||||||
|
val centerY = height / 2f
|
||||||
|
|
||||||
|
// Draw RAM icon on the left
|
||||||
|
canvas.drawText(ramIcon, 18f, centerY - 8f, iconPaint)
|
||||||
|
|
||||||
|
// Draw percentage text at the top center
|
||||||
|
val percentText = "${ramUsagePercent.roundToInt()}%"
|
||||||
|
canvas.drawText(percentText, centerX, centerY - 8f, textPaint)
|
||||||
|
|
||||||
|
// Draw memory usage text below percentage
|
||||||
|
val usedGB = usedRamMB / 1024f
|
||||||
|
val totalGB = totalRamMB / 1024f
|
||||||
|
val memoryText = if (totalGB >= 1.0f) {
|
||||||
|
"%.1fGB/%.1fGB".format(usedGB, totalGB)
|
||||||
|
} else {
|
||||||
|
"${usedRamMB}MB/${totalRamMB}MB"
|
||||||
|
}
|
||||||
|
canvas.drawText(memoryText, centerX, centerY + 8f, smallTextPaint)
|
||||||
|
|
||||||
|
// Draw RAM meter background at the bottom
|
||||||
|
canvas.drawRoundRect(meterBackgroundRect, 4f, 4f, meterBackgroundPaint)
|
||||||
|
|
||||||
|
// Draw RAM meter fill
|
||||||
|
val fillWidth = meterBackgroundRect.width() * (ramUsagePercent / 100f)
|
||||||
|
meterFillRect.right = meterBackgroundRect.left + fillWidth
|
||||||
|
canvas.drawRoundRect(meterFillRect, 4f, 4f, meterFillPaint)
|
||||||
|
|
||||||
|
Log.d("RamMeter", "onDraw called - Usage: $percentText, Memory: $memoryText")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2025 citron Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
package org.citron.citron_emu.views
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.IntentFilter
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.Paint
|
||||||
|
import android.graphics.RectF
|
||||||
|
import android.graphics.Typeface
|
||||||
|
import android.os.BatteryManager
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.View
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import org.citron.citron_emu.R
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
class ThermalIndicatorView @JvmOverloads constructor(
|
||||||
|
context: Context,
|
||||||
|
attrs: AttributeSet? = null,
|
||||||
|
defStyleAttr: Int = 0
|
||||||
|
) : View(context, attrs, defStyleAttr) {
|
||||||
|
|
||||||
|
private val backgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
|
color = Color.parseColor("#80000000") // Semi-transparent black
|
||||||
|
style = Paint.Style.FILL
|
||||||
|
}
|
||||||
|
|
||||||
|
private val borderPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
|
color = Color.WHITE
|
||||||
|
style = Paint.Style.STROKE
|
||||||
|
strokeWidth = 2f
|
||||||
|
}
|
||||||
|
|
||||||
|
private val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
|
color = Color.WHITE
|
||||||
|
textSize = 22f
|
||||||
|
typeface = Typeface.DEFAULT_BOLD
|
||||||
|
textAlign = Paint.Align.CENTER
|
||||||
|
setShadowLayer(2f, 1f, 1f, Color.BLACK)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val smallTextPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
|
color = Color.WHITE
|
||||||
|
textSize = 16f
|
||||||
|
typeface = Typeface.DEFAULT
|
||||||
|
textAlign = Paint.Align.CENTER
|
||||||
|
setShadowLayer(2f, 1f, 1f, Color.BLACK)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val iconPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
|
color = Color.WHITE
|
||||||
|
textSize = 24f
|
||||||
|
textAlign = Paint.Align.CENTER
|
||||||
|
setShadowLayer(2f, 1f, 1f, Color.BLACK)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var batteryTemperature: Float = 0f
|
||||||
|
private var thermalStatus: String = "🌡️"
|
||||||
|
|
||||||
|
private val backgroundRect = RectF()
|
||||||
|
|
||||||
|
fun updateTemperature(temperature: Float) {
|
||||||
|
try {
|
||||||
|
batteryTemperature = temperature
|
||||||
|
Log.d("ThermalIndicator", "Battery temperature updated: ${batteryTemperature}°C")
|
||||||
|
|
||||||
|
// Update thermal status icon based on temperature
|
||||||
|
thermalStatus = when {
|
||||||
|
batteryTemperature < 20f -> "❄️" // Cold
|
||||||
|
batteryTemperature < 30f -> "🌡️" // Normal
|
||||||
|
batteryTemperature < 40f -> "🔥" // Warm
|
||||||
|
batteryTemperature < 50f -> "🥵" // Hot
|
||||||
|
else -> "☢️" // Critical
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update text color based on temperature
|
||||||
|
val tempColor = when {
|
||||||
|
batteryTemperature < 20f -> Color.parseColor("#87CEEB") // Sky blue
|
||||||
|
batteryTemperature < 30f -> Color.WHITE // White
|
||||||
|
batteryTemperature < 40f -> Color.parseColor("#FFA500") // Orange
|
||||||
|
batteryTemperature < 50f -> Color.parseColor("#FF4500") // Red orange
|
||||||
|
else -> Color.parseColor("#FF0000") // Red
|
||||||
|
}
|
||||||
|
|
||||||
|
textPaint.color = tempColor
|
||||||
|
smallTextPaint.color = tempColor
|
||||||
|
borderPaint.color = tempColor
|
||||||
|
|
||||||
|
// Always invalidate to trigger a redraw
|
||||||
|
invalidate()
|
||||||
|
Log.d("ThermalIndicator", "View invalidated, temperature: ${batteryTemperature}°C, status: $thermalStatus")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// Fallback in case of any errors
|
||||||
|
batteryTemperature = 25f
|
||||||
|
thermalStatus = "🌡️"
|
||||||
|
Log.e("ThermalIndicator", "Error updating temperature", e)
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||||
|
val desiredWidth = 120
|
||||||
|
val desiredHeight = 60
|
||||||
|
|
||||||
|
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
|
||||||
|
val widthSize = MeasureSpec.getSize(widthMeasureSpec)
|
||||||
|
val heightMode = MeasureSpec.getMode(heightMeasureSpec)
|
||||||
|
val heightSize = MeasureSpec.getSize(heightMeasureSpec)
|
||||||
|
|
||||||
|
val width = when (widthMode) {
|
||||||
|
MeasureSpec.EXACTLY -> widthSize
|
||||||
|
MeasureSpec.AT_MOST -> minOf(desiredWidth, widthSize)
|
||||||
|
else -> desiredWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
val height = when (heightMode) {
|
||||||
|
MeasureSpec.EXACTLY -> heightSize
|
||||||
|
MeasureSpec.AT_MOST -> minOf(desiredHeight, heightSize)
|
||||||
|
else -> desiredHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
setMeasuredDimension(width, height)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
|
||||||
|
super.onSizeChanged(w, h, oldw, oldh)
|
||||||
|
backgroundRect.set(4f, 4f, w - 4f, h - 4f)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDraw(canvas: Canvas) {
|
||||||
|
super.onDraw(canvas)
|
||||||
|
|
||||||
|
// Draw background with rounded corners
|
||||||
|
canvas.drawRoundRect(backgroundRect, 12f, 12f, backgroundPaint)
|
||||||
|
canvas.drawRoundRect(backgroundRect, 12f, 12f, borderPaint)
|
||||||
|
|
||||||
|
val centerX = width / 2f
|
||||||
|
val centerY = height / 2f
|
||||||
|
|
||||||
|
// Draw thermal icon on the left
|
||||||
|
canvas.drawText(thermalStatus, 18f, centerY - 8f, iconPaint)
|
||||||
|
|
||||||
|
// Draw temperature in Celsius (main temperature)
|
||||||
|
val celsiusText = "${batteryTemperature.roundToInt()}°C"
|
||||||
|
canvas.drawText(celsiusText, centerX, centerY - 8f, textPaint)
|
||||||
|
|
||||||
|
// Draw temperature in Fahrenheit (smaller, below)
|
||||||
|
val fahrenheit = (batteryTemperature * 9f / 5f + 32f).roundToInt()
|
||||||
|
val fahrenheitText = "${fahrenheit}°F"
|
||||||
|
canvas.drawText(fahrenheitText, centerX, centerY + 12f, smallTextPaint)
|
||||||
|
|
||||||
|
Log.d("ThermalIndicator", "onDraw called - Celsius: $celsiusText, Fahrenheit: $fahrenheitText")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-FileCopyrightText: 2025 citron Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
@@ -69,6 +70,8 @@ struct Values {
|
|||||||
Settings::Category::Overlay};
|
Settings::Category::Overlay};
|
||||||
Settings::Setting<bool> show_thermal_overlay{linkage, false, "show_thermal_overlay",
|
Settings::Setting<bool> show_thermal_overlay{linkage, false, "show_thermal_overlay",
|
||||||
Settings::Category::Overlay};
|
Settings::Category::Overlay};
|
||||||
|
Settings::Setting<bool> show_ram_meter{linkage, false, "show_ram_meter",
|
||||||
|
Settings::Category::Overlay};
|
||||||
Settings::Setting<bool> show_input_overlay{linkage, true, "show_input_overlay",
|
Settings::Setting<bool> show_input_overlay{linkage, true, "show_input_overlay",
|
||||||
Settings::Category::Overlay};
|
Settings::Category::Overlay};
|
||||||
Settings::Setting<bool> touchscreen{linkage, true, "touchscreen", Settings::Category::Overlay};
|
Settings::Setting<bool> touchscreen{linkage, true, "touchscreen", Settings::Category::Overlay};
|
||||||
|
|||||||
@@ -143,31 +143,41 @@
|
|||||||
android:layout_marginHorizontal="20dp"
|
android:layout_marginHorizontal="20dp"
|
||||||
android:fitsSystemWindows="true">
|
android:fitsSystemWindows="true">
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
<LinearLayout
|
||||||
android:id="@+id/show_fps_text"
|
|
||||||
style="@style/TextAppearance.Material3.BodySmall"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="left"
|
android:layout_gravity="center_horizontal|top"
|
||||||
android:clickable="false"
|
android:layout_marginTop="16dp"
|
||||||
android:focusable="false"
|
android:orientation="horizontal"
|
||||||
android:textColor="@android:color/white"
|
android:gravity="center">
|
||||||
android:shadowColor="@android:color/black"
|
|
||||||
android:shadowRadius="3"
|
|
||||||
tools:ignore="RtlHardcoded" />
|
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
<org.citron.citron_emu.views.FpsIndicatorView
|
||||||
android:id="@+id/show_thermals_text"
|
android:id="@+id/fps_indicator_view"
|
||||||
style="@style/TextAppearance.Material3.BodySmall"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="right"
|
android:layout_marginEnd="8dp"
|
||||||
android:clickable="false"
|
android:clickable="false"
|
||||||
android:focusable="false"
|
android:focusable="false"
|
||||||
android:textColor="@android:color/white"
|
android:visibility="gone" />
|
||||||
android:shadowColor="@android:color/black"
|
|
||||||
android:shadowRadius="3"
|
<org.citron.citron_emu.views.ThermalIndicatorView
|
||||||
tools:ignore="RtlHardcoded" />
|
android:id="@+id/thermal_indicator_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:clickable="false"
|
||||||
|
android:focusable="false"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<org.citron.citron_emu.views.RamMeterView
|
||||||
|
android:id="@+id/ram_meter_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:clickable="false"
|
||||||
|
android:focusable="false"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,11 @@
|
|||||||
android:title="@string/emulation_thermal_indicator"
|
android:title="@string/emulation_thermal_indicator"
|
||||||
android:checkable="true" />
|
android:checkable="true" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/ram_meter"
|
||||||
|
android:title="@string/emulation_ram_meter"
|
||||||
|
android:checkable="true" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_edit_overlay"
|
android:id="@+id/menu_edit_overlay"
|
||||||
android:title="@string/emulation_touch_overlay_edit" />
|
android:title="@string/emulation_touch_overlay_edit" />
|
||||||
|
|||||||
@@ -303,4 +303,71 @@
|
|||||||
<item>2</item>
|
<item>2</item>
|
||||||
</integer-array>
|
</integer-array>
|
||||||
|
|
||||||
|
<!-- Zep Zone setting arrays -->
|
||||||
|
<string-array name="memoryLayoutNames">
|
||||||
|
<item>4GB DRAM (Default)</item>
|
||||||
|
<item>6GB DRAM (Unsafe)</item>
|
||||||
|
<item>8GB DRAM (Unsafe)</item>
|
||||||
|
<item>10GB DRAM (Unsafe)</item>
|
||||||
|
<item>12GB DRAM (Unsafe)</item>
|
||||||
|
<item>14GB DRAM (Unsafe)</item>
|
||||||
|
<item>16GB DRAM (Unsafe)</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<integer-array name="memoryLayoutValues">
|
||||||
|
<item>0</item>
|
||||||
|
<item>1</item>
|
||||||
|
<item>2</item>
|
||||||
|
<item>3</item>
|
||||||
|
<item>4</item>
|
||||||
|
<item>5</item>
|
||||||
|
<item>6</item>
|
||||||
|
</integer-array>
|
||||||
|
|
||||||
|
<string-array name="astcDecodeModeNames">
|
||||||
|
<item>CPU</item>
|
||||||
|
<item>GPU</item>
|
||||||
|
<item>CPU Asynchronous</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<integer-array name="astcDecodeModeValues">
|
||||||
|
<item>0</item>
|
||||||
|
<item>1</item>
|
||||||
|
<item>2</item>
|
||||||
|
</integer-array>
|
||||||
|
|
||||||
|
<string-array name="astcRecompressionNames">
|
||||||
|
<item>Uncompressed</item>
|
||||||
|
<item>BC1</item>
|
||||||
|
<item>BC3</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<integer-array name="astcRecompressionValues">
|
||||||
|
<item>0</item>
|
||||||
|
<item>1</item>
|
||||||
|
<item>2</item>
|
||||||
|
</integer-array>
|
||||||
|
|
||||||
|
<string-array name="shaderBackendNames">
|
||||||
|
<item>GLSL</item>
|
||||||
|
<item>GLASM</item>
|
||||||
|
<item>SPIR-V</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<integer-array name="shaderBackendValues">
|
||||||
|
<item>0</item>
|
||||||
|
<item>1</item>
|
||||||
|
<item>2</item>
|
||||||
|
</integer-array>
|
||||||
|
|
||||||
|
<string-array name="vramUsageModeNames">
|
||||||
|
<item>Conservative</item>
|
||||||
|
<item>Aggressive</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<integer-array name="vramUsageModeValues">
|
||||||
|
<item>0</item>
|
||||||
|
<item>1</item>
|
||||||
|
</integer-array>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -410,6 +410,13 @@
|
|||||||
<string name="preferences_theme">Theme and color</string>
|
<string name="preferences_theme">Theme and color</string>
|
||||||
<string name="preferences_debug">Debug</string>
|
<string name="preferences_debug">Debug</string>
|
||||||
<string name="preferences_debug_description">CPU/GPU debugging, graphics API, fastmem</string>
|
<string name="preferences_debug_description">CPU/GPU debugging, graphics API, fastmem</string>
|
||||||
|
<string name="preferences_zep_zone">Zep Zone</string>
|
||||||
|
<string name="preferences_zep_zone_description">Advanced emulation settings</string>
|
||||||
|
|
||||||
|
<!-- Zep Zone Headers -->
|
||||||
|
<string name="memory_layout_header">Memory Layout</string>
|
||||||
|
<string name="astc_settings_header">ASTC Settings</string>
|
||||||
|
<string name="advanced_graphics_header">Advanced Graphics</string>
|
||||||
|
|
||||||
<!-- Game properties -->
|
<!-- Game properties -->
|
||||||
<string name="info">Info</string>
|
<string name="info">Info</string>
|
||||||
@@ -472,7 +479,8 @@
|
|||||||
<string name="emulation_exit">Exit emulation</string>
|
<string name="emulation_exit">Exit emulation</string>
|
||||||
<string name="emulation_done">Done</string>
|
<string name="emulation_done">Done</string>
|
||||||
<string name="emulation_fps_counter">FPS counter</string>
|
<string name="emulation_fps_counter">FPS counter</string>
|
||||||
<string name="emulation_thermal_indicator">Thermal indicator</string>
|
<string name="emulation_thermal_indicator">Battery temperature</string>
|
||||||
|
<string name="emulation_ram_meter">RAM usage meter</string>
|
||||||
<string name="emulation_toggle_controls">Toggle controls</string>
|
<string name="emulation_toggle_controls">Toggle controls</string>
|
||||||
<string name="emulation_rel_stick_center">Relative stick center</string>
|
<string name="emulation_rel_stick_center">Relative stick center</string>
|
||||||
<string name="emulation_dpad_slide">D-pad slide</string>
|
<string name="emulation_dpad_slide">D-pad slide</string>
|
||||||
@@ -1167,4 +1175,16 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
</string>
|
</string>
|
||||||
|
|
||||||
|
<!-- Zep Zone settings strings -->
|
||||||
|
<string name="memory_layout_mode">Memory Layout</string>
|
||||||
|
<string name="memory_layout_mode_description">Increases the amount of emulated RAM from the stock 4GB of the retail Switch to the developer kit\'s 8/6GB. It doesn\'t improve stability or performance and is intended to let big texture mods fit in emulated RAM. Enabling it will increase memory use. It is not recommended to enable unless a specific game with a texture mod needs it.</string>
|
||||||
|
<string name="astc_decode_mode">ASTC Decoding Method</string>
|
||||||
|
<string name="astc_decode_mode_description">Controls how ASTC textures are decoded. GPU decoding is faster but may cause issues on some devices.</string>
|
||||||
|
<string name="astc_recompression">ASTC Recompression Method</string>
|
||||||
|
<string name="astc_recompression_description">Controls how ASTC textures are recompressed when GPU doesn\'t support them natively.</string>
|
||||||
|
<string name="shader_backend">Shader Backend</string>
|
||||||
|
<string name="shader_backend_description">Controls which shader backend to use for rendering.</string>
|
||||||
|
<string name="vram_usage_mode">VRAM Usage Mode</string>
|
||||||
|
<string name="vram_usage_mode_description">Controls how aggressively VRAM is used. Conservative mode limits VRAM usage for better stability.</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
|
|
||||||
namespace Service::Sockets {
|
namespace Service::Sockets {
|
||||||
|
|
||||||
constexpr Result ResultNotImplemented{ErrorModule::NSD, 1}; // Generic "not implemented"
|
[[maybe_unused]] constexpr Result ResultNotImplemented{ErrorModule::NSD, 1}; // Generic "not implemented"
|
||||||
constexpr Result ResultNsdNotInitialized{ErrorModule::NSD, 2}; // Example, if needed
|
[[maybe_unused]] constexpr Result ResultNsdNotInitialized{ErrorModule::NSD, 2}; // Example, if needed
|
||||||
constexpr Result ResultPermissionDenied{ErrorModule::NSD, 3}; // For nsd:a specific calls
|
constexpr Result ResultPermissionDenied{ErrorModule::NSD, 3}; // For nsd:a specific calls
|
||||||
constexpr Result ResultOverflow{ErrorModule::NSD, 6};
|
constexpr Result ResultOverflow{ErrorModule::NSD, 6};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user