727 lines
42 KiB
C++
727 lines
42 KiB
C++
class MainMenu;
|
|
|
|
class FullOverlay : public tsl::Gui {
|
|
private:
|
|
char DeltaCPU_c[12] = "";
|
|
char DeltaGPU_c[12] = "";
|
|
char DeltaRAM_c[12] = "";
|
|
char RealCPU_Hz_c[64] = "";
|
|
char RealGPU_Hz_c[64] = "";
|
|
char RealRAM_Hz_c[64] = "";
|
|
char GPU_Load_c[32] = "";
|
|
char Rotation_SpeedLevel_c[64] = "";
|
|
char RAM_compressed_c[64] = "";
|
|
char RAM_var_compressed_c[128] = "";
|
|
char RAM_percentage_var_compressed_c[128] = "";
|
|
char CPU_Hz_c[64] = "";
|
|
char GPU_Hz_c[64] = "";
|
|
char RAM_Hz_c[64] = "";
|
|
char CPU_compressed_c[160] = "";
|
|
char SOC_temperature_c[32] = "";
|
|
char PCB_temperature_c[32] = "";
|
|
char skin_temperature_c[32] = "";
|
|
char CPU_temp_c[32] = "";
|
|
char GPU_temp_c[32] = "";
|
|
char RAM_temp_c[32] = "";
|
|
char BatteryDraw_c[64] = "";
|
|
char FPS_var_compressed_c[64] = "";
|
|
char RAM_load_c[64] = "";
|
|
char RAM_load2_c[64] = "";
|
|
char RAM_bw_peak_c[16] = "";
|
|
char RAM_bw_total_c[16] = "";
|
|
char RAM_bw_gpu_c[16] = "";
|
|
char RAM_bw_cpu_c[16] = "";
|
|
char Resolutions_c[64] = "";
|
|
char readSpeed_c[32] = "";
|
|
|
|
// New separated value buffers for CPU cores
|
|
char CPU_Core0_c[16] = "";
|
|
char CPU_Core1_c[16] = "";
|
|
char CPU_Core2_c[16] = "";
|
|
char CPU_Core3_c[16] = "";
|
|
|
|
// New separated value buffers for FPS
|
|
char PFPS_value_c[16] = "";
|
|
char FPS_value_c[16] = "";
|
|
|
|
static constexpr uint8_t COMMON_MARGIN = 20;
|
|
FullSettings settings;
|
|
uint64_t systemtickfrequency_impl = systemtickfrequency;
|
|
std::string formattedKeyCombo = keyCombo;
|
|
std::string message;
|
|
const std::vector<std::string> KEY_SYMBOLS = {
|
|
"\uE0E4", "\uE0E5", "\uE0E6", "\uE0E7",
|
|
"\uE0E8", "\uE0E9", "\uE0ED", "\uE0EB",
|
|
"\uE0EE", "\uE0EC", "\uE0E0", "\uE0E1",
|
|
"\uE0E2", "\uE0E3", "\uE08A", "\uE08B",
|
|
"\uE0B6", "\uE0B5"
|
|
};
|
|
|
|
bool skipOnce = true;
|
|
bool runOnce = true;
|
|
|
|
bool originalUseRightAlignment = ult::useRightAlignment;
|
|
public:
|
|
FullOverlay() {
|
|
disableJumpTo = true;
|
|
GetConfigSettings(&settings);
|
|
mutexInit(&mutex_BatteryChecker);
|
|
mutexInit(&mutex_Misc);
|
|
tsl::hlp::requestForeground(false);
|
|
TeslaFPS = settings.refreshRate;
|
|
systemtickfrequency_impl /= settings.refreshRate;
|
|
idletick0.store(systemtickfrequency_impl, std::memory_order_relaxed);
|
|
idletick1.store(systemtickfrequency_impl, std::memory_order_relaxed);
|
|
idletick2.store(systemtickfrequency_impl, std::memory_order_relaxed);
|
|
idletick3.store(systemtickfrequency_impl, std::memory_order_relaxed);
|
|
if (settings.setPosRight) {
|
|
const auto [horizontalUnderscanPixels, verticalUnderscanPixels] = tsl::gfx::getUnderscanPixels();
|
|
tsl::gfx::Renderer::get().setLayerPos(1280-32 - horizontalUnderscanPixels, 0);
|
|
ult::useRightAlignment = true;
|
|
} else {
|
|
tsl::gfx::Renderer::get().setLayerPos(0, 0);
|
|
ult::useRightAlignment = false;
|
|
}
|
|
if (settings.disableScreenshots) {
|
|
tsl::gfx::Renderer::get().removeScreenshotStacks();
|
|
}
|
|
deactivateOriginalFooter = true;
|
|
formatButtonCombination(formattedKeyCombo);
|
|
//message = "Press " + formattedKeyCombo + " to Exit";
|
|
|
|
realVoltsPolling = false;
|
|
StartThreads();
|
|
}
|
|
~FullOverlay() {
|
|
CloseThreads();
|
|
fixForeground = true;
|
|
ult::useRightAlignment = originalUseRightAlignment;
|
|
if (settings.disableScreenshots) {
|
|
tsl::gfx::Renderer::get().addScreenshotStacks();
|
|
}
|
|
deactivateOriginalFooter = false;
|
|
}
|
|
|
|
resolutionCalls m_resolutionRenderCalls[8] = {0};
|
|
resolutionCalls m_resolutionViewportCalls[8] = {0};
|
|
resolutionCalls m_resolutionOutput[8] = {0};
|
|
uint8_t resolutionLookup = 0;
|
|
|
|
virtual tsl::elm::Element* createUI() override {
|
|
|
|
|
|
auto Status = new tsl::elm::CustomDrawer([this](tsl::gfx::Renderer *renderer, u16 x, u16 y, u16 w, u16 h) {
|
|
//static auto targetFreqWidth = renderer->getTextDimensions("Target Frequency", false, 15).first;
|
|
//static auto realFreqWidth = renderer->getTextDimensions("Real Frequency", false, 15).first;
|
|
//static auto freqWidth = std::max(targetFreqWidth, realFreqWidth);
|
|
|
|
//static auto batteryLabelWidth = renderer->getTextDimensions("Battery Power Flow", false, 15).first;
|
|
//static auto fanLabelWidth = renderer->getTextDimensions("Fan Rotation Level", false, 15).first;
|
|
//static auto boardWidth = std::max(batteryLabelWidth, fanLabelWidth);
|
|
|
|
static constexpr size_t valueOffset = 150+10;
|
|
static constexpr size_t deltaOffset = 246+10;
|
|
static constexpr size_t ramPercentageOffset = 350+10;
|
|
|
|
//Print strings
|
|
///CPU
|
|
if (R_SUCCEEDED(clkrstCheck) || R_SUCCEEDED(pcvCheck)) {
|
|
|
|
uint32_t height_offset = 155;
|
|
if (realCPU_Hz && settings.showRealFreqs) {
|
|
height_offset = 162;
|
|
}
|
|
renderer->drawString("CPU Usage", false, COMMON_MARGIN, 120, 20, (settings.catColor1));
|
|
if (settings.showTargetFreqs) {
|
|
//static auto targetFreqWidth = renderer->getTextDimensions("Target Frequency: ", false, 15).first;
|
|
renderer->drawString("Target Frequency", false, COMMON_MARGIN, height_offset, 15, (settings.catColor2));
|
|
renderer->drawString(CPU_Hz_c, false, COMMON_MARGIN + valueOffset, height_offset, 15, (settings.textColor));
|
|
}
|
|
if (realCPU_Hz && settings.showRealFreqs) {
|
|
//static auto realFreqWidth = renderer->getTextDimensions("Real Frequency: ", false, 15).first;
|
|
renderer->drawString("Real Frequency", false, COMMON_MARGIN, height_offset - 15, 15, (settings.catColor2));
|
|
renderer->drawString(RealCPU_Hz_c, false, COMMON_MARGIN + valueOffset, height_offset - 15, 15, (settings.textColor));
|
|
if (settings.showDeltas && settings.showTargetFreqs) {
|
|
renderer->drawString(DeltaCPU_c, false, COMMON_MARGIN + deltaOffset, height_offset - 7, 15, (settings.textColor));
|
|
}
|
|
else if (settings.showDeltas && !settings.showTargetFreqs) {
|
|
renderer->drawString(DeltaCPU_c, false, COMMON_MARGIN + deltaOffset, height_offset - 15, 15, (settings.textColor));
|
|
}
|
|
}
|
|
else if (realCPU_Hz && settings.showDeltas && (settings.showRealFreqs || settings.showTargetFreqs)) {
|
|
renderer->drawString(DeltaCPU_c, false, COMMON_MARGIN + deltaOffset, height_offset, 15, (settings.textColor));
|
|
}
|
|
|
|
// CPU Core labels and values
|
|
static auto core0Width = renderer->getTextDimensions("Core 0 ", false, 15).first;
|
|
static auto core1Width = renderer->getTextDimensions("Core 1 ", false, 15).first;
|
|
static auto core2Width = renderer->getTextDimensions("Core 2 ", false, 15).first;
|
|
static auto core3Width = renderer->getTextDimensions("Core 3 ", false, 15).first;
|
|
|
|
const uint32_t core_height = height_offset + 30;
|
|
renderer->drawString("Core 0 ", false, COMMON_MARGIN, core_height, 15, (settings.catColor2));
|
|
renderer->drawString(CPU_Core0_c, false, COMMON_MARGIN + core0Width, core_height, 15, (settings.textColor));
|
|
|
|
renderer->drawString("Core 1 ", false, COMMON_MARGIN, core_height + 15, 15, (settings.catColor2));
|
|
renderer->drawString(CPU_Core1_c, false, COMMON_MARGIN + core1Width, core_height + 15, 15, (settings.textColor));
|
|
|
|
renderer->drawString("Core 2 ", false, COMMON_MARGIN, core_height + 30, 15, (settings.catColor2));
|
|
renderer->drawString(CPU_Core2_c, false, COMMON_MARGIN + core2Width, core_height + 30, 15, (settings.textColor));
|
|
|
|
renderer->drawString("Core 3 ", false, COMMON_MARGIN, core_height + 45, 15, (settings.catColor2));
|
|
renderer->drawString(CPU_Core3_c, false, COMMON_MARGIN + core3Width, core_height + 45, 15, (settings.textColor));
|
|
}
|
|
|
|
///GPU
|
|
if (R_SUCCEEDED(clkrstCheck) || R_SUCCEEDED(pcvCheck) || R_SUCCEEDED(nvCheck)) {
|
|
|
|
uint32_t height_offset = 320-8;
|
|
if (realGPU_Hz && settings.showRealFreqs) {
|
|
height_offset = 327-8;
|
|
}
|
|
|
|
renderer->drawString("GPU Usage", false, COMMON_MARGIN, 285-8, 20, (settings.catColor1));
|
|
if (R_SUCCEEDED(clkrstCheck) || R_SUCCEEDED(pcvCheck)) {
|
|
if (settings.showTargetFreqs) {
|
|
//static auto targetFreqWidth = renderer->getTextDimensions("Target Frequency: ", false, 15).first;
|
|
renderer->drawString("Target Frequency", false, COMMON_MARGIN, height_offset, 15, (settings.catColor2));
|
|
renderer->drawString(GPU_Hz_c, false, COMMON_MARGIN + valueOffset, height_offset, 15, (settings.textColor));
|
|
}
|
|
if (realCPU_Hz && settings.showRealFreqs) {
|
|
//static auto realFreqWidth = renderer->getTextDimensions("Real Frequency: ", false, 15).first;
|
|
renderer->drawString("Real Frequency", false, COMMON_MARGIN, height_offset - 15, 15, (settings.catColor2));
|
|
renderer->drawString(RealGPU_Hz_c, false, COMMON_MARGIN + valueOffset, height_offset - 15, 15, (settings.textColor));
|
|
if (settings.showDeltas && settings.showTargetFreqs) {
|
|
renderer->drawString(DeltaGPU_c, false, COMMON_MARGIN + deltaOffset, height_offset - 7, 15, (settings.textColor));
|
|
}
|
|
else if (settings.showDeltas && !settings.showTargetFreqs) {
|
|
renderer->drawString(DeltaGPU_c, false, COMMON_MARGIN + deltaOffset, height_offset - 15, 15, (settings.textColor));
|
|
}
|
|
}
|
|
else if (realGPU_Hz && settings.showDeltas && (settings.showRealFreqs || settings.showTargetFreqs)) {
|
|
renderer->drawString(DeltaGPU_c, false, COMMON_MARGIN + deltaOffset, height_offset, 15, (settings.textColor));
|
|
}
|
|
}
|
|
if (R_SUCCEEDED(nvCheck)) {
|
|
//static auto loadWidth = renderer->getTextDimensions("Load: ", false, 15).first;
|
|
renderer->drawString("Load", false, COMMON_MARGIN, height_offset + 15, 15, (settings.catColor2));
|
|
renderer->drawString(GPU_Load_c, false, COMMON_MARGIN + valueOffset, height_offset + 15, 15, (settings.textColor));
|
|
}
|
|
|
|
}
|
|
|
|
static std::vector<std::string> specialChars = {""};
|
|
|
|
///RAM
|
|
if (R_SUCCEEDED(clkrstCheck) || R_SUCCEEDED(pcvCheck) || R_SUCCEEDED(Hinted)) {
|
|
|
|
uint32_t height_offset = 410;
|
|
if (realRAM_Hz && settings.showRealFreqs) {
|
|
height_offset += 7;
|
|
}
|
|
|
|
renderer->drawString("RAM Usage", false, COMMON_MARGIN, 375, 20, (settings.catColor1));
|
|
if (R_SUCCEEDED(clkrstCheck) || R_SUCCEEDED(pcvCheck)) {
|
|
if (settings.showTargetFreqs) {
|
|
//static auto targetFreqWidth = renderer->getTextDimensions("Target Frequency: ", false, 15).first;
|
|
renderer->drawString("Target Frequency", false, COMMON_MARGIN, height_offset, 15, (settings.catColor2));
|
|
renderer->drawString(RAM_Hz_c, false, COMMON_MARGIN + valueOffset, height_offset, 15, (settings.textColor));
|
|
}
|
|
if (realRAM_Hz && settings.showRealFreqs) {
|
|
//static auto realFreqWidth = renderer->getTextDimensions("Real Frequency: ", false, 15).first;
|
|
renderer->drawString("Real Frequency", false, COMMON_MARGIN, height_offset - 15, 15, (settings.catColor2));
|
|
renderer->drawString(RealRAM_Hz_c, false, COMMON_MARGIN + valueOffset, height_offset - 15, 15, (settings.textColor));
|
|
if (settings.showDeltas && settings.showTargetFreqs) {
|
|
renderer->drawString(DeltaRAM_c, false, COMMON_MARGIN + deltaOffset, height_offset - 7, 15, (settings.textColor));
|
|
}
|
|
else if (settings.showDeltas && !settings.showTargetFreqs) {
|
|
renderer->drawString(DeltaRAM_c, false, COMMON_MARGIN + deltaOffset, height_offset - 15, 15, (settings.textColor));
|
|
}
|
|
}
|
|
else if (realRAM_Hz && settings.showDeltas && (settings.showRealFreqs || settings.showTargetFreqs)) {
|
|
renderer->drawString(DeltaRAM_c, false, COMMON_MARGIN + deltaOffset, height_offset, 15, (settings.textColor));
|
|
}
|
|
if (R_SUCCEEDED(hocclkCheck)) {
|
|
if (settings.ramInfoMode == "Bandwidth") {
|
|
// Fixed column layout: labels at fixed x, values at fixed x after max label width
|
|
// Col1: Peak/GPU labels, Col2: Total/CPU labels
|
|
// Values start at a fixed offset so columns don't shift with different digit counts
|
|
static const uint32_t bwLbl1W = renderer->getTextDimensions("Peak ", false, 15).first;
|
|
static const uint32_t bwLbl2W = renderer->getTextDimensions("Total ", false, 15).first;
|
|
static const uint32_t bwValW = renderer->getTextDimensions("99.9 GB/s", false, 15).first;
|
|
static const uint32_t bwGap = renderer->getTextDimensions(" ", false, 15).first;
|
|
const uint32_t xV1 = COMMON_MARGIN + bwLbl1W;
|
|
const uint32_t xL2 = xV1 + bwValW + bwGap;
|
|
const uint32_t xV2 = xL2 + bwLbl2W;
|
|
// Row 1: Peak ... Total
|
|
renderer->drawString("Peak", false, COMMON_MARGIN, height_offset+15, 15, settings.catColor2);
|
|
renderer->drawString(RAM_bw_peak_c, false, xV1, height_offset+15, 15, settings.textColor);
|
|
renderer->drawString("Total", false, xL2, height_offset+15, 15, settings.catColor2);
|
|
renderer->drawString(RAM_bw_total_c, false, xV2, height_offset+15, 15, settings.textColor);
|
|
// Row 2: GPU ... CPU
|
|
renderer->drawString("GPU", false, COMMON_MARGIN, height_offset+30, 15, settings.catColor2);
|
|
renderer->drawString(RAM_bw_gpu_c, false, xV1, height_offset+30, 15, settings.textColor);
|
|
renderer->drawString("CPU", false, xL2, height_offset+30, 15, settings.catColor2);
|
|
renderer->drawString(RAM_bw_cpu_c, false, xV2, height_offset+30, 15, settings.textColor);
|
|
} else {
|
|
static std::vector<std::string> partLoadColoredChars = {"CPU", "GPU"};
|
|
renderer->drawString("Load", false, COMMON_MARGIN, height_offset+15, 15, (settings.catColor2));
|
|
renderer->drawStringWithColoredSections(RAM_load_c, false, partLoadColoredChars, COMMON_MARGIN + valueOffset, height_offset+15, 15, (settings.textColor), settings.catColor2);
|
|
}
|
|
}
|
|
}
|
|
if (R_SUCCEEDED(Hinted)) {
|
|
const uint32_t ramUsageOffset = (R_SUCCEEDED(hocclkCheck) && settings.ramInfoMode == "Bandwidth") ? height_offset + 50 : height_offset + 40;
|
|
//static auto textWidth = renderer->getTextDimensions("Total \nApplication \nApplet \nSystem \nSystem Unsafe ", false, 15).first;
|
|
renderer->drawString("Total\nApplication\nApplet\nSystem\nSystem Unsafe", false, COMMON_MARGIN, ramUsageOffset, 15, (settings.catColor2));
|
|
renderer->drawString(RAM_var_compressed_c, false, COMMON_MARGIN + valueOffset, ramUsageOffset, 15, (settings.textColor));
|
|
renderer->drawString(RAM_percentage_var_compressed_c, false, ramPercentageOffset, ramUsageOffset, 15, (settings.textColor));
|
|
}
|
|
}
|
|
|
|
///Thermal
|
|
if (R_SUCCEEDED(i2cCheck) || R_SUCCEEDED(tcCheck) || R_SUCCEEDED(pwmCheck)) {
|
|
renderer->drawString("Board", false, 20, 550+2, 20, (settings.catColor1));
|
|
if (R_SUCCEEDED(i2cCheck)) {
|
|
renderer->drawString("Battery Power Flow", false, COMMON_MARGIN, 575+2, 15, (settings.catColor2));
|
|
renderer->drawStringWithColoredSections(BatteryDraw_c, false, specialChars, COMMON_MARGIN + valueOffset, 575+2, 15, (settings.textColor), settings.separatorColor);
|
|
}
|
|
if (R_SUCCEEDED(pwmCheck)) {
|
|
renderer->drawString("Fan Rotation Level", false, COMMON_MARGIN, 590+2, 15, (settings.catColor2));
|
|
renderer->drawString(Rotation_SpeedLevel_c, false, COMMON_MARGIN + valueOffset, 590+2, 15, (settings.textColor));
|
|
}
|
|
if (R_SUCCEEDED(i2cCheck) || R_SUCCEEDED(tcCheck)) {
|
|
static auto socLabelWidth = renderer->getTextDimensions("SOC ", false, 15).first;
|
|
static auto pcbLabelWidth = renderer->getTextDimensions("PCB ", false, 15).first;
|
|
static auto maxLabelWidth = std::max(socLabelWidth, pcbLabelWidth);
|
|
static auto skinLabelWidth = renderer->getTextDimensions("Skin ", false, 15).first;
|
|
|
|
// Compute gradient colors for temperatures
|
|
const tsl::Color socColor = settings.useDynamicColors ? tsl::GradientColor(SOC_temperatureF) : settings.textColor;
|
|
const tsl::Color pcbColor = settings.useDynamicColors ? tsl::GradientColor(PCB_temperatureF) : settings.textColor;
|
|
const tsl::Color skinColor = settings.useDynamicColors ? tsl::GradientColor(static_cast<float>(skin_temperaturemiliC) / 1000.0f) : settings.textColor;
|
|
|
|
renderer->drawString("Temperatures", false, COMMON_MARGIN, 605+2, 15, (settings.catColor2));
|
|
|
|
// SOC - starts at valueOffset next to "Temperatures"
|
|
uint32_t current_x = COMMON_MARGIN + valueOffset;
|
|
renderer->drawString("SOC ", false, current_x, 605+2, 15, (settings.catColor2));
|
|
current_x += maxLabelWidth;
|
|
renderer->drawString(SOC_temperature_c, false, current_x, 605+2, 15, socColor);
|
|
|
|
// SKIN - same spacing to the right
|
|
current_x += renderer->getTextDimensions(SOC_temperature_c, false, 15).first + 15;
|
|
renderer->drawString("Skin ", false, current_x, 605+2, 15, (settings.catColor2));
|
|
current_x += skinLabelWidth;
|
|
renderer->drawString(skin_temperature_c, false, current_x, 605+2, 15, skinColor);
|
|
|
|
// PCB - below SOC on next line
|
|
current_x = COMMON_MARGIN + valueOffset;
|
|
renderer->drawString("PCB ", false, current_x, 620+2, 15, (settings.catColor2));
|
|
current_x += maxLabelWidth;
|
|
renderer->drawString(PCB_temperature_c, false, current_x, 620+2, 15, pcbColor);
|
|
}
|
|
}
|
|
|
|
// Real temps - CPU, GPU, RAM
|
|
if (settings.realTemps && (realCPU_Temp != 0 || realGPU_Temp != 0 || realRAM_Temp != 0)) {
|
|
static auto cpuTempLabelWidth = renderer->getTextDimensions("CPU ", false, 15).first;
|
|
static auto gpuTempLabelWidth = renderer->getTextDimensions("GPU ", false, 15).first;
|
|
static auto ramTempLabelWidth = renderer->getTextDimensions("RAM ", false, 15).first;
|
|
|
|
uint32_t current_x = COMMON_MARGIN + 58;;
|
|
|
|
// CPU temp
|
|
if (realCPU_Temp != 0) {
|
|
const tsl::Color cpuTempColor = settings.useDynamicColors ? tsl::GradientColor(realCPU_Temp / 1000.0f) : settings.textColor;
|
|
renderer->drawString("CPU ", false, current_x, 635+2, 15, (settings.catColor2));
|
|
current_x += cpuTempLabelWidth;
|
|
renderer->drawString(CPU_temp_c, false, current_x, 635+2, 15, cpuTempColor);
|
|
current_x += renderer->getTextDimensions(CPU_temp_c, false, 15).first + 15;
|
|
}
|
|
|
|
// GPU temp
|
|
if (realGPU_Temp != 0) {
|
|
const tsl::Color gpuTempColor = settings.useDynamicColors ? tsl::GradientColor(realGPU_Temp / 1000.0f) : settings.textColor;
|
|
renderer->drawString("GPU ", false, current_x, 635+2, 15, (settings.catColor2));
|
|
current_x += gpuTempLabelWidth;
|
|
renderer->drawString(GPU_temp_c, false, current_x, 635+2, 15, gpuTempColor);
|
|
current_x += renderer->getTextDimensions(GPU_temp_c, false, 15).first + 15;
|
|
}
|
|
|
|
// RAM temp
|
|
if (realRAM_Temp != 0) {
|
|
const tsl::Color ramTempColor = settings.useDynamicColors ? tsl::GradientColor(realRAM_Temp / 1000.0f) : settings.textColor;
|
|
renderer->drawString("RAM ", false, current_x, 635+2, 15, (settings.catColor2));
|
|
current_x += ramTempLabelWidth;
|
|
renderer->drawString(RAM_temp_c, false, current_x, 635+2, 15, ramTempColor);
|
|
}
|
|
}
|
|
|
|
///FPS
|
|
if (GameRunning) {
|
|
const uint32_t width_offset = valueOffset;
|
|
if (settings.showFPS || settings.showRES || settings.showRDSD) {
|
|
renderer->drawString("Game", false, COMMON_MARGIN + width_offset, 185+12, 20, (settings.catColor1));
|
|
}
|
|
uint32_t height = 210+12;
|
|
if (settings.showFPS == true) {
|
|
static auto pfpsWidth = renderer->getTextDimensions("PFPS ", false, 15).first;
|
|
static auto fpsWidth = renderer->getTextDimensions("FPS ", false, 15).first;
|
|
|
|
renderer->drawString("PFPS ", false, COMMON_MARGIN + width_offset, height, 15, (settings.catColor2));
|
|
renderer->drawString(PFPS_value_c, false, COMMON_MARGIN + width_offset + pfpsWidth, height, 15, (settings.textColor));
|
|
|
|
// Calculate position for "FPS " label - add some spacing
|
|
const uint32_t fps_x_offset = COMMON_MARGIN + width_offset + pfpsWidth + renderer->getTextDimensions(PFPS_value_c, false, 15).first + 15;
|
|
renderer->drawString("FPS ", false, fps_x_offset, height, 15, (settings.catColor2));
|
|
renderer->drawString(FPS_value_c, false, fps_x_offset + fpsWidth, height, 15, (settings.textColor));
|
|
|
|
height += 15;
|
|
}
|
|
if ((settings.showRES == true) && NxFps && SharedMemoryUsed && (NxFps -> API >= 1)) {
|
|
static auto resLabelWidth = renderer->getTextDimensions("Resolutions ", false, 15).first;
|
|
renderer->drawString("Resolutions ", false, COMMON_MARGIN + width_offset, height, 15, (settings.catColor2));
|
|
|
|
renderer->drawStringWithColoredSections(Resolutions_c, false, specialChars, COMMON_MARGIN + width_offset + resLabelWidth, height, 15, (settings.textColor), settings.separatorColor);
|
|
height += 15;
|
|
}
|
|
if (settings.showRDSD == true) {
|
|
static auto readLabelWidth = renderer->getTextDimensions("Read Speed ", false, 15).first;
|
|
renderer->drawString("Read Speed ", false, COMMON_MARGIN + width_offset, height, 15, (settings.catColor2));
|
|
renderer->drawString(readSpeed_c, false, COMMON_MARGIN + width_offset + readLabelWidth, height, 15, (settings.textColor));
|
|
}
|
|
}
|
|
|
|
//renderer->drawStringWithColoredSections(message, false, KEY_SYMBOLS, 30, 693, 23, a(tsl::bottomTextColor), a(tsl::buttonColor));
|
|
|
|
|
|
static const auto pressWidth = renderer->getTextDimensions("Press ", false, 23).first;
|
|
static const auto keyComboWidth = renderer->getTextDimensions(formattedKeyCombo.c_str(), false, 23).first;
|
|
|
|
static constexpr u16 baseX = 30;
|
|
static constexpr u16 baseY = 693;
|
|
static constexpr u8 fontSize = 23;
|
|
|
|
// Draw "Press "
|
|
renderer->drawString("Press ", false, baseX, baseY, fontSize, (tsl::bottomTextColor));
|
|
|
|
// Draw formatted key combo with colored sections
|
|
renderer->drawStringWithColoredSections(formattedKeyCombo, false, KEY_SYMBOLS, baseX + pressWidth, baseY, fontSize, (tsl::bottomTextColor), (tsl::buttonColor));
|
|
|
|
// Draw " to Exit"
|
|
renderer->drawString(" to Exit", false, baseX + pressWidth + keyComboWidth, baseY, fontSize, (tsl::bottomTextColor));
|
|
});
|
|
|
|
auto rootFrame = new tsl::elm::HeaderOverlayFrame("Horizon OC Monitor", APP_VERSION);
|
|
rootFrame->setContent(Status);
|
|
|
|
return rootFrame;
|
|
}
|
|
|
|
virtual void update() override {
|
|
//Make stuff ready to print
|
|
///CPU
|
|
if (systemtickfrequency_impl > 0) {
|
|
const uint64_t idle0_val = std::min(idletick0.load(std::memory_order_acquire), systemtickfrequency_impl);
|
|
const uint64_t idle1_val = std::min(idletick1.load(std::memory_order_acquire), systemtickfrequency_impl);
|
|
const uint64_t idle2_val = std::min(idletick2.load(std::memory_order_acquire), systemtickfrequency_impl);
|
|
const uint64_t idle3_val = std::min(idletick3.load(std::memory_order_acquire), systemtickfrequency_impl);
|
|
|
|
const float usage0 = std::clamp(100.0f * (1.0f - float(idle0_val) / systemtickfrequency_impl), 0.0f, 100.0f);
|
|
const float usage1 = std::clamp(100.0f * (1.0f - float(idle1_val) / systemtickfrequency_impl), 0.0f, 100.0f);
|
|
const float usage2 = std::clamp(100.0f * (1.0f - float(idle2_val) / systemtickfrequency_impl), 0.0f, 100.0f);
|
|
const float usage3 = std::clamp(100.0f * (1.0f - float(idle3_val) / systemtickfrequency_impl), 0.0f, 100.0f);
|
|
|
|
// Format individual core values
|
|
snprintf(CPU_Core0_c, sizeof(CPU_Core0_c), "%.2f%%", usage0);
|
|
snprintf(CPU_Core1_c, sizeof(CPU_Core1_c), "%.2f%%", usage1);
|
|
snprintf(CPU_Core2_c, sizeof(CPU_Core2_c), "%.2f%%", usage2);
|
|
snprintf(CPU_Core3_c, sizeof(CPU_Core3_c), "%.2f%%", usage3);
|
|
}
|
|
|
|
mutexLock(&mutex_Misc);
|
|
snprintf(CPU_Hz_c, sizeof(CPU_Hz_c), "%u.%u MHz", CPU_Hz / 1000000, (CPU_Hz / 100000) % 10);
|
|
if (realCPU_Hz) {
|
|
snprintf(RealCPU_Hz_c, sizeof(RealCPU_Hz_c), "%u.%u MHz", realCPU_Hz / 1000000, (realCPU_Hz / 100000) % 10);
|
|
const int32_t deltaCPU = (int32_t)(realCPU_Hz / 1000) - (CPU_Hz / 1000);
|
|
snprintf(DeltaCPU_c, sizeof(DeltaCPU_c), "Δ %d.%u", deltaCPU / 1000, abs(deltaCPU / 100) % 10);
|
|
}
|
|
|
|
///GPU
|
|
snprintf(GPU_Hz_c, sizeof GPU_Hz_c, "%u.%u MHz", GPU_Hz / 1000000, (GPU_Hz / 100000) % 10);
|
|
if (realGPU_Hz) {
|
|
snprintf(RealGPU_Hz_c, sizeof(RealGPU_Hz_c), "%u.%u MHz", realGPU_Hz / 1000000, (realGPU_Hz / 100000) % 10);
|
|
const int32_t deltaGPU = (int32_t)(realGPU_Hz / 1000) - (GPU_Hz / 1000);
|
|
snprintf(DeltaGPU_c, sizeof(DeltaGPU_c), "Δ %d.%u", deltaGPU / 1000, abs(deltaGPU / 100) % 10);
|
|
}
|
|
snprintf(GPU_Load_c, sizeof GPU_Load_c, "%u.%u%%", GPU_Load_u / 10, GPU_Load_u % 10);
|
|
|
|
///RAM
|
|
snprintf(RAM_Hz_c, sizeof RAM_Hz_c, "%u.%u MHz", RAM_Hz / 1000000, (RAM_Hz / 100000) % 10);
|
|
if (realRAM_Hz) {
|
|
snprintf(RealRAM_Hz_c, sizeof(RealRAM_Hz_c), "%u.%u MHz", realRAM_Hz / 1000000, (realRAM_Hz / 100000) % 10);
|
|
const int32_t deltaRAM = (int32_t)(realRAM_Hz / 1000) - (RAM_Hz / 1000);
|
|
snprintf(DeltaRAM_c, sizeof(DeltaRAM_c), "Δ %d.%u", deltaRAM / 1000, abs(deltaRAM / 100) % 10);
|
|
}
|
|
|
|
const float RAM_Total_application_f = (float)RAM_Total_application_u / 1024 / 1024;
|
|
const float RAM_Total_applet_f = (float)RAM_Total_applet_u / 1024 / 1024;
|
|
const float RAM_Total_system_f = (float)RAM_Total_system_u / 1024 / 1024;
|
|
const float RAM_Total_systemunsafe_f = (float)RAM_Total_systemunsafe_u / 1024 / 1024;
|
|
const float RAM_Total_all_f = RAM_Total_application_f + RAM_Total_applet_f + RAM_Total_system_f + RAM_Total_systemunsafe_f;
|
|
|
|
const float RAM_Used_application_f = (float)RAM_Used_application_u / 1024 / 1024;
|
|
const float RAM_Used_applet_f = (float)RAM_Used_applet_u / 1024 / 1024;
|
|
const float RAM_Used_system_f = (float)RAM_Used_system_u / 1024 / 1024;
|
|
const float RAM_Used_systemunsafe_f = (float)RAM_Used_systemunsafe_u / 1024 / 1024;
|
|
const float RAM_Used_all_f = RAM_Used_application_f + RAM_Used_applet_f + RAM_Used_system_f + RAM_Used_systemunsafe_f;
|
|
|
|
// Compute percentages
|
|
const int RAMPct_all = (int)((RAM_Used_all_f / RAM_Total_all_f) * 100.0f );
|
|
const int RAMPct_app = (int)((RAM_Used_application_f / RAM_Total_application_f) * 100.0f );
|
|
const int RAMPct_applet = (int)((RAM_Used_applet_f / RAM_Total_applet_f) * 100.0f );
|
|
const int RAMPct_system = (int)((RAM_Used_system_f / RAM_Total_system_f) * 100.0f );
|
|
const int RAMPct_systemunsafe = (int)((RAM_Used_systemunsafe_f/ RAM_Total_systemunsafe_f)* 100.0f );
|
|
|
|
snprintf(RAM_var_compressed_c, sizeof(RAM_var_compressed_c),
|
|
"%.1f MB / %.1f MB\n"
|
|
"%.1f MB / %.1f MB\n"
|
|
"%.1f MB / %.1f MB\n"
|
|
"%.1f MB / %.1f MB\n"
|
|
"%.1f MB / %.1f MB",
|
|
RAM_Used_all_f, RAM_Total_all_f,
|
|
RAM_Used_application_f, RAM_Total_application_f,
|
|
RAM_Used_applet_f, RAM_Total_applet_f,
|
|
RAM_Used_system_f, RAM_Total_system_f,
|
|
RAM_Used_systemunsafe_f, RAM_Total_systemunsafe_f
|
|
);
|
|
|
|
// 2. Percentages only (newlines preserved)
|
|
snprintf(RAM_percentage_var_compressed_c, sizeof(RAM_percentage_var_compressed_c),
|
|
"(%d%%)\n"
|
|
"(%d%%)\n"
|
|
"(%d%%)\n"
|
|
"(%d%%)\n"
|
|
"(%d%%)",
|
|
RAMPct_all,
|
|
RAMPct_app,
|
|
RAMPct_applet,
|
|
RAMPct_system,
|
|
RAMPct_systemunsafe
|
|
);
|
|
|
|
if (R_SUCCEEDED(hocclkCheck)) {
|
|
if (settings.ramInfoMode == "Bandwidth") {
|
|
const unsigned bwAll = partLoad[HocClkPartLoad_RamBWAll] / 1000;
|
|
const unsigned bwAllD = (partLoad[HocClkPartLoad_RamBWAll] % 1000) / 100;
|
|
const unsigned bwPeak = partLoad[HocClkPartLoad_RamBWPeak] / 1000;
|
|
const unsigned bwPeakD= (partLoad[HocClkPartLoad_RamBWPeak]% 1000) / 100;
|
|
const unsigned bwCpu = partLoad[HocClkPartLoad_RamBWCpu] / 1000;
|
|
const unsigned bwCpuD = (partLoad[HocClkPartLoad_RamBWCpu] % 1000) / 100;
|
|
const unsigned bwGpu = partLoad[HocClkPartLoad_RamBWGpu] / 1000;
|
|
const unsigned bwGpuD = (partLoad[HocClkPartLoad_RamBWGpu] % 1000) / 100;
|
|
snprintf(RAM_bw_peak_c, sizeof RAM_bw_peak_c, "%u.%u GB/s", bwPeak, bwPeakD);
|
|
snprintf(RAM_bw_total_c, sizeof RAM_bw_total_c, "%u.%u GB/s", bwAll, bwAllD);
|
|
snprintf(RAM_bw_gpu_c, sizeof RAM_bw_gpu_c, "%u.%u GB/s", bwGpu, bwGpuD);
|
|
snprintf(RAM_bw_cpu_c, sizeof RAM_bw_cpu_c, "%u.%u GB/s", bwCpu, bwCpuD);
|
|
} else {
|
|
const int RAM_GPU_Load = partLoad[HocClkPartLoad_EMC] - partLoad[HocClkPartLoad_EMCCpu];
|
|
snprintf(RAM_load_c, sizeof RAM_load_c,
|
|
"%u.%u%% CPU %u.%u%% GPU %u.%u%%",
|
|
partLoad[HocClkPartLoad_EMC] / 10, partLoad[HocClkPartLoad_EMC] % 10,
|
|
partLoad[HocClkPartLoad_EMCCpu] / 10, partLoad[HocClkPartLoad_EMCCpu] % 10,
|
|
RAM_GPU_Load / 10, RAM_GPU_Load % 10);
|
|
}
|
|
}
|
|
///Thermal
|
|
snprintf(SOC_temperature_c, sizeof SOC_temperature_c, "%.1f\u00B0C", SOC_temperatureF);
|
|
snprintf(PCB_temperature_c, sizeof PCB_temperature_c, "%.1f\u00B0C", PCB_temperatureF);
|
|
snprintf(skin_temperature_c, sizeof skin_temperature_c, "%d.%d\u00B0C", skin_temperaturemiliC / 1000, (skin_temperaturemiliC / 100) % 10);
|
|
|
|
snprintf(Rotation_SpeedLevel_c, sizeof Rotation_SpeedLevel_c, "%.1f%%", Rotation_Duty);
|
|
|
|
if (settings.realTemps) {
|
|
if (realCPU_Temp != 0) {
|
|
snprintf(CPU_temp_c, sizeof(CPU_temp_c), "%.1f°C", realCPU_Temp / 1000.0f);
|
|
}
|
|
if (realGPU_Temp != 0) {
|
|
snprintf(GPU_temp_c, sizeof(GPU_temp_c), "%.1f°C", realGPU_Temp / 1000.0f);
|
|
}
|
|
if (realRAM_Temp != 0) {
|
|
snprintf(RAM_temp_c, sizeof(RAM_temp_c), "%.1f°C", realRAM_Temp / 1000.0f);
|
|
}
|
|
}
|
|
///FPS
|
|
if (settings.showFPS == true) {
|
|
snprintf(PFPS_value_c, sizeof PFPS_value_c, "%1u", FPS);
|
|
snprintf(FPS_value_c, sizeof FPS_value_c, "%.1f", useOldFPSavg ? FPSavg_old : FPSavg);
|
|
}
|
|
|
|
//Resolutions
|
|
if ((settings.showRES == true) && GameRunning && NxFps) {
|
|
if (!resolutionLookup) {
|
|
(NxFps -> renderCalls[0].calls) = 0xFFFF;
|
|
resolutionLookup = 1;
|
|
}
|
|
else if (resolutionLookup == 1) {
|
|
if ((NxFps -> renderCalls[0].calls) != 0xFFFF) resolutionLookup = 2;
|
|
}
|
|
else {
|
|
if (NxFps && SharedMemoryUsed) {
|
|
memcpy(&m_resolutionRenderCalls, &(NxFps -> renderCalls), sizeof(m_resolutionRenderCalls));
|
|
memcpy(&m_resolutionViewportCalls, &(NxFps -> viewportCalls), sizeof(m_resolutionViewportCalls));
|
|
} else {
|
|
memset(&m_resolutionRenderCalls, 0, sizeof(m_resolutionRenderCalls));
|
|
memset(&m_resolutionViewportCalls, 0, sizeof(m_resolutionViewportCalls));
|
|
}
|
|
qsort(m_resolutionRenderCalls, 8, sizeof(resolutionCalls), compare);
|
|
qsort(m_resolutionViewportCalls, 8, sizeof(resolutionCalls), compare);
|
|
memset(&m_resolutionOutput, 0, sizeof(m_resolutionOutput));
|
|
size_t out_iter = 0;
|
|
bool found = false;
|
|
for (size_t i = 0; i < 8; i++) {
|
|
for (size_t x = 0; x < 8; x++) {
|
|
if (m_resolutionRenderCalls[i].width == 0) {
|
|
break;
|
|
}
|
|
if ((m_resolutionRenderCalls[i].width == m_resolutionViewportCalls[x].width) && (m_resolutionRenderCalls[i].height == m_resolutionViewportCalls[x].height)) {
|
|
m_resolutionOutput[out_iter].width = m_resolutionRenderCalls[i].width;
|
|
m_resolutionOutput[out_iter].height = m_resolutionRenderCalls[i].height;
|
|
m_resolutionOutput[out_iter].calls = (m_resolutionRenderCalls[i].calls > m_resolutionViewportCalls[x].calls) ? m_resolutionRenderCalls[i].calls : m_resolutionViewportCalls[x].calls;
|
|
out_iter++;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found && m_resolutionRenderCalls[i].width != 0) {
|
|
m_resolutionOutput[out_iter].width = m_resolutionRenderCalls[i].width;
|
|
m_resolutionOutput[out_iter].height = m_resolutionRenderCalls[i].height;
|
|
m_resolutionOutput[out_iter].calls = m_resolutionRenderCalls[i].calls;
|
|
out_iter++;
|
|
}
|
|
found = false;
|
|
if (out_iter == 8) break;
|
|
}
|
|
if (out_iter < 8) {
|
|
const size_t out_iter_s = out_iter;
|
|
for (size_t x = 0; x < 8; x++) {
|
|
for (size_t y = 0; y < out_iter_s; y++) {
|
|
if (m_resolutionViewportCalls[x].width == 0) {
|
|
break;
|
|
}
|
|
if ((m_resolutionViewportCalls[x].width == m_resolutionOutput[y].width) && (m_resolutionViewportCalls[x].height == m_resolutionOutput[y].height)) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found && m_resolutionViewportCalls[x].width != 0) {
|
|
m_resolutionOutput[out_iter].width = m_resolutionViewportCalls[x].width;
|
|
m_resolutionOutput[out_iter].height = m_resolutionViewportCalls[x].height;
|
|
m_resolutionOutput[out_iter].calls = m_resolutionViewportCalls[x].calls;
|
|
out_iter++;
|
|
}
|
|
found = false;
|
|
if (out_iter == 8) break;
|
|
}
|
|
}
|
|
qsort(m_resolutionOutput, 8, sizeof(resolutionCalls), compare);
|
|
static std::pair<uint16_t, uint16_t> old_res[2];
|
|
|
|
// Only swap if BOTH resolutions exist (prevent swapping with empty slot)
|
|
if (m_resolutionOutput[0].width && m_resolutionOutput[1].width) {
|
|
if ((m_resolutionOutput[0].width == old_res[1].first && m_resolutionOutput[0].height == old_res[1].second) ||
|
|
(m_resolutionOutput[1].width == old_res[0].first && m_resolutionOutput[1].height == old_res[0].second)) {
|
|
const uint16_t swap_width = m_resolutionOutput[0].width;
|
|
const uint16_t swap_height = m_resolutionOutput[0].height;
|
|
m_resolutionOutput[0].width = m_resolutionOutput[1].width;
|
|
m_resolutionOutput[0].height = m_resolutionOutput[1].height;
|
|
m_resolutionOutput[1].width = swap_width;
|
|
m_resolutionOutput[1].height = swap_height;
|
|
}
|
|
}
|
|
|
|
//if (!m_resolutionOutput[1].width) {
|
|
// snprintf(Resolutions_c, sizeof(Resolutions_c), "%dx%d", m_resolutionOutput[0].width, m_resolutionOutput[0].height);
|
|
//}
|
|
//else {
|
|
// snprintf(Resolutions_c, sizeof(Resolutions_c), "%dx%d%dx%d", m_resolutionOutput[0].width, m_resolutionOutput[0].height, m_resolutionOutput[1].width, m_resolutionOutput[1].height);
|
|
//}
|
|
|
|
if (!m_resolutionOutput[1].width || !m_resolutionOutput[0].width) {
|
|
if (!m_resolutionOutput[1].width)
|
|
snprintf(Resolutions_c, sizeof(Resolutions_c), "%dx%d", m_resolutionOutput[0].width, m_resolutionOutput[0].height);
|
|
else snprintf(Resolutions_c, sizeof(Resolutions_c), "%dx%d", m_resolutionOutput[1].width, m_resolutionOutput[1].height);
|
|
}
|
|
else snprintf(Resolutions_c, sizeof(Resolutions_c),"%dx%d%dx%d", m_resolutionOutput[0].width, m_resolutionOutput[0].height, m_resolutionOutput[1].width, m_resolutionOutput[1].height);
|
|
|
|
old_res[0] = std::make_pair(m_resolutionOutput[0].width, m_resolutionOutput[0].height);
|
|
old_res[1] = std::make_pair(m_resolutionOutput[1].width, m_resolutionOutput[1].height);
|
|
}
|
|
if (settings.showRDSD == true && GameRunning && NxFps) {
|
|
if ((NxFps -> readSpeedPerSecond) != 0.f) snprintf(readSpeed_c, sizeof(readSpeed_c), "%.2f MiB/s", (NxFps -> readSpeedPerSecond) / 1048576.f);
|
|
else snprintf(readSpeed_c, sizeof(readSpeed_c), "n/d");
|
|
}
|
|
}
|
|
else if (!GameRunning && resolutionLookup != 0) {
|
|
resolutionLookup = 0;
|
|
}
|
|
|
|
mutexUnlock(&mutex_Misc);
|
|
|
|
/* ── Battery / power draw ───────────────────────────────────── */
|
|
char remainingBatteryLife[8];
|
|
|
|
/* Normalise "-0.00" → "0.00" W */
|
|
const float drawW = (fabsf(PowerConsumption) < 0.01f) ? 0.0f
|
|
: PowerConsumption;
|
|
|
|
mutexLock(&mutex_BatteryChecker);
|
|
|
|
/* keep "--:--" whenever estimate is negative */
|
|
if (batTimeEstimate >= 0 && !(drawW <= 0.01f && drawW >= -0.01f)) {
|
|
snprintf(remainingBatteryLife, sizeof(remainingBatteryLife),
|
|
"%d:%02d", batTimeEstimate / 60, batTimeEstimate % 60);
|
|
} else {
|
|
strcpy(remainingBatteryLife, "--:--");
|
|
}
|
|
|
|
const float batteryPercent = (float)_batteryChargeInfoFields.RawBatteryCharge / 1000.0f;
|
|
|
|
snprintf(BatteryDraw_c, sizeof(BatteryDraw_c),
|
|
"%.2f W%.0f%% [%s]",
|
|
drawW,
|
|
batteryPercent,
|
|
remainingBatteryLife);
|
|
|
|
mutexUnlock(&mutex_BatteryChecker);
|
|
|
|
if (!skipOnce) {
|
|
if (runOnce) {
|
|
isRendering = true;
|
|
leventClear(&renderingStopEvent);
|
|
runOnce = false;
|
|
}
|
|
} else {
|
|
skipOnce = false;
|
|
}
|
|
}
|
|
|
|
|
|
virtual bool handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos, HidAnalogStickState joyStickPosLeft, HidAnalogStickState joyStickPosRight) override {
|
|
if (isKeyComboPressed(keysHeld, keysDown)) {
|
|
isRendering = false;
|
|
leventSignal(&renderingStopEvent);
|
|
triggerRumbleDoubleClick.store(true, std::memory_order_release);
|
|
triggerExitSound.store(true, std::memory_order_release);
|
|
skipOnce = true;
|
|
runOnce = true;
|
|
TeslaFPS = 60;
|
|
lastSelectedItem = "Full";
|
|
lastMode = "";
|
|
tsl::swapTo<MainMenu>();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}; |