Merge branch 'pr-25' into purple

This commit is contained in:
MrPurple666 2025-04-14 04:29:24 -03:00
commit a98ac705e4
15 changed files with 464 additions and 149 deletions

View file

@ -31,9 +31,16 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
SHOW_THERMAL_OVERLAY("show_thermal_overlay"), SHOW_THERMAL_OVERLAY("show_thermal_overlay"),
FRAME_INTERPOLATION("frame_interpolation"), FRAME_INTERPOLATION("frame_interpolation"),
FRAME_SKIPPING("frame_skipping"); FRAME_SKIPPING("frame_skipping");
external fun isFrameSkippingEnabled(): Boolean external fun isFrameSkippingEnabled(): Boolean
external fun isFrameInterpolationEnabled(): Boolean external fun isFrameInterpolationEnabled(): Boolean
SHOW_FPS("show_fps"),
SHOW_FRAMETIME("show_frame_time"),
SHOW_SPEED("show_speed"),
SHOW_APP_RAM_USAGE("show_app_ram_usage"),
SHOW_SYSTEM_RAM_USAGE("show_system_ram_usage"),
SHOW_BAT_TEMPERATURE("show_bat_temperature"),
OVERLAY_BACKGROUND("overlay_background"),;
override fun getBoolean(needsGlobal: Boolean): Boolean = override fun getBoolean(needsGlobal: Boolean): Boolean =
NativeConfig.getBoolean(key, needsGlobal) NativeConfig.getBoolean(key, needsGlobal)

View file

@ -32,6 +32,7 @@ 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"),
PERF_OVERLAY_POSITION("perf_overlay_position"),
FSR_SHARPENING_SLIDER("fsr_sharpening_slider"); FSR_SHARPENING_SLIDER("fsr_sharpening_slider");
override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal) override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal)

View file

@ -12,6 +12,7 @@ object Settings {
SECTION_ROOT(R.string.advanced_settings), SECTION_ROOT(R.string.advanced_settings),
SECTION_SYSTEM(R.string.preferences_system), SECTION_SYSTEM(R.string.preferences_system),
SECTION_RENDERER(R.string.preferences_graphics), SECTION_RENDERER(R.string.preferences_graphics),
SECTION_PERFORMANCE_STATS(R.string.show_stats_overlay),
SECTION_AUDIO(R.string.preferences_audio), SECTION_AUDIO(R.string.preferences_audio),
SECTION_INPUT(R.string.preferences_controls), SECTION_INPUT(R.string.preferences_controls),
SECTION_INPUT_PLAYER_ONE, SECTION_INPUT_PLAYER_ONE,
@ -33,6 +34,7 @@ object Settings {
const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch" const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
const val PREF_SHOULD_SHOW_PRE_ALPHA_WARNING = "ShouldShowPreAlphaWarning" const val PREF_SHOULD_SHOW_PRE_ALPHA_WARNING = "ShouldShowPreAlphaWarning"
const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown" const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown"
const val SECTION_STATS_OVERLAY = "Stats Overlay"
// Deprecated input overlay preference keys // Deprecated input overlay preference keys
const val PREF_CONTROL_SCALE = "controlScale" const val PREF_CONTROL_SCALE = "controlScale"

View file

@ -337,6 +337,71 @@ abstract class SettingsItem(
valuesId = R.array.rendererResolutionValues valuesId = R.array.rendererResolutionValues
) )
) )
put(
SwitchSetting(
BooleanSetting.SHOW_PERFORMANCE_OVERLAY,
R.string.enable_stats_overlay_,
descriptionId = R.string.stats_overlay_options_description
)
)
put(
SwitchSetting(
BooleanSetting.OVERLAY_BACKGROUND,
R.string.overlay_background,
descriptionId = R.string.overlay_background_description
)
)
put(
SingleChoiceSetting(
IntSetting.PERF_OVERLAY_POSITION,
titleId = R.string.overlay_position,
descriptionId = R.string.overlay_position_description,
choicesId = R.array.statsPosition,
valuesId = R.array.staticThemeValues
)
)
put(
SwitchSetting(
BooleanSetting.SHOW_FPS,
R.string.show_fps,
descriptionId = R.string.show_fps_description
)
)
put(
SwitchSetting(
BooleanSetting.SHOW_FRAMETIME,
R.string.show_frametime,
descriptionId = R.string.show_frametime_description
)
)
put(
SwitchSetting(
BooleanSetting.SHOW_SPEED,
R.string.show_speed,
descriptionId = R.string.show_speed_description
)
)
put(
SwitchSetting(
BooleanSetting.SHOW_APP_RAM_USAGE,
R.string.show_app_ram_usage,
descriptionId = R.string.show_app_ram_usage_description
)
)
put(
SwitchSetting(
BooleanSetting.SHOW_SYSTEM_RAM_USAGE,
R.string.show_system_ram_usage,
descriptionId = R.string.show_system_ram_usage_description
)
)
put(
SwitchSetting(
BooleanSetting.SHOW_BAT_TEMPERATURE,
R.string.show_bat_temperature,
descriptionId = R.string.show_bat_temperature_description
)
)
put( put(
SingleChoiceSetting( SingleChoiceSetting(
IntSetting.RENDERER_VSYNC, IntSetting.RENDERER_VSYNC,

View file

@ -88,6 +88,7 @@ class SettingsFragmentPresenter(
MenuTag.SECTION_ROOT -> addConfigSettings(sl) MenuTag.SECTION_ROOT -> addConfigSettings(sl)
MenuTag.SECTION_SYSTEM -> addSystemSettings(sl) MenuTag.SECTION_SYSTEM -> addSystemSettings(sl)
MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl) MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl)
MenuTag.SECTION_PERFORMANCE_STATS -> addPerfomanceOverlaySettings(sl)
MenuTag.SECTION_AUDIO -> addAudioSettings(sl) MenuTag.SECTION_AUDIO -> addAudioSettings(sl)
MenuTag.SECTION_INPUT -> addInputSettings(sl) MenuTag.SECTION_INPUT -> addInputSettings(sl)
MenuTag.SECTION_INPUT_PLAYER_ONE -> addInputPlayer(sl, 0) MenuTag.SECTION_INPUT_PLAYER_ONE -> addInputPlayer(sl, 0)
@ -128,6 +129,15 @@ class SettingsFragmentPresenter(
menuKey = MenuTag.SECTION_RENDERER menuKey = MenuTag.SECTION_RENDERER
) )
) )
if (!NativeConfig.isPerGameConfigLoaded())
add(
SubmenuSetting(
titleId = R.string.stats_overlay_options,
descriptionId = R.string.stats_overlay_options_description,
iconId = R.drawable.ic_frames,
menuKey = MenuTag.SECTION_PERFORMANCE_STATS
)
)
add( add(
SubmenuSetting( SubmenuSetting(
titleId = R.string.preferences_audio, titleId = R.string.preferences_audio,
@ -277,6 +287,23 @@ class SettingsFragmentPresenter(
} }
} }
private fun addPerfomanceOverlaySettings(sl: ArrayList<SettingsItem>) {
sl.apply {
add(HeaderSetting(R.string.stats_overlay_customization))
add(BooleanSetting.SHOW_PERFORMANCE_OVERLAY.key)
add(BooleanSetting.OVERLAY_BACKGROUND.key)
add(IntSetting.PERF_OVERLAY_POSITION.key)
add(HeaderSetting(R.string.stats_overlay_items))
add(BooleanSetting.SHOW_FPS.key)
add(BooleanSetting.SHOW_FRAMETIME.key)
add(BooleanSetting.SHOW_SPEED.key)
add(BooleanSetting.SHOW_APP_RAM_USAGE.key)
add(BooleanSetting.SHOW_SYSTEM_RAM_USAGE.key)
add(BooleanSetting.SHOW_BAT_TEMPERATURE.key)
}
}
private fun addAudioSettings(sl: ArrayList<SettingsItem>) { private fun addAudioSettings(sl: ArrayList<SettingsItem>) {
sl.apply { sl.apply {
add(IntSetting.AUDIO_OUTPUT_ENGINE.key) add(IntSetting.AUDIO_OUTPUT_ENGINE.key)

View file

@ -4,18 +4,29 @@
package org.yuzu.yuzu_emu.fragments package org.yuzu.yuzu_emu.fragments
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.ActivityManager
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.graphics.Color
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
import android.os.SystemClock import android.os.SystemClock
import android.util.Rational import android.util.Rational
import android.view.* import android.view.Gravity
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.Surface
import android.view.SurfaceHolder
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout import android.widget.FrameLayout
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
@ -26,7 +37,6 @@ import androidx.core.graphics.Insets
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
import androidx.drawerlayout.widget.DrawerLayout import androidx.drawerlayout.widget.DrawerLayout
import androidx.drawerlayout.widget.DrawerLayout.DrawerListener import androidx.drawerlayout.widget.DrawerLayout.DrawerListener
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
@ -36,6 +46,7 @@ import androidx.navigation.fragment.navArgs
import androidx.window.layout.FoldingFeature import androidx.window.layout.FoldingFeature
import androidx.window.layout.WindowInfoTracker import androidx.window.layout.WindowInfoTracker
import androidx.window.layout.WindowLayoutInfo import androidx.window.layout.WindowLayoutInfo
import com.google.android.material.color.MaterialColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.slider.Slider import com.google.android.material.slider.Slider
import org.yuzu.yuzu_emu.HomeNavigationDirections import org.yuzu.yuzu_emu.HomeNavigationDirections
@ -51,28 +62,28 @@ import org.yuzu.yuzu_emu.features.settings.model.Settings.EmulationOrientation
import org.yuzu.yuzu_emu.features.settings.model.Settings.EmulationVerticalAlignment import org.yuzu.yuzu_emu.features.settings.model.Settings.EmulationVerticalAlignment
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.model.DriverViewModel import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.model.EmulationViewModel import org.yuzu.yuzu_emu.model.EmulationViewModel
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.overlay.model.OverlayControl import org.yuzu.yuzu_emu.overlay.model.OverlayControl
import org.yuzu.yuzu_emu.overlay.model.OverlayLayout import org.yuzu.yuzu_emu.overlay.model.OverlayLayout
import org.yuzu.yuzu_emu.utils.* import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GameHelper
import org.yuzu.yuzu_emu.utils.GameIconUtils
import org.yuzu.yuzu_emu.utils.Log
import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.ViewUtils
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import java.lang.NullPointerException import org.yuzu.yuzu_emu.utils.collect
import android.content.BroadcastReceiver import java.io.File
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.util.TypedValue
import android.app.ActivityManager
import android.graphics.Color
import android.os.Debug
class EmulationFragment : Fragment(), SurfaceHolder.Callback { class EmulationFragment : Fragment(), SurfaceHolder.Callback {
private lateinit var emulationState: EmulationState private lateinit var emulationState: EmulationState
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 lateinit var cpuBackend: String
private var batteryReceiverRegistered: Boolean = false private lateinit var gpuDriver: String
private var _binding: FragmentEmulationBinding? = null private var _binding: FragmentEmulationBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
@ -198,8 +209,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
}) })
binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
binding.inGameMenu.getHeaderView(0).findViewById<TextView>(R.id.text_game_title).text = binding.inGameMenu.getHeaderView(0).apply {
game.title val titleView = findViewById<TextView>(R.id.text_game_title)
titleView.text = game.title
}
binding.inGameMenu.menu.findItem(R.id.menu_lock_drawer).apply { binding.inGameMenu.menu.findItem(R.id.menu_lock_drawer).apply {
val lockMode = IntSetting.LOCK_DRAWER.getInt() val lockMode = IntSetting.LOCK_DRAWER.getInt()
@ -375,9 +388,23 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
emulationState.updateSurface() emulationState.updateSurface()
// Setup overlays // Setup overlays
updateShowFpsOverlay() updateshowStatsOvelray()
val temperature = getBatteryTemperature(requireContext())
updateThermalOverlay(temperature) // Re update binding when the specs values get initialized properly
binding.inGameMenu.getHeaderView(0).apply {
val titleView = findViewById<TextView>(R.id.text_game_title)
val cpuBackendLabel = findViewById<TextView>(R.id.cpu_backend)
val gpuvendorLabel = findViewById<TextView>(R.id.gpu_vendor)
titleView.text = game.title
cpuBackendLabel.text = NativeLibrary.getCpuBackend()
gpuvendorLabel.text = NativeLibrary.getGpuDriver()
}
val position = IntSetting.PERF_OVERLAY_POSITION.getInt()
updateStatsPosition(position)
} }
} }
emulationViewModel.isEmulationStopping.collect(viewLifecycleOwner) { emulationViewModel.isEmulationStopping.collect(viewLifecycleOwner) {
@ -385,7 +412,7 @@ 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.showStatsOverlayText)
} }
} }
emulationViewModel.drawerOpen.collect(viewLifecycleOwner) { emulationViewModel.drawerOpen.collect(viewLifecycleOwner) {
@ -466,23 +493,11 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
override fun onPause() { override fun onPause() {
if (emulationState.isRunning && emulationActivity?.isInPictureInPictureMode != true) { if (emulationState.isRunning && emulationActivity?.isInPictureInPictureMode != true) {
emulationState.pause() emulationState.pause()
}
context?.let {
if (batteryReceiverRegistered) {
it.unregisterReceiver(batteryReceiver)
batteryReceiverRegistered = false
}
} }
super.onPause() super.onPause()
} }
override fun onDestroyView() { override fun onDestroyView() {
context?.let {
if (batteryReceiverRegistered) {
it.unregisterReceiver(batteryReceiver)
batteryReceiverRegistered = false
}
}
super.onDestroyView() super.onDestroyView()
_binding = null _binding = null
} }
@ -493,11 +508,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
if (!batteryReceiverRegistered) { // If the overlay is enabled, we need to update the position if changed
val filter = IntentFilter(Intent.ACTION_BATTERY_CHANGED) val position = IntSetting.PERF_OVERLAY_POSITION.getInt()
context?.registerReceiver(batteryReceiver, filter) updateStatsPosition(position)
batteryReceiverRegistered = true
}
} }
private fun resetInputOverlay() { private fun resetInputOverlay() {
@ -508,48 +521,103 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
} }
@SuppressLint("DefaultLocale") @SuppressLint("DefaultLocale")
private fun updateShowFpsOverlay() { private fun updateshowStatsOvelray() {
val showOverlay = BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean() val showOverlay = BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()
binding.showFpsText.setTextColor(Color.parseColor("#A146FF")) binding.showStatsOverlayText.apply {
binding.showFpsText.setVisible(showOverlay) setTextColor(
MaterialColors.getColor(
this,
com.google.android.material.R.attr.colorPrimary
)
)
}
binding.showStatsOverlayText.setVisible(showOverlay)
if (showOverlay) { if (showOverlay) {
val SYSTEM_FPS = 0 val SYSTEM_FPS = 0
val FPS = 1 val FPS = 1
val FRAMETIME = 2 val FRAMETIME = 2
val SPEED = 3 val SPEED = 3
val sb = StringBuilder()
perfStatsUpdater = { perfStatsUpdater = {
if (emulationViewModel.emulationStarted.value && if (emulationViewModel.emulationStarted.value &&
!emulationViewModel.isEmulationStopping.value !emulationViewModel.isEmulationStopping.value
) { ) {
sb.setLength(0)
val perfStats = NativeLibrary.getPerfStats() val perfStats = NativeLibrary.getPerfStats()
val cpuBackend = NativeLibrary.getCpuBackend()
val gpuDriver = NativeLibrary.getGpuDriver()
// Get memory info
val mi = ActivityManager.MemoryInfo()
val activityManager =
requireContext().getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
activityManager.getMemoryInfo(mi)
// Calculate used memory
val usedMegs = (mi.totalMem - mi.availMem) / 1048576L // Convert bytes to megabytes
val actualFps = perfStats[FPS] val actualFps = perfStats[FPS]
if (BooleanSetting.SHOW_FPS.getBoolean(NativeConfig.isPerGameConfigLoaded())) {
val enableFrameInterpolation = BooleanSetting.FRAME_INTERPOLATION.getBoolean() val enableFrameInterpolation = BooleanSetting.FRAME_INTERPOLATION.getBoolean()
val generatedFpsText = if (enableFrameInterpolation) { val enableFrameSkipping = BooleanSetting.FRAME_SKIPPING.getBoolean()
val generatedFps = actualFps * 2
String.format("(Generated: %.1f)", generatedFps) var fpsText = String.format("FPS: %.1f", actualFps)
} else {
"" if (enableFrameInterpolation) {
val interpolatedFps = actualFps * 2
fpsText += String.format(" (Interp: %.1f)", interpolatedFps)
} }
if (_binding != null) {
binding.showFpsText.text = String.format( if (enableFrameSkipping) {
"%.1f %s FPS • %d MB • %s/%s", fpsText += " [Skipping]"
actualFps, generatedFpsText, usedMegs, cpuBackend, gpuDriver }
sb.append(fpsText)
}
if (BooleanSetting.SHOW_FRAMETIME.getBoolean(NativeConfig.isPerGameConfigLoaded())) {
if (sb.isNotEmpty()) sb.append(" | ")
sb.append(
String.format(
"FT: %.1fms",
(perfStats[FRAMETIME] * 1000.0f).toFloat()
)
) )
} }
perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 800)
if (BooleanSetting.SHOW_SPEED.getBoolean(NativeConfig.isPerGameConfigLoaded())) {
if (sb.isNotEmpty()) sb.append(" | ")
sb.append(
String.format(
"Speed: %d%%",
(perfStats[SPEED] * 100.0 + 0.5).toInt()
)
)
} }
if (BooleanSetting.SHOW_APP_RAM_USAGE.getBoolean(NativeConfig.isPerGameConfigLoaded())) {
if (sb.isNotEmpty()) sb.append(" | ")
val appRamUsage = File("/proc/self/statm").readLines()[0].split(' ')[1].toLong() * 4096 / 1000000
sb.append("Process RAM: $appRamUsage MB")
}
if (BooleanSetting.SHOW_SYSTEM_RAM_USAGE.getBoolean(NativeConfig.isPerGameConfigLoaded())) {
if (sb.isNotEmpty()) sb.append(" | ")
context?.let { ctx ->
val activityManager = ctx.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val memInfo = ActivityManager.MemoryInfo()
activityManager.getMemoryInfo(memInfo)
val usedRamMB = (memInfo.totalMem - memInfo.availMem) / 1048576L
sb.append("RAM: $usedRamMB MB")
}
}
if (BooleanSetting.SHOW_BAT_TEMPERATURE.getBoolean(NativeConfig.isPerGameConfigLoaded())) {
if (sb.isNotEmpty()) sb.append(" | ")
val batteryTemp = getBatteryTemperature()
val tempF = celsiusToFahrenheit(batteryTemp)
sb.append(String.format("%.1f°C/%.1f°F", batteryTemp, tempF))
}
if (BooleanSetting.OVERLAY_BACKGROUND.getBoolean(NativeConfig.isPerGameConfigLoaded())) {
binding.showStatsOverlayText.setBackgroundResource(R.color.yuzu_transparent_black)
} else {
binding.showStatsOverlayText.setBackgroundResource(0)
}
binding.showStatsOverlayText.text = sb.toString()
}
perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 800)
} }
perfStatsUpdateHandler.post(perfStatsUpdater!!) perfStatsUpdateHandler.post(perfStatsUpdater!!)
} else { } else {
@ -559,18 +627,55 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
} }
private val batteryReceiver = object : BroadcastReceiver() { private fun updateStatsPosition(position: Int) {
override fun onReceive(context: Context?, intent: Intent?) { val params = binding.showStatsOverlayText.layoutParams as FrameLayout.LayoutParams
intent?.let { when (position) {
if (it.action == Intent.ACTION_BATTERY_CHANGED) { 0 -> {
val temperature = getBatteryTemperature(context!!) params.gravity = (Gravity.TOP or Gravity.START)
updateThermalOverlay(temperature) params.setMargins(resources.getDimensionPixelSize(R.dimen.spacing_large), 0, 0, 0)
} }
}
}
}
private fun updateThermalOverlay(temperature: Float) { 1 -> {
params.gravity = (Gravity.TOP or Gravity.CENTER_HORIZONTAL)
}
2 -> {
params.gravity = (Gravity.TOP or Gravity.END)
params.setMargins(0, 0, resources.getDimensionPixelSize(R.dimen.spacing_large), 0)
}
3 -> {
params.gravity = (Gravity.BOTTOM or Gravity.START)
params.setMargins(resources.getDimensionPixelSize(R.dimen.spacing_large), 0, 0, 0)
}
4 -> {
params.gravity = (Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL)
}
5 -> {
params.gravity = (Gravity.BOTTOM or Gravity.END)
params.setMargins(0, 0, resources.getDimensionPixelSize(R.dimen.spacing_large), 0)
}
}
}
private fun getBatteryTemperature(): Float {
try {
val batteryIntent = requireContext().registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
// Temperature in tenths of a degree Celsius
val temperature = batteryIntent?.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0) ?: 0
// Convert to degrees Celsius
return temperature / 10.0f
} catch (e: Exception) {
return 0.0f
}
}
private fun celsiusToFahrenheit(celsius: Float): Float {
return (celsius * 9 / 5) + 32
}
private fun updateThermalOverlay(temperature: Float) {
if (BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean() && if (BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean() &&
emulationViewModel.emulationStarted.value && emulationViewModel.emulationStarted.value &&
!emulationViewModel.isEmulationStopping.value !emulationViewModel.isEmulationStopping.value
@ -589,17 +694,9 @@ private fun updateThermalOverlay(temperature: Float) {
binding.showThermalsText.setTextColor(color) binding.showThermalsText.setTextColor(color)
binding.showThermalsText.text = String.format("%.1f°C • %.1f°F", temperature, fahrenheit) binding.showThermalsText.text = String.format("%.1f°C • %.1f°F", temperature, fahrenheit)
} }
}
private fun getBatteryTemperature(context: Context): Float {
val intent: Intent? = context.registerReceiver(
null,
IntentFilter(Intent.ACTION_BATTERY_CHANGED)
)
val temperature = intent?.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0) ?: 0
return temperature / 10.0f
} }
@SuppressLint("SourceLockedOrientationActivity") @SuppressLint("SourceLockedOrientationActivity")
private fun updateOrientation() { private fun updateOrientation() {
emulationActivity?.let { emulationActivity?.let {
@ -725,10 +822,8 @@ private fun getBatteryTemperature(context: Context): Float {
popup.menuInflater.inflate(R.menu.menu_overlay_options, popup.menu) popup.menuInflater.inflate(R.menu.menu_overlay_options, popup.menu)
popup.menu.apply { popup.menu.apply {
findItem(R.id.menu_toggle_fps).isChecked = findItem(R.id.menu_show_stats_overlay).isChecked =
BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean() BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()
findItem(R.id.thermal_indicator).isChecked =
BooleanSetting.SHOW_THERMAL_OVERLAY.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()
@ -741,34 +836,12 @@ private fun getBatteryTemperature(context: Context): Float {
popup.setOnDismissListener { NativeConfig.saveGlobalConfig() } popup.setOnDismissListener { NativeConfig.saveGlobalConfig() }
popup.setOnMenuItemClickListener { popup.setOnMenuItemClickListener {
when (it.itemId) { when (it.itemId) {
R.id.menu_toggle_fps -> { R.id.menu_show_stats_overlay -> {
it.isChecked = !it.isChecked it.isChecked = !it.isChecked
BooleanSetting.SHOW_PERFORMANCE_OVERLAY.setBoolean(it.isChecked) BooleanSetting.SHOW_PERFORMANCE_OVERLAY.setBoolean(it.isChecked)
updateShowFpsOverlay() updateshowStatsOvelray()
true true
} }
R.id.thermal_indicator -> {
it.isChecked = !it.isChecked
BooleanSetting.SHOW_THERMAL_OVERLAY.setBoolean(it.isChecked)
if (it.isChecked) {
val temperature = getBatteryTemperature(requireContext())
updateThermalOverlay(temperature)
if (!batteryReceiverRegistered) {
val filter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
context?.registerReceiver(batteryReceiver, filter)
batteryReceiverRegistered = true
}
} else {
if (batteryReceiverRegistered) {
context?.unregisterReceiver(batteryReceiver)
batteryReceiverRegistered = false
}
binding.showThermalsText.text = ""
}
true
}
R.id.menu_edit_overlay -> { R.id.menu_edit_overlay -> {
binding.drawerLayout.close() binding.drawerLayout.close()
binding.surfaceInputOverlay.requestFocus() binding.surfaceInputOverlay.requestFocus()
@ -959,7 +1032,8 @@ private fun getBatteryTemperature(context: Context): Float {
right = cutInsets.right right = cutInsets.right
} }
v.updatePadding(left = left, top = cutInsets.top, right = right) v.setPadding(left, cutInsets.top, right, 0)
windowInsets windowInsets
} }
} }

View file

@ -66,9 +66,23 @@ struct Values {
Settings::Setting<bool> haptic_feedback{linkage, true, "haptic_feedback", Settings::Setting<bool> haptic_feedback{linkage, true, "haptic_feedback",
Settings::Category::Overlay}; Settings::Category::Overlay};
Settings::Setting<bool> show_performance_overlay{linkage, true, "show_performance_overlay", Settings::Setting<bool> show_performance_overlay{linkage, true, "show_performance_overlay",
Settings::Category::Overlay}; Settings::Category::Overlay, Settings::Specialization::Paired, true , true};
Settings::Setting<bool> show_thermal_overlay{linkage, false, "show_thermal_overlay", Settings::Setting<bool> overlay_background{linkage, false, "overlay_background",
Settings::Category::Overlay}; Settings::Category::Overlay, Settings::Specialization::Default, true , true, &show_performance_overlay};
Settings::Setting<s32> perf_overlay_position{linkage, 0, "perf_overlay_position",
Settings::Category::Overlay, Settings::Specialization::Default, true , true, &show_performance_overlay};
Settings::Setting<bool> show_fps{linkage, true, "show_fps",
Settings::Category::Overlay, Settings::Specialization::Default, true , true, &show_performance_overlay};
Settings::Setting<bool> show_frame_time{linkage, false, "show_frame_time",
Settings::Category::Overlay, Settings::Specialization::Default, true , true, &show_performance_overlay};
Settings::Setting<bool> show_speed{linkage, true, "show_speed",
Settings::Category::Overlay, Settings::Specialization::Default, true , true, &show_performance_overlay};
Settings::Setting<bool> show_app_ram_usage{linkage, false, "show_app_ram_usage",
Settings::Category::Overlay, Settings::Specialization::Default, true , true, &show_performance_overlay};
Settings::Setting<bool> show_system_ram_usage{linkage, false, "show_system_ram_usage",
Settings::Category::Overlay, Settings::Specialization::Default, true , true, &show_performance_overlay};
Settings::Setting<bool> show_bat_temperature{linkage, false, "show_bat_temperature",
Settings::Category::Overlay, Settings::Specialization::Default, true , true, &show_performance_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};

View file

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#00000000"
android:strokeColor="#FF000000"
android:strokeWidth="1"
android:pathData="M4,4 L4,20 L20,20" />
<path
android:fillColor="#00000000"
android:strokeColor="#FF000000"
android:strokeWidth="2"
android:pathData="M4,16 L8,12 L12,14 L16,8 L20,10" />
<path
android:fillColor="#FF000000"
android:pathData="M4,16 C3.45,16 3,15.55 3,15 C3,14.45 3.45,14 4,14 C4.55,14 5,14.45 5,15 C5,15.55 4.55,16 4,16" />
<path
android:fillColor="#FF000000"
android:pathData="M8,12 C7.45,12 7,11.55 7,11 C7,10.45 7.45,10 8,10 C8.55,10 9,10.45 9,11 C9,11.55 8.55,12 8,12" />
<path
android:fillColor="#FF000000"
android:pathData="M12,14 C11.45,14 11,13.55 11,13 C11,12.45 11.45,12 12,12 C12.55,12 13,12.45 13,13 C13,13.55 12.55,14 12,14" />
<path
android:fillColor="#FF000000"
android:pathData="M16,8 C15.45,8 15,7.55 15,7 C15,6.45 15.45,6 16,6 C16.55,6 17,6.45 17,7 C17,7.55 16.55,8 16,8" />
<path
android:fillColor="#FF000000"
android:pathData="M20,10 C19.45,10 19,9.55 19,9 C19,8.45 19.45,8 20,8 C20.55,8 21,8.45 21,9 C21,9.55 20.55,10 20,10" />
</vector>

View file

@ -140,15 +140,13 @@
android:id="@+id/overlay_container" android:id="@+id/overlay_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginHorizontal="20dp" android:fitsSystemWindows="false">
android:fitsSystemWindows="true">
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView
android:id="@+id/show_fps_text" android:id="@+id/show_stats_overlay_text"
style="@style/TextAppearance.Material3.BodySmall" 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:clickable="false" android:clickable="false"
android:focusable="false" android:focusable="false"
android:textColor="@android:color/white" android:textColor="@android:color/white"

View file

@ -1,14 +1,64 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.textview.MaterialTextView <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginTop="24dp"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/text_game_title" android:id="@+id/text_game_title"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
android:textAppearance="?attr/textAppearanceHeadlineMedium" android:textAppearance="?attr/textAppearanceHeadlineMedium"
android:textColor="?attr/colorOnSurface" android:textColor="?attr/colorOnSurface"
android:textAlignment="viewStart" android:textAlignment="viewStart"
tools:text="Super Mario Odyssey" /> android:layout_marginBottom="8dp"
tools:text="text_game_title" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.google.android.material.textview.MaterialTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?attr/textAppearanceBodyMedium"
android:textColor="?attr/colorOnSurfaceVariant"
android:textAlignment="viewStart"
android:layout_marginEnd="4dp"
android:text="System Info:" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/cpu_backend"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?attr/textAppearanceBodyMedium"
android:textColor="?attr/colorOnSurfaceVariant"
android:textAlignment="viewStart"
tools:text="cpu_backend" />
<com.google.android.material.textview.MaterialTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?attr/textAppearanceBodyMedium"
android:textColor="?attr/colorOnSurfaceVariant"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:text="|" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/gpu_vendor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?attr/textAppearanceBodyMedium"
android:textColor="?attr/colorOnSurfaceVariant"
android:textAlignment="viewStart"
tools:text="gpu_vendor" />
</LinearLayout>
</LinearLayout>

View file

@ -2,13 +2,8 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <menu xmlns:android="http://schemas.android.com/apk/res/android">
<item <item
android:id="@+id/menu_toggle_fps" android:id="@+id/menu_show_stats_overlay"
android:title="@string/emulation_fps_counter" android:title="@string/show_stats_overlay"
android:checkable="true" />
<item
android:id="@+id/thermal_indicator"
android:title="@string/emulation_thermal_indicator"
android:checkable="true" /> android:checkable="true" />
<item <item

View file

@ -227,6 +227,7 @@
<color name="yuzu_surfaceTint_gray">#B7B7B7</color> <color name="yuzu_surfaceTint_gray">#B7B7B7</color>
<!-- Common Colors Across All Themes --> <!-- Common Colors Across All Themes -->
<color name="yuzu_transparent_black">#80000000</color>
<color name="yuzu_outlineVariant">#C6C5D0</color> <color name="yuzu_outlineVariant">#C6C5D0</color>
<color name="yuzu_error">#FFB4AB</color> <color name="yuzu_error">#FFB4AB</color>
<color name="yuzu_errorContainer">#93000A</color> <color name="yuzu_errorContainer">#93000A</color>

View file

@ -249,6 +249,23 @@
<item>2</item> <item>2</item>
</integer-array> </integer-array>
<string-array name="statsPosition">
<item>@string/overlay_position_top_left</item>
<item>@string/overlay_position_center_top</item>
<item>@string/overlay_position_top_right</item>
<item>@string/overlay_position_bottom_left</item>
<item>@string/overlay_position_center_bottom</item>
<item>@string/overlay_position_bottom_right</item>
</string-array>
<integer-array name="statsPositionValues">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
</integer-array>
<string-array name="cpuBackendArm64Names"> <string-array name="cpuBackendArm64Names">
<item>@string/cpu_backend_dynarmic</item> <item>@string/cpu_backend_dynarmic</item>
<item>@string/cpu_backend_nce</item> <item>@string/cpu_backend_nce</item>

View file

@ -9,6 +9,37 @@
<string name="notice_notification_channel_description">Shows notifications when something goes wrong.</string> <string name="notice_notification_channel_description">Shows notifications when something goes wrong.</string>
<string name="notification_permission_not_granted">Notification permission not granted!</string> <string name="notification_permission_not_granted">Notification permission not granted!</string>
<!-- Stats Overlay settings -->
<string name="show_stats_overlay">ShoW Performance Stats Overlay</string>
<string name="stats_overlay_customization">Customization</string>
<string name="stats_overlay_items">Visibility</string>
<string name="stats_overlay_options">Overlay</string>
<string name="enable_stats_overlay_">Enable Performance Stats Overlay</string>
<string name="stats_overlay_options_description">Configure what information is shown in the performance stats overlay</string>
<string name="show_fps">Show FPS</string>
<string name="show_fps_description">Display current frames per second</string>
<string name="show_frametime">Show Frametime</string>
<string name="show_frametime_description">Display current frametime</string>
<string name="show_speed">Show Speed</string>
<string name="show_speed_description">Display current emulation speed percentage</string>
<string name="show_app_ram_usage">Show App Memory Usage</string>
<string name="show_app_ram_usage_description">Display the amount of RAM getting used by the emulator</string>
<string name="show_system_ram_usage">Show System Memory Usage</string>
<string name="show_system_ram_usage_description">Display the amount of RAM getting used by the system</string>
<string name="show_bat_temperature">Show Battery Temperature</string>
<string name="show_bat_temperature_description">Display current Battery temperature in Celsius and Fahrenheit</string>
<string name="overlay_position">Overlay Position</string>
<string name="overlay_position_description">Choose where the performance stats overlay is displayed on the screen</string>
<string name="overlay_position_top_left">Top Left</string>
<string name="overlay_position_top_right">Top Right</string>
<string name="overlay_position_bottom_left">Bottom Left</string>
<string name="overlay_position_bottom_right">Bottom Right</string>
<string name="overlay_position_center_top">Center Top</string>
<string name="overlay_position_center_bottom">Center Bottom</string>
<string name="overlay_background">Overlay Background</string>
<string name="overlay_background_description">Adds a background behind the overlay for easier reading</string>
<!-- Setup strings --> <!-- Setup strings -->
<string name="welcome">Welcome!</string> <string name="welcome">Welcome!</string>
<string name="welcome_description">Learn how to setup &lt;b>eden&lt;/b> and jump into emulation.</string> <string name="welcome_description">Learn how to setup &lt;b>eden&lt;/b> and jump into emulation.</string>

View file

@ -229,6 +229,7 @@
<color name="yuzu_onErrorContainer">#410002</color> <color name="yuzu_onErrorContainer">#410002</color>
<color name="yuzu_shadow">#000000</color> <color name="yuzu_shadow">#000000</color>
<color name="yuzu_scrim">#000000</color> <color name="yuzu_scrim">#000000</color>
<color name="yuzu_transparent_black">#80000000</color>
<!-- Values used in dark mode but here are jsut white / black values--> <!-- Values used in dark mode but here are jsut white / black values-->
<color name="yuzu_onPrimary_blue">#FFFFFF</color> <color name="yuzu_onPrimary_blue">#FFFFFF</color>
<color name="yuzu_onSecondary_blue">#FFFFFF</color> <color name="yuzu_onSecondary_blue">#FFFFFF</color>