rewrite everything

This commit is contained in:
souldbminersmwc
2025-09-17 19:56:06 -04:00
parent a1bfcebba8
commit f3eae72b47
177 changed files with 49152 additions and 1258 deletions

View File

@@ -0,0 +1,23 @@
/*
* --------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
* wrote this file. As long as you retain this notice you can do whatever you
* want with this stuff. If you meet any of us some day, and you think this
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
* --------------------------------------------------------------------------
*/
#pragma once
#if defined(__cplusplus)
extern "C"
{
#endif
#include <sysclk.h>
#include <sysclk/client/ipc.h>
#if defined(__cplusplus)
}
#endif

View File

@@ -0,0 +1,81 @@
/*
* --------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
* wrote this file. As long as you retain this notice you can do whatever you
* want with this stuff. If you meet any of us some day, and you think this
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
* --------------------------------------------------------------------------
*/
#define TESLA_INIT_IMPL
#include <tesla.hpp>
#include "ui/gui/fatal_gui.h"
#include "ui/gui/main_gui.h"
#include "rgltr_services.h" // for extern Service g_rgltrSrv, etc.
class AppOverlay : public tsl::Overlay
{
public:
AppOverlay() {}
~AppOverlay() {}
//virtual void initServices() override {
// rgltrInitialize();
//}
virtual void exitServices() override {
rgltrExit();
sysclkIpcExit();
}
virtual std::unique_ptr<tsl::Gui> loadInitialGui() override
{
uint32_t apiVersion;
smInitialize();
tsl::hlp::ScopeGuard smGuard([] { smExit(); });
if(!sysclkIpcRunning())
{
return initially<FatalGui>(
"sys-clk is not running.\n\n"
"\n"
"Please make sure it is correctly\n\n"
"installed and enabled.",
""
);
}
if(R_FAILED(sysclkIpcInitialize()) || R_FAILED(sysclkIpcGetAPIVersion(&apiVersion)))
{
return initially<FatalGui>(
"Could not connect to sys-clk.\n\n"
"\n"
"Please make sure it is correctly\n\n"
"installed and enabled.",
""
);
}
if(SYSCLK_IPC_API_VERSION != apiVersion)
{
return initially<FatalGui>(
"Overlay not compatible with\n\n"
"the running sys-clk version.\n\n"
"\n"
"Please make sure everything is\n\n"
"installed and up to date.",
""
);
}
return initially<MainGui>();
}
};
int main(int argc, char **argv)
{
return tsl::loop<AppOverlay>(argc, argv);
}

View File

@@ -0,0 +1,41 @@
typedef enum {
PcvPowerDomain_Max77620_Sd0 = 0,
PcvPowerDomain_Max77620_Sd1 = 1,
PcvPowerDomain_Max77620_Sd2 = 2,
PcvPowerDomain_Max77620_Sd3 = 3,
PcvPowerDomain_Max77620_Ldo0 = 4,
PcvPowerDomain_Max77620_Ldo1 = 5,
PcvPowerDomain_Max77620_Ldo2 = 6,
PcvPowerDomain_Max77620_Ldo3 = 7,
PcvPowerDomain_Max77620_Ldo4 = 8,
PcvPowerDomain_Max77620_Ldo5 = 9,
PcvPowerDomain_Max77620_Ldo6 = 10,
PcvPowerDomain_Max77620_Ldo7 = 11,
PcvPowerDomain_Max77620_Ldo8 = 12,
PcvPowerDomain_Max77621_Cpu = 13,
PcvPowerDomain_Max77621_Gpu = 14,
PcvPowerDomain_Max77812_Cpu = 15,
PcvPowerDomain_Max77812_Gpu = 16,
PcvPowerDomain_Max77812_Dram = 17,
} PowerDomain;
typedef enum {
PcvPowerDomainId_Max77620_Sd0 = 0x3A000080,
PcvPowerDomainId_Max77620_Sd1 = 0x3A000081,
PcvPowerDomainId_Max77620_Sd2 = 0x3A000082,
PcvPowerDomainId_Max77620_Sd3 = 0x3A000083,
PcvPowerDomainId_Max77620_Ldo0 = 0x3A0000A0,
PcvPowerDomainId_Max77620_Ldo1 = 0x3A0000A1,
PcvPowerDomainId_Max77620_Ldo2 = 0x3A0000A2,
PcvPowerDomainId_Max77620_Ldo3 = 0x3A0000A3,
PcvPowerDomainId_Max77620_Ldo4 = 0x3A0000A4,
PcvPowerDomainId_Max77620_Ldo5 = 0x3A0000A5,
PcvPowerDomainId_Max77620_Ldo6 = 0x3A0000A6,
PcvPowerDomainId_Max77620_Ldo7 = 0x3A0000A7,
PcvPowerDomainId_Max77620_Ldo8 = 0x3A0000A8,
PcvPowerDomainId_Max77621_Cpu = 0x3A000003,
PcvPowerDomainId_Max77621_Gpu = 0x3A000004,
PcvPowerDomainId_Max77812_Cpu = 0x3A000003,
PcvPowerDomainId_Max77812_Gpu = 0x3A000004,
PcvPowerDomainId_Max77812_Dram = 0x3A000005,
} PowerDomainId;

View File

@@ -0,0 +1,19 @@
#pragma once
#include <switch.h>
#include "pcv_types.h"
typedef struct {
Service s;
} RgltrSession;
Result rgltrInitialize(void);
void rgltrExit(void);
Service* rgltrGetServiceSession(void);
Result rgltrOpenSession(RgltrSession* session_out, PowerDomainId module_id);
void rgltrCloseSession(RgltrSession* session);
Result rgltrGetVoltage(RgltrSession* session, u32 *out_volt);
Result rgltrGetPowerModuleNumLimit(u32 *out);
Result rgltrGetVoltageEnabled(RgltrSession* session, u32 *out);

View File

@@ -0,0 +1,43 @@
// rgltr_services.cpp (no changes needed here—just compile it once)
#include <switch.h>
#include "rgltr.h"
#include "rgltr_services.h" // for extern Service g_rgltrSrv, etc.
// Global service handle
Service g_rgltrSrv;
Result rgltrInitialize(void) {
if (hosversionBefore(8, 0, 0)) {
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
}
return smGetService(&g_rgltrSrv, "rgltr");
}
void rgltrExit(void) {
serviceClose(&g_rgltrSrv);
}
Result rgltrOpenSession(RgltrSession* session_out, PowerDomainId module_id) {
const u32 in = (u32)module_id;
return serviceDispatchIn(
&g_rgltrSrv,
0,
in,
.out_num_objects = 1,
.out_objects = &session_out->s
);
}
Result rgltrGetVoltage(RgltrSession* session, u32* out_volt) {
// Service returns µV (microvolts) in a local u32:
u32 temp = 0;
Result rc = serviceDispatchOut(&session->s, 4, temp);
if (R_SUCCEEDED(rc)) {
*out_volt = temp;
}
return rc;
}
void rgltrCloseSession(RgltrSession* session) {
serviceClose(&session->s);
}

View File

@@ -0,0 +1,29 @@
// rgltr_services.h
// ========
// Minimal header declarations for rgltrrelated functionality.
// Any file that wants to call rgltrOpenSession(), rgltrGetVoltage(), etc. should
// simply do `#include "infonx.h"` (NOT infonx.cpp).
#pragma once
#include <switch.h> // for Service, Result, hosversionBefore(), smGetService(), serviceClose(), etc.
#include "rgltr.h" // for RgltrSession, PowerDomainId, etc.
// Global service handle for "rgltr". Defined in infonx.cpp.
extern Service g_rgltrSrv;
// Open/close the "rgltr" service. You must call rgltrInitialize() (once) before using
// rgltrOpenSession() & friends. Call rgltrExit() when your app is shutting down.
Result rgltrInitialize(void);
void rgltrExit(void);
// Open a regulator session for the given PowerDomainId (e.g. CPU, GPU, DRAM).
// On success, (*session_out).s will contain a valid Service handle.
Result rgltrOpenSession(RgltrSession* session_out, PowerDomainId module_id);
// Query the current voltage (in microvolts, µV) from a previously opened session.
// Writes the result into *out_volt.
Result rgltrGetVoltage(RgltrSession* session, u32* out_volt);
// Close a previously opened regulator session.
void rgltrCloseSession(RgltrSession* session);

View File

@@ -0,0 +1,31 @@
/*
* --------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
* wrote this file. As long as you retain this notice you can do whatever you
* want with this stuff. If you meet any of us some day, and you think this
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
* --------------------------------------------------------------------------
*/
#pragma once
#include <tesla.hpp>
#include "../gui/base_gui.h"
class BaseFrame : public tsl::elm::HeaderOverlayFrame
{
public:
BaseFrame(BaseGui* gui) : tsl::elm::HeaderOverlayFrame(234) {
this->gui = gui;
}
void draw(tsl::gfx::Renderer* renderer) override
{
tsl::elm::HeaderOverlayFrame::draw(renderer);
this->gui->preDraw(renderer);
}
protected:
BaseGui* gui;
};

View File

@@ -0,0 +1,28 @@
/*
* --------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
* wrote this file. As long as you retain this notice you can do whatever you
* want with this stuff. If you meet any of us some day, and you think this
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
* --------------------------------------------------------------------------
*/
#pragma once
#include <cstdio>
#define FREQ_DEFAULT_TEXT "Do not override"
static inline std::string formatListFreqMHz(std::uint32_t mhz)
{
if(mhz == 0)
{
return FREQ_DEFAULT_TEXT;
}
char buf[10];
return std::string(buf, snprintf(buf, sizeof(buf), "%u MHz", mhz));
}
static inline std::string formatListFreqHz(std::uint32_t hz) { return formatListFreqMHz(hz / 1000000); }

View File

@@ -0,0 +1,127 @@
/*
* --------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
* wrote this file. As long as you retain this notice you can do whatever you
* want with this stuff. If you meet any of us some day, and you think this
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
* --------------------------------------------------------------------------
*/
#include "app_profile_gui.h"
#include "../format.h"
#include "fatal_gui.h"
AppProfileGui::AppProfileGui(std::uint64_t applicationId, SysClkTitleProfileList* profileList)
{
this->applicationId = applicationId;
this->profileList = profileList;
}
AppProfileGui::~AppProfileGui()
{
delete this->profileList;
}
void AppProfileGui::openFreqChoiceGui(tsl::elm::ListItem* listItem, SysClkProfile profile, SysClkModule module)
{
std::uint32_t hzList[SYSCLK_FREQ_LIST_MAX];
std::uint32_t hzCount;
Result rc = sysclkIpcGetFreqList(module, &hzList[0], SYSCLK_FREQ_LIST_MAX, &hzCount);
if(R_FAILED(rc))
{
FatalGui::openWithResultCode("sysclkIpcGetFreqList", rc);
return;
}
tsl::changeTo<FreqChoiceGui>(this->profileList->mhzMap[profile][module] * 1000000, hzList, hzCount, module, [this, listItem, profile, module](std::uint32_t hz) {
this->profileList->mhzMap[profile][module] = hz / 1000000;
listItem->setValue(formatListFreqMHz(this->profileList->mhzMap[profile][module]));
Result rc = sysclkIpcSetProfiles(this->applicationId, this->profileList);
if(R_FAILED(rc))
{
FatalGui::openWithResultCode("sysclkIpcSetProfiles", rc);
return false;
}
return true;
});
}
void AppProfileGui::addModuleListItem(SysClkProfile profile, SysClkModule module)
{
tsl::elm::ListItem* listItem = new tsl::elm::ListItem(sysclkFormatModule(module, true));
listItem->setValue(formatListFreqMHz(this->profileList->mhzMap[profile][module]));
listItem->setClickListener([this, listItem, profile, module](u64 keys) {
if((keys & HidNpadButton_A) == HidNpadButton_A)
{
this->openFreqChoiceGui(listItem, profile, module);
return true;
}
else if((keys & HidNpadButton_Y) == HidNpadButton_Y)
{
// Reset to "Do not override" (0 MHz)
this->profileList->mhzMap[profile][module] = 0;
listItem->setValue(formatListFreqMHz(0));
// Save the updated profile
Result rc = sysclkIpcSetProfiles(this->applicationId, this->profileList);
if(R_FAILED(rc))
{
FatalGui::openWithResultCode("sysclkIpcSetProfiles", rc);
return false;
}
return true;
}
return false;
});
this->listElement->addItem(listItem);
}
void AppProfileGui::addProfileUI(SysClkProfile profile)
{
this->listElement->addItem(new tsl::elm::CategoryHeader(sysclkFormatProfile(profile, true) + std::string(" ") + ult::DIVIDER_SYMBOL + "  Reset"));
this->addModuleListItem(profile, SysClkModule_CPU);
this->addModuleListItem(profile, SysClkModule_GPU);
this->addModuleListItem(profile, SysClkModule_MEM);
}
void AppProfileGui::listUI()
{
this->addProfileUI(SysClkProfile_Docked);
this->addProfileUI(SysClkProfile_Handheld);
this->addProfileUI(SysClkProfile_HandheldCharging);
this->addProfileUI(SysClkProfile_HandheldChargingOfficial);
this->addProfileUI(SysClkProfile_HandheldChargingUSB);
}
void AppProfileGui::changeTo(std::uint64_t applicationId)
{
SysClkTitleProfileList* profileList = new SysClkTitleProfileList;
Result rc = sysclkIpcGetProfiles(applicationId, profileList);
if(R_FAILED(rc))
{
delete profileList;
FatalGui::openWithResultCode("sysclkIpcGetProfiles", rc);
return;
}
tsl::changeTo<AppProfileGui>(applicationId, profileList);
}
void AppProfileGui::update()
{
BaseMenuGui::update();
if((this->context && this->applicationId != this->context->applicationId) && this->applicationId != SYSCLK_GLOBAL_PROFILE_TID)
{
tsl::changeTo<FatalGui>(
"Application changed\n\n"
"\n"
"The running application changed\n\n"
"while editing was going on.",
""
);
}
}

View File

@@ -0,0 +1,34 @@
/*
* --------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
* wrote this file. As long as you retain this notice you can do whatever you
* want with this stuff. If you meet any of us some day, and you think this
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
* --------------------------------------------------------------------------
*/
#pragma once
#include "../../ipc.h"
#include "base_menu_gui.h"
#include "freq_choice_gui.h"
#define SYSCLK_GLOBAL_PROFILE_TID 0xA111111111111111
class AppProfileGui : public BaseMenuGui
{
protected:
std::uint64_t applicationId;
SysClkTitleProfileList* profileList;
void openFreqChoiceGui(tsl::elm::ListItem* listItem, SysClkProfile profile, SysClkModule module);
void addModuleListItem(SysClkProfile profile, SysClkModule module);
void addProfileUI(SysClkProfile profile);
public:
AppProfileGui(std::uint64_t applicationId, SysClkTitleProfileList* profileList);
~AppProfileGui();
void listUI() override;
static void changeTo(std::uint64_t applicationId);
void update() override;
};

View File

@@ -0,0 +1,56 @@
/*
* --------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
* wrote this file. As long as you retain this notice you can do whatever you
* want with this stuff. If you meet any of us some day, and you think this
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
* --------------------------------------------------------------------------
*/
#include "base_gui.h"
#include "../elements/base_frame.h"
#include "logo_rgba_bin.h"
#define LOGO_WIDTH 110
#define LOGO_HEIGHT 39
#define LOGO_X 18
#define LOGO_Y 21
#define LOGO_LABEL_X (LOGO_X + LOGO_WIDTH + 6)
#define LOGO_LABEL_Y 50
#define LOGO_LABEL_FONT_SIZE 28
#define VERSION_X (LOGO_LABEL_X + 110+8)
#define VERSION_Y LOGO_LABEL_Y-4
#define VERSION_FONT_SIZE 15
std::string getVersionString() {
char buf[0x100] = ""; // 256 bytes — safe for any expected version string
Result rc = sysclkIpcGetVersionString(buf, sizeof(buf));
if (R_FAILED(rc) || buf[0] == '\0') {
return "unknown";
}
return std::string(buf);
}
void BaseGui::preDraw(tsl::gfx::Renderer* renderer)
{
renderer->drawBitmap(LOGO_X, LOGO_Y, LOGO_WIDTH, LOGO_HEIGHT, logo_rgba_bin);
renderer->drawString("overlay", false, LOGO_LABEL_X, LOGO_LABEL_Y, LOGO_LABEL_FONT_SIZE, renderer->a(TEXT_COLOR));
renderer->drawString(TARGET_VERSION, false, VERSION_X, VERSION_Y, VERSION_FONT_SIZE, tsl::bannerVersionTextColor);
}
tsl::elm::Element* BaseGui::createUI()
{
BaseFrame* rootFrame = new BaseFrame(this);
rootFrame->setContent(this->baseUI());
return rootFrame;
}
void BaseGui::update()
{
this->refresh();
}

View File

@@ -0,0 +1,36 @@
/*
* --------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
* wrote this file. As long as you retain this notice you can do whatever you
* want with this stuff. If you meet any of us some day, and you think this
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
* --------------------------------------------------------------------------
*/
#pragma once
#include <tesla.hpp>
#include <fstream>
#include <vector>
#include <string>
#include <algorithm>
#include "../style.h"
#include "../../ipc.h"
class BaseGui : public tsl::Gui
{
public:
BaseGui() {}
~BaseGui() {}
virtual void preDraw(tsl::gfx::Renderer* renderer);
void update() override;
tsl::elm::Element* createUI() override;
virtual tsl::elm::Element* baseUI() = 0;
virtual void refresh() {}
private:
};
extern std::string getVersionString();

View File

@@ -0,0 +1,291 @@
/*
* --------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
* wrote this file. As long as you retain this notice you can do whatever you
* want with this stuff. If you meet any of us some day, and you think this
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
* --------------------------------------------------------------------------
*/
#include "base_menu_gui.h"
#include "fatal_gui.h"
// Cache hardware model to avoid repeated syscalls
BaseMenuGui::BaseMenuGui() : tempColors{tsl::Color(0), tsl::Color(0), tsl::Color(0)}
{
tsl::initializeThemeVars();
this->context = nullptr;
this->lastContextUpdate = 0;
this->listElement = nullptr;
// Initialize all voltages to zero once
memset(&cpuVoltageUv, 0, sizeof(u32) * 5); // Zero all 5 voltage values at once
// Pre-cache hardware model during initialization
IsMariko();
// Initialize display strings
memset(displayStrings, 0, sizeof(displayStrings));
}
BaseMenuGui::~BaseMenuGui() {
delete this->context; // delete handles nullptr automatically
}
// Fast preDraw - just renders pre-computed strings
void BaseMenuGui::preDraw(tsl::gfx::Renderer* renderer) {
BaseGui::preDraw(renderer);
if(!this->context) [[unlikely]] return;
// All constants pre-calculated and cached
static constexpr const char* const labels[10] = {
"App ID", "Profile", "CPU", "GPU", "MEM", "SOC", "PCB", "Skin", "Now", "Avg"
};
static constexpr u32 dataPositions[6] = {63-3+3, 200-1, 344-1-3, 200-1, 342-1, 321-1};
static u32 labelWidths[10];
static bool positionsInitialized = false;
if (!positionsInitialized) {
for (int i = 0; i < 10; i++) {
labelWidths[i] = renderer->getTextDimensions(labels[i], false, SMALL_TEXT_SIZE).first;
}
positionsInitialized = true;
}
static u32 positions[10] = {24-1, 310-labelWidths[1], 24-1, 192-labelWidths[3], 332-labelWidths[4], 24-1, 192 - labelWidths[6], 332-labelWidths[7], 192 - labelWidths[8], 332-labelWidths[9]};
static u32 maxProfileValueWidth = renderer->getTextDimensions("Official Charger", false, SMALL_TEXT_SIZE).first; // longest word
u32 y = 91;
// === TOP SECTION ===
renderer->drawRoundedRect(14, 70-1, 420, 30+2, 10.0f, renderer->aWithOpacity(tsl::tableBGColor));
// App ID - use pre-formatted string
renderer->drawString(labels[0], false, positions[0], y, SMALL_TEXT_SIZE, tsl::sectionTextColor);
renderer->drawString(displayStrings[0], false, positions[0] + labelWidths[0] + 9, y, SMALL_TEXT_SIZE, tsl::infoTextColor);
// Profile - use pre-formatted string
renderer->drawString(labels[1], false, 423 - maxProfileValueWidth - labelWidths[1] - 9, y, SMALL_TEXT_SIZE, tsl::sectionTextColor);
renderer->drawString(displayStrings[1], false, 423 - maxProfileValueWidth, y, SMALL_TEXT_SIZE, tsl::infoTextColor);
y = 129; // Direct assignment instead of += 38
// === MAIN DATA SECTION ===
renderer->drawRoundedRect(14, 106, 420, 116, 10.0f, renderer->aWithOpacity(tsl::tableBGColor));
// === FREQUENCY SECTION ===
// Labels first (better cache locality)
renderer->drawString(labels[2], false, positions[2], y, SMALL_TEXT_SIZE, tsl::sectionTextColor);
renderer->drawString(labels[3], false, positions[3], y, SMALL_TEXT_SIZE, tsl::sectionTextColor);
renderer->drawString(labels[4], false, positions[4], y, SMALL_TEXT_SIZE, tsl::sectionTextColor);
// Current frequencies - use pre-formatted strings
renderer->drawString(displayStrings[2], false, dataPositions[0], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // CPU
renderer->drawString(displayStrings[3], false, dataPositions[1], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // GPU
renderer->drawString(displayStrings[4], false, dataPositions[2], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // MEM
y = 149; // Direct assignment (129 + 20)
// === REAL FREQUENCIES ===
renderer->drawString(displayStrings[5], false, dataPositions[0], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // CPU real
renderer->drawString(displayStrings[6], false, dataPositions[1], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // GPU real
renderer->drawString(displayStrings[7], false, dataPositions[2], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // MEM real
y = 169; // Direct assignment (149 + 20)
// === VOLTAGES ===
renderer->drawString(displayStrings[8], false, dataPositions[0], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // CPU voltage
renderer->drawString(displayStrings[9], false, dataPositions[1], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // GPU voltage
// Memory voltage - check if VDD is present
if (emcVoltageUv && vddVoltageUv) {
renderer->drawStringWithColoredSections(displayStrings[10], false, {""}, dataPositions[5]-16, y, SMALL_TEXT_SIZE, tsl::infoTextColor, tsl::separatorColor);
} else if (vddVoltageUv) {
renderer->drawString(displayStrings[10], false, dataPositions[2], y, SMALL_TEXT_SIZE, tsl::infoTextColor);
} else if (emcVoltageUv) {
renderer->drawString(displayStrings[10], false, dataPositions[2], y, SMALL_TEXT_SIZE, tsl::infoTextColor);
}
y = 191; // Direct assignment (169 + 22)
// === TEMPERATURE SECTION ===
// Labels
renderer->drawString(labels[5], false, positions[5], y, SMALL_TEXT_SIZE, tsl::sectionTextColor);
renderer->drawString(labels[6], false, positions[6]-1, y, SMALL_TEXT_SIZE, tsl::sectionTextColor);
renderer->drawString(labels[7], false, positions[7], y, SMALL_TEXT_SIZE, tsl::sectionTextColor);
// Temperatures with color - use pre-computed colors
renderer->drawString(displayStrings[11], false, dataPositions[0], y, SMALL_TEXT_SIZE, tempColors[0]); // SOC
renderer->drawString(displayStrings[12], false, dataPositions[1], y, SMALL_TEXT_SIZE, tempColors[1]); // PCB
renderer->drawString(displayStrings[13], false, dataPositions[2], y, SMALL_TEXT_SIZE, tempColors[2]); // Skin
y = 211; // Direct assignment (191 + 20)
// === SOC VOLTAGE & POWER ===
// SOC voltage (if available)
if (socVoltageUv) [[likely]] {
renderer->drawString(displayStrings[14], false, dataPositions[0], y, SMALL_TEXT_SIZE, tsl::infoTextColor);
}
// Power labels and values
renderer->drawString(labels[8], false, positions[8]-1, y, SMALL_TEXT_SIZE, tsl::sectionTextColor);
renderer->drawString(labels[9], false, positions[9], y, SMALL_TEXT_SIZE, tsl::sectionTextColor);
renderer->drawString(displayStrings[15], false, dataPositions[3], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // Power now
renderer->drawString(displayStrings[16], false, dataPositions[4], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // Power avg
}
// Optimized refresh - now does all the string formatting once per second
void BaseMenuGui::refresh()
{
const u64 ticks = armGetSystemTick();
// Use cached comparison - 1 billion nanoseconds
if (armTicksToNs(ticks - this->lastContextUpdate) <= 1000000000UL) [[likely]] {
return; // Early exit for most calls
}
this->lastContextUpdate = ticks;
// Lazy context allocation
if (!this->context) [[unlikely]] {
this->context = new SysClkContext;
}
// === ULTRA-FAST VOLTAGE READING ===
// Pre-computed domain configuration based on hardware
static const PowerDomainId domains[] = {
PcvPowerDomainId_Max77621_Cpu, // [0] CPU
PcvPowerDomainId_Max77621_Gpu, // [1] GPU
PcvPowerDomainId_Max77812_Dram, // [2] EMC/DRAM - Mariko only
PcvPowerDomainId_Max77620_Sd0, // [3] SOC - EOS only
PcvPowerDomainId_Max77620_Sd1 // [4] VDD2 - EOS only
};
// Voltage array for direct indexing
u32* voltages[] = {&cpuVoltageUv, &gpuVoltageUv, &emcVoltageUv, &socVoltageUv, &vddVoltageUv};
// Single regulator init/exit cycle
if (R_SUCCEEDED(rgltrInitialize())) [[likely]] {
if (IsMariko()) {
// Mariko with EOS: all 5 domains
for (int i = 0; i < 5; ++i) {
RgltrSession session;
if (R_SUCCEEDED(rgltrOpenSession(&session, domains[i]))) [[likely]] {
if (R_FAILED(rgltrGetVoltage(&session, voltages[i]))) {
*voltages[i] = 0;
}
rgltrCloseSession(&session);
} else {
*voltages[i] = 0;
}
}
} else {
// Erista
// Erista with EOS: CPU, GPU, SOC, VDD (no DRAM)
for (int i = 0; i < 5; ++i) {
if (i == 2) continue; // Skip DRAM domain
RgltrSession session;
if (R_SUCCEEDED(rgltrOpenSession(&session, domains[i]))) [[likely]] {
if (R_FAILED(rgltrGetVoltage(&session, voltages[i]))) {
*voltages[i] = 0;
}
rgltrCloseSession(&session);
} else {
*voltages[i] = 0;
}
emcVoltageUv = 0; // Erista never supports DRAM
}
}
rgltrExit();
} else {
// Zero all voltages on regulator failure
memset(&cpuVoltageUv, 0, sizeof(u32) * 5);
}
// === SYSCLK CONTEXT UPDATE ===
const Result rc = sysclkIpcGetCurrentContext(this->context);
if (R_FAILED(rc)) [[unlikely]] {
FatalGui::openWithResultCode("sysclkIpcGetCurrentContext", rc);
return;
}
// === FORMAT ALL DISPLAY STRINGS (once per second) ===
// App ID (hex conversion)
sprintf(displayStrings[0], "%016lX", context->applicationId);
// Profile
strcpy(displayStrings[1], sysclkFormatProfile(context->profile, true));
// Current frequencies
u32 hz = context->freqs[0]; // CPU
sprintf(displayStrings[2], "%u.%u MHz", hz / 1000000U, (hz / 100000U) % 10U);
hz = context->freqs[1]; // GPU
sprintf(displayStrings[3], "%u.%u MHz", hz / 1000000U, (hz / 100000U) % 10U);
hz = context->freqs[2]; // MEM
sprintf(displayStrings[4], "%u.%u MHz", hz / 1000000U, (hz / 100000U) % 10U);
// Real frequencies
hz = context->realFreqs[0]; // CPU
sprintf(displayStrings[5], "%u.%u MHz", hz / 1000000U, (hz / 100000U) % 10U);
hz = context->realFreqs[1]; // GPU
sprintf(displayStrings[6], "%u.%u MHz", hz / 1000000U, (hz / 100000U) % 10U);
hz = context->realFreqs[2]; // MEM
sprintf(displayStrings[7], "%u.%u MHz", hz / 1000000U, (hz / 100000U) % 10U);
// Voltages
sprintf(displayStrings[8], "%u mV", cpuVoltageUv / 1000U);
sprintf(displayStrings[9], "%u mV", gpuVoltageUv / 1000U);
// Memory voltage (handle VDD case)
if (emcVoltageUv && vddVoltageUv) {
//sprintf(displayStrings[10], "%u%u mV", vddVoltageUv / 1000U, emcVoltageUv / 1000U);
//sprintf(displayStrings[10], "%u%.1f mV", vddVoltageUv / 1000U, emcVoltageUv / 1000.0f);
sprintf(displayStrings[10], "%u.%u%u mV", vddVoltageUv / 1000U, (vddVoltageUv % 1000U) / 100U, emcVoltageUv / 1000U);
} else if (vddVoltageUv) {
//sprintf(displayStrings[10], "%u mV", vddVoltageUv / 1000U);
sprintf(displayStrings[10], "%u.%u mV", vddVoltageUv / 1000U, (vddVoltageUv % 1000U) / 100U);
} else if (emcVoltageUv) {
sprintf(displayStrings[10], "%u mV", emcVoltageUv / 1000U);
}
// Temperatures and pre-compute colors
u32 millis = context->temps[0]; // SOC
sprintf(displayStrings[11], "%u.%u °C", millis / 1000U, (millis % 1000U) / 100U);
tempColors[0] = tsl::GradientColor(millis * 0.001f);
millis = context->temps[1]; // PCB
sprintf(displayStrings[12], "%u.%u °C", millis / 1000U, (millis % 1000U) / 100U);
tempColors[1] = tsl::GradientColor(millis * 0.001f);
millis = context->temps[2]; // Skin
sprintf(displayStrings[13], "%u.%u °C", millis / 1000U, (millis % 1000U) / 100U);
tempColors[2] = tsl::GradientColor(millis * 0.001f);
// SOC voltage (if available)
if (socVoltageUv) {
sprintf(displayStrings[14], "%u mV", socVoltageUv / 1000U);
}
// Power
sprintf(displayStrings[15], "%d mW", context->power[0]); // Now
sprintf(displayStrings[16], "%d mW", context->power[1]); // Avg
}
tsl::elm::Element* BaseMenuGui::baseUI()
{
auto* list = new tsl::elm::List();
this->listElement = list;
this->listUI();
return list;
}

View File

@@ -0,0 +1,59 @@
/*
* --------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
* wrote this file. As long as you retain this notice you can do whatever you
* want with this stuff. If you meet any of us some day, and you think this
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
* --------------------------------------------------------------------------
*/
#pragma once
#include "../../rgltr_services.h"
#include "../../ipc.h"
#include "base_gui.h"
class BaseMenuGui : public BaseGui
{
protected:
SysClkContext* context;
std::uint64_t lastContextUpdate;
std::uint32_t cpuVoltageUv;
std::uint32_t gpuVoltageUv;
std::uint32_t emcVoltageUv;
std::uint32_t socVoltageUv; //add soc voltage
std::uint32_t vddVoltageUv;//add vdd2 voltage
public:
bool g_hardwareModelCached = false;
bool g_isMariko = false;
bool IsMariko() {
if (!g_hardwareModelCached) {
SetSysProductModel model = SetSysProductModel_Invalid;
setsysGetProductModel(&model);
g_isMariko = (model == SetSysProductModel_Iowa ||
model == SetSysProductModel_Hoag ||
model == SetSysProductModel_Calcio ||
model == SetSysProductModel_Aula);
g_hardwareModelCached = true;
}
return g_isMariko;
}
bool IsErista() {
return !IsMariko();
}
BaseMenuGui();
~BaseMenuGui();
void preDraw(tsl::gfx::Renderer* renderer) override;
tsl::elm::List* listElement;
tsl::elm::Element* baseUI() override;
void refresh() override;
virtual void listUI() = 0;
private:
char displayStrings[17][32]; // Pre-formatted display strings
tsl::Color tempColors[3]; // Pre-computed temperature colors
};

View File

@@ -0,0 +1,67 @@
/*
* --------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
* wrote this file. As long as you retain this notice you can do whatever you
* want with this stuff. If you meet any of us some day, and you think this
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
* --------------------------------------------------------------------------
*/
#include "fatal_gui.h"
FatalGui::FatalGui(const std::string message, const std::string info)
{
this->message = message;
this->info = info;
}
void FatalGui::openWithResultCode(std::string tag, Result rc)
{
char rcStr[32];
std::string info = tag;
info.append(rcStr, snprintf(rcStr, sizeof(rcStr), "\n\n[0x%x] %04d-%04d", rc, R_MODULE(rc), R_DESCRIPTION(rc)));
tsl::changeTo<FatalGui>(
"Could not connect to sys-clk.\n\n"
"\n"
"Please make sure everything is\n\n"
"correctly installed and enabled.",
info
);
}
tsl::elm::Element* FatalGui::baseUI()
{
tsl::elm::CustomDrawer* drawer = new tsl::elm::CustomDrawer([this](tsl::gfx::Renderer* renderer, u16 x, u16 y, u16 w, u16 h) {
renderer->drawString("\uE150", false, 40, 210, 40, TEXT_COLOR);
renderer->drawString("Fatal error", false, 100, 210, 30, TEXT_COLOR);
std::uint32_t txtY = 255;
if(!this->message.empty())
{
txtY += renderer->drawString(this->message.c_str(), false, 40, txtY, 23, TEXT_COLOR).second;
txtY += 55;
}
if(!this->info.empty())
{
renderer->drawString(this->info.c_str(), false, 40, txtY, 18, DESC_COLOR);
}
});
return drawer;
}
bool FatalGui::handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos, HidAnalogStickState joyStickPosLeft, HidAnalogStickState joyStickPosRight)
{
if((keysDown & HidNpadButton_A) == HidNpadButton_A || (keysDown & HidNpadButton_B) == HidNpadButton_B)
{
while(tsl::Overlay::get()->getCurrentGui() != nullptr) {
tsl::goBack();
}
return true;
}
return false;
}

View File

@@ -0,0 +1,29 @@
/*
* --------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
* wrote this file. As long as you retain this notice you can do whatever you
* want with this stuff. If you meet any of us some day, and you think this
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
* --------------------------------------------------------------------------
*/
#pragma once
#include <list>
#include "base_gui.h"
class FatalGui : public BaseGui
{
protected:
std::string message;
std::string info;
public:
FatalGui(const std::string message, const std::string info);
~FatalGui() {}
tsl::elm::Element* baseUI() override;
bool handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos, HidAnalogStickState joyStickPosLeft, HidAnalogStickState joyStickPosRight);
static void openWithResultCode(std::string tag, Result rc);
};

View File

@@ -0,0 +1,67 @@
/*
* --------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
* wrote this file. As long as you retain this notice you can do whatever you
* want with this stuff. If you meet any of us some day, and you think this
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
* --------------------------------------------------------------------------
*/
#include "freq_choice_gui.h"
#include "../format.h"
#include "fatal_gui.h"
FreqChoiceGui::FreqChoiceGui(std::uint32_t selectedHz, std::uint32_t* hzList, std::uint32_t hzCount, SysClkModule module, FreqChoiceListener listener)
{
this->selectedHz = selectedHz;
this->hzList = hzList;
this->hzCount = hzCount;
this->module = module; // Add this
this->listener = listener;
}
tsl::elm::ListItem* FreqChoiceGui::createFreqListItem(std::uint32_t hz, bool selected)
{
tsl::elm::ListItem* listItem = new tsl::elm::ListItem(formatListFreqHz(hz), "", true);
listItem->setValue(selected ? "\uE14B" : "");
listItem->setClickListener([this, hz](u64 keys) {
if((keys & HidNpadButton_A) == HidNpadButton_A && this->listener)
{
if(this->listener(hz))
{
tsl::goBack();
}
return true;
}
return false;
});
return listItem;
}
void FreqChoiceGui::listUI()
{
// Add CategoryHeader based on module
std::string moduleName = sysclkFormatModule(this->module, true);
this->listElement->addItem(new tsl::elm::CategoryHeader(moduleName));
this->listElement->addItem(this->createFreqListItem(0, this->selectedHz == 0));
std::uint32_t hz;
for(std::uint32_t i = 0; i < this->hzCount; i++) {
hz = this->hzList[i];
// Skip 204 MHz exactly
if(moduleName == "Memory" && hz == 204000000) {
continue;
}
// if(moduleName == "GPU") {
// if(BaseGui::isMariko() && is)
// continue;
// }
this->listElement->addItem(this->createFreqListItem(hz, (hz / 1000000) == (this->selectedHz / 1000000)));
}
this->listElement->jumpToItem("", "");
}

View File

@@ -0,0 +1,35 @@
/*
* --------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
* wrote this file. As long as you retain this notice you can do whatever you
* want with this stuff. If you meet any of us some day, and you think this
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
* --------------------------------------------------------------------------
*/
#pragma once
#include <list>
#include "base_menu_gui.h"
using FreqChoiceListener = std::function<bool(std::uint32_t hz)>;
#define FREQ_DEFAULT_TEXT "Do not override"
class FreqChoiceGui : public BaseMenuGui
{
protected:
std::uint32_t selectedHz;
std::uint32_t* hzList;
std::uint32_t hzCount;
SysClkModule module; // Added
FreqChoiceListener listener;
tsl::elm::ListItem* createFreqListItem(std::uint32_t hz, bool selected);
public:
FreqChoiceGui(std::uint32_t selectedHz, std::uint32_t* hzList, std::uint32_t hzCount, SysClkModule module, FreqChoiceListener listener);
~FreqChoiceGui() {}
void listUI() override;
};

View File

@@ -0,0 +1,108 @@
/*
* --------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
* wrote this file. As long as you retain this notice you can do whatever you
* want with this stuff. If you meet any of us some day, and you think this
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
* --------------------------------------------------------------------------
*/
#include "global_override_gui.h"
#include "fatal_gui.h"
#include "../format.h"
GlobalOverrideGui::GlobalOverrideGui()
{
for(std::uint16_t m = 0; m < SysClkModule_EnumMax; m++)
{
this->listItems[m] = nullptr;
this->listHz[m] = 0;
}
}
void GlobalOverrideGui::openFreqChoiceGui(SysClkModule module)
{
std::uint32_t hzList[SYSCLK_FREQ_LIST_MAX];
std::uint32_t hzCount;
Result rc = sysclkIpcGetFreqList(module, &hzList[0], SYSCLK_FREQ_LIST_MAX, &hzCount);
if(R_FAILED(rc))
{
FatalGui::openWithResultCode("sysclkIpcGetFreqList", rc);
return;
}
tsl::changeTo<FreqChoiceGui>(this->context->overrideFreqs[module], hzList, hzCount, module, [this, module](std::uint32_t hz) {
Result rc = sysclkIpcSetOverride(module, hz);
if(R_FAILED(rc))
{
FatalGui::openWithResultCode("sysclkIpcSetOverride", rc);
return false;
}
this->lastContextUpdate = armGetSystemTick();
this->context->overrideFreqs[module] = hz;
return true;
});
}
void GlobalOverrideGui::addModuleListItem(SysClkModule module)
{
tsl::elm::ListItem* listItem = new tsl::elm::ListItem(sysclkFormatModule(module, true));
listItem->setValue(formatListFreqMHz(0));
listItem->setClickListener([this, module](u64 keys) {
if((keys & HidNpadButton_A) == HidNpadButton_A)
{
this->openFreqChoiceGui(module);
return true;
}
else if((keys & HidNpadButton_Y) == HidNpadButton_Y)
{
// Reset override to "Do not override" (0 Hz)
Result rc = sysclkIpcSetOverride(module, 0);
if(R_FAILED(rc))
{
FatalGui::openWithResultCode("sysclkIpcSetOverride", rc);
return false;
}
// Update context and tracking variables
this->lastContextUpdate = armGetSystemTick();
this->context->overrideFreqs[module] = 0;
this->listHz[module] = 0;
// Update display
this->listItems[module]->setValue(formatListFreqHz(0));
return true;
}
return false;
});
this->listElement->addItem(listItem);
this->listItems[module] = listItem;
}
void GlobalOverrideGui::listUI()
{
this->listElement->addItem(new tsl::elm::CategoryHeader("Temporary Overrides " + ult::DIVIDER_SYMBOL + "  Reset"));
this->addModuleListItem(SysClkModule_CPU);
this->addModuleListItem(SysClkModule_GPU);
this->addModuleListItem(SysClkModule_MEM);
}
void GlobalOverrideGui::refresh()
{
BaseMenuGui::refresh();
if(this->context)
{
for(std::uint16_t m = 0; m < SysClkModule_EnumMax; m++)
{
if(this->listItems[m] != nullptr && this->listHz[m] != this->context->overrideFreqs[m])
{
this->listItems[m]->setValue(formatListFreqHz(this->context->overrideFreqs[m]));
this->listHz[m] = this->context->overrideFreqs[m];
}
}
}
}

View File

@@ -0,0 +1,31 @@
/*
* --------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
* wrote this file. As long as you retain this notice you can do whatever you
* want with this stuff. If you meet any of us some day, and you think this
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
* --------------------------------------------------------------------------
*/
#pragma once
#include "../../ipc.h"
#include "base_menu_gui.h"
#include "freq_choice_gui.h"
class GlobalOverrideGui : public BaseMenuGui
{
protected:
tsl::elm::ListItem* listItems[SysClkModule_EnumMax];
std::uint32_t listHz[SysClkModule_EnumMax];
void openFreqChoiceGui(SysClkModule module);
void addModuleListItem(SysClkModule module);
public:
GlobalOverrideGui();
~GlobalOverrideGui() {}
void listUI() override;
void refresh() override;
};

View File

@@ -0,0 +1,93 @@
/*
* --------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
* wrote this file. As long as you retain this notice you can do whatever you
* want with this stuff. If you meet any of us some day, and you think this
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
* --------------------------------------------------------------------------
*/
#include "main_gui.h"
#include "fatal_gui.h"
#include "app_profile_gui.h"
#include "global_override_gui.h"
#include "misc_gui.h"
void MainGui::listUI()
{
this->enabledToggle = new tsl::elm::ToggleListItem("Enable", false);
enabledToggle->setStateChangedListener([this](bool state) {
Result rc = sysclkIpcSetEnabled(state);
if(R_FAILED(rc))
{
FatalGui::openWithResultCode("sysclkIpcSetEnabled", rc);
}
this->lastContextUpdate = armGetSystemTick();
this->context->enabled = state;
});
this->listElement->addItem(this->enabledToggle);
tsl::elm::ListItem* appProfileItem = new tsl::elm::ListItem("Edit App Profile");
appProfileItem->setClickListener([this](u64 keys) {
if((keys & HidNpadButton_A) == HidNpadButton_A && this->context)
{
AppProfileGui::changeTo(this->context->applicationId);
return true;
}
return false;
});
this->listElement->addItem(appProfileItem);
this->listElement->addItem(new tsl::elm::CategoryHeader("Advanced"));
tsl::elm::ListItem* globalProfileItem = new tsl::elm::ListItem("Edit Global Profile");
globalProfileItem->setClickListener([this](u64 keys) {
if((keys & HidNpadButton_A) == HidNpadButton_A && this->context)
{
AppProfileGui::changeTo(SYSCLK_GLOBAL_PROFILE_TID);
return true;
}
return false;
});
this->listElement->addItem(globalProfileItem);
tsl::elm::ListItem* globalOverrideItem = new tsl::elm::ListItem("Temporary Overrides");
globalOverrideItem->setClickListener([this](u64 keys) {
if((keys & HidNpadButton_A) == HidNpadButton_A)
{
tsl::changeTo<GlobalOverrideGui>();
return true;
}
return false;
});
this->listElement->addItem(globalOverrideItem);
//this->listElement->addItem(new tsl::elm::CategoryHeader("Misc"));
tsl::elm::ListItem* miscItem = new tsl::elm::ListItem("Settings");
miscItem->setClickListener([this](u64 keys) {
if((keys & HidNpadButton_A) == HidNpadButton_A && this->context)
{
tsl::changeTo<MiscGui>();
return true;
}
return false;
});
this->listElement->addItem(miscItem);
}
void MainGui::refresh()
{
BaseMenuGui::refresh();
//if(this->context)
//{
// this->enabledToggle->setState(this->context->enabled);
//}
}

View File

@@ -0,0 +1,25 @@
/*
* --------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
* wrote this file. As long as you retain this notice you can do whatever you
* want with this stuff. If you meet any of us some day, and you think this
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
* --------------------------------------------------------------------------
*/
#pragma once
#include "base_menu_gui.h"
class MainGui : public BaseMenuGui
{
protected:
tsl::elm::ToggleListItem* enabledToggle;
public:
MainGui() {}
~MainGui() {}
void listUI() override;
void refresh() override;
};

View File

@@ -0,0 +1,444 @@
#include "misc_gui.h"
#include "fatal_gui.h"
#include "../format.h"
#include <cstdio>
#include <cstring>
//#include <sstream>
MiscGui::MiscGui()
{
// Load current config values
configValues["uncapped_clocks"] = getConfigValue("uncapped_clocks");
configValues["override_boost_mode"] = getConfigValue("override_boost_mode");
configValues["auto_cpu_boost"] = getConfigValue("auto_cpu_boost");
configValues["sync_reversenx"] = getConfigValue("sync_reversenx");
// gpu_dvfs is handled separately as it's now a trackbar with integer values
}
MiscGui::~MiscGui()
{
this->configToggles.clear();
}
bool MiscGui::getConfigValue(const std::string& iniKey)
{
FILE* file = fopen("/config/sys-clk/config.ini", "r");
if (!file) {
// Return default values if file doesn't exist
return (iniKey == "gpu_dvfs"); // gpu_dvfs defaults to true, others default to false
}
char line[512];
bool inValuesSection = false;
size_t len;
while (fgets(line, sizeof(line), file)) {
// Remove newline if present
len = strlen(line);
if (len > 0 && line[len - 1] == '\n') {
line[len - 1] = '\0';
}
// Trim whitespace
char* start = line;
while (*start == ' ' || *start == '\t') start++;
char* end = start + strlen(start) - 1;
while (end > start && (*end == ' ' || *end == '\t')) {
*end = '\0';
end--;
}
// Check for [values] section
if (strcmp(start, "[values]") == 0) {
inValuesSection = true;
continue;
}
// Check for new section
if (strlen(start) > 0 && start[0] == '[') {
inValuesSection = false;
continue;
}
// Parse key=value in values section
if (inValuesSection) {
char* equalPos = strchr(start, '=');
if (equalPos != nullptr) {
*equalPos = '\0'; // Split the string
char* key = start;
char* value = equalPos + 1;
if(iniKey == "uncapped_clocks") {
Result rc = sysclkIpcSetEnabled(value);
}
// Trim key
char* keyEnd = key + strlen(key) - 1;
while (keyEnd > key && (*keyEnd == ' ' || *keyEnd == '\t')) {
*keyEnd = '\0';
keyEnd--;
}
// Trim value
while (*value == ' ' || *value == '\t') value++;
char* valueEnd = value + strlen(value) - 1;
while (valueEnd > value && (*valueEnd == ' ' || *valueEnd == '\t')) {
*valueEnd = '\0';
valueEnd--;
}
if (iniKey == key) {
bool result = (strcmp(value, "1") == 0);
fclose(file);
return result;
}
}
}
}
fclose(file);
// Return default values if key not found
return (iniKey == "gpu_dvfs"); // gpu_dvfs defaults to true, others default to false
}
int MiscGui::getConfigIntValue(const std::string& iniKey, int defaultValue)
{
FILE* file = fopen("/config/sys-clk/config.ini", "r");
if (!file) {
return defaultValue;
}
char line[512];
bool inValuesSection = false;
size_t len;
while (fgets(line, sizeof(line), file)) {
// Remove newline if present
len = strlen(line);
if (len > 0 && line[len - 1] == '\n') {
line[len - 1] = '\0';
}
// Trim whitespace
char* start = line;
while (*start == ' ' || *start == '\t') start++;
char* end = start + strlen(start) - 1;
while (end > start && (*end == ' ' || *end == '\t')) {
*end = '\0';
end--;
}
// Check for [values] section
if (strcmp(start, "[values]") == 0) {
inValuesSection = true;
continue;
}
// Check for new section
if (strlen(start) > 0 && start[0] == '[') {
inValuesSection = false;
continue;
}
// Parse key=value in values section
if (inValuesSection) {
char* equalPos = strchr(start, '=');
if (equalPos != nullptr) {
*equalPos = '\0'; // Split the string
char* key = start;
char* value = equalPos + 1;
// Trim key
char* keyEnd = key + strlen(key) - 1;
while (keyEnd > key && (*keyEnd == ' ' || *keyEnd == '\t')) {
*keyEnd = '\0';
keyEnd--;
}
// Trim value
while (*value == ' ' || *value == '\t') value++;
char* valueEnd = value + strlen(value) - 1;
while (valueEnd > value && (*valueEnd == ' ' || *valueEnd == '\t')) {
*valueEnd = '\0';
valueEnd--;
}
if (iniKey == key) {
int result = atoi(value);
fclose(file);
return result;
}
}
}
}
fclose(file);
return defaultValue;
}
SysClkIpcToggleUncappedClocks(SysClkModule_CPU, g_freq_table_hz[SysClkModule_CPU][result])
void MiscGui::setConfigValue(const std::string& iniKey, bool value)
{
if(iniKey == "uncapped_clocks") {
Result rc = sysclkIpcSetEnabled(value);
}
// Read the entire file
FILE* file = fopen("/config/sys-clk/config.ini", "r");
std::vector<std::string> lines;
if (file) {
char line[512];
size_t len;
while (fgets(line, sizeof(line), file)) {
// Remove newline if present
len = strlen(line);
if (len > 0 && line[len - 1] == '\n') {
line[len - 1] = '\0';
}
lines.push_back(std::string(line));
}
fclose(file);
}
// Find and update the value
bool inValuesSection = false;
bool keyFound = false;
int valuesSectionIndex = -1;
std::string trimmedLine;
size_t equalPos;
std::string key;
for (size_t i = 0; i < lines.size(); i++) {
trimmedLine = lines[i];
trimmedLine.erase(0, trimmedLine.find_first_not_of(" \t"));
trimmedLine.erase(trimmedLine.find_last_not_of(" \t") + 1);
if (trimmedLine == "[values]") {
inValuesSection = true;
valuesSectionIndex = i;
continue;
}
if (trimmedLine.length() > 0 && trimmedLine[0] == '[') {
inValuesSection = false;
continue;
}
if (inValuesSection) {
equalPos = trimmedLine.find('=');
if (equalPos != std::string::npos) {
key = trimmedLine.substr(0, equalPos);
key.erase(0, key.find_first_not_of(" \t"));
key.erase(key.find_last_not_of(" \t") + 1);
if (key == iniKey) {
lines[i] = iniKey + "=" + (value ? "1" : "0");
keyFound = true;
break;
}
}
}
}
// If key wasn't found, add it to the values section
if (!keyFound) {
if (valuesSectionIndex == -1) {
// Add [values] section if it doesn't exist
lines.push_back("[values]");
lines.push_back(iniKey + "=" + (value ? "1" : "0"));
} else {
// Add to existing values section
lines.insert(lines.begin() + valuesSectionIndex + 1, iniKey + "=" + (value ? "1" : "0"));
}
}
// Write the file back
FILE* outFile = fopen("/config/sys-clk/config.ini", "w");
if (outFile) {
for (const auto& fileLine : lines) {
fprintf(outFile, "%s\n", fileLine.c_str());
}
fclose(outFile);
}
}
void MiscGui::setConfigIntValue(const std::string& iniKey, int value)
{
// Read the entire file
FILE* file = fopen("/config/sys-clk/config.ini", "r");
std::vector<std::string> lines;
if (file) {
char line[512];
size_t len;
while (fgets(line, sizeof(line), file)) {
// Remove newline if present
len = strlen(line);
if (len > 0 && line[len - 1] == '\n') {
line[len - 1] = '\0';
}
lines.push_back(std::string(line));
}
fclose(file);
}
// Find and update the value
bool inValuesSection = false;
bool keyFound = false;
int valuesSectionIndex = -1;
std::string trimmedLine;
size_t equalPos;
std::string key;
for (size_t i = 0; i < lines.size(); i++) {
trimmedLine = lines[i];
trimmedLine.erase(0, trimmedLine.find_first_not_of(" \t"));
trimmedLine.erase(trimmedLine.find_last_not_of(" \t") + 1);
if (trimmedLine == "[values]") {
inValuesSection = true;
valuesSectionIndex = i;
continue;
}
if (trimmedLine.length() > 0 && trimmedLine[0] == '[') {
inValuesSection = false;
continue;
}
if (inValuesSection) {
equalPos = trimmedLine.find('=');
if (equalPos != std::string::npos) {
key = trimmedLine.substr(0, equalPos);
key.erase(0, key.find_first_not_of(" \t"));
key.erase(key.find_last_not_of(" \t") + 1);
if (key == iniKey) {
lines[i] = iniKey + "=" + std::to_string(value);
keyFound = true;
break;
}
}
}
}
// If key wasn't found, add it to the values section
if (!keyFound) {
if (valuesSectionIndex == -1) {
// Add [values] section if it doesn't exist
lines.push_back("[values]");
lines.push_back(iniKey + "=" + std::to_string(value));
} else {
// Add to existing values section
lines.insert(lines.begin() + valuesSectionIndex + 1, iniKey + "=" + std::to_string(value));
}
}
// Write the file back
FILE* outFile = fopen("/config/sys-clk/config.ini", "w");
if (outFile) {
for (const auto& fileLine : lines) {
fprintf(outFile, "%s\n", fileLine.c_str());
}
fclose(outFile);
}
}
void MiscGui::addConfigToggle(const std::string& iniKey, const char* displayName) {
tsl::elm::ToggleListItem* toggle = new tsl::elm::ToggleListItem(displayName, configValues[iniKey]);
toggle->setStateChangedListener([this, iniKey](bool state) {
configValues[iniKey] = state;
setConfigValue(iniKey, state);
this->lastContextUpdate = armGetSystemTick();
});
this->listElement->addItem(toggle);
this->configToggles[iniKey] = toggle;
}
void MiscGui::updateConfigToggles() {
for (const auto& [key, toggle] : this->configToggles) {
if (toggle != nullptr) {
bool currentValue = getConfigValue(key);
configValues[key] = currentValue;
toggle->setState(currentValue);
}
}
}
void MiscGui::listUI()
{
this->listElement->addItem(new tsl::elm::CategoryHeader("Settings"));
this->enabledToggle = new tsl::elm::ToggleListItem("Enable", false);
enabledToggle->setStateChangedListener([this](bool state) {
Result rc = sysclkIpcSetEnabled(state);
if(R_FAILED(rc))
{
FatalGui::openWithResultCode("sysclkIpcSetEnabled", rc);
}
this->lastContextUpdate = armGetSystemTick();
this->context->enabled = state;
});
this->listElement->addItem(this->enabledToggle);
// Add the 4 boolean config toggles using INI keys
addConfigToggle("uncapped_clocks", "Uncapped Clocks");
addConfigToggle("override_boost_mode", "Override Boost Mode");
addConfigToggle("auto_cpu_boost", "Auto CPU Boost");
addConfigToggle("reversenx_sync", "Sync ReverseNX");
// Add GPU DVFS as a NamedStepTrackBar with V2 style
this->gpuDvfsTrackbar = new tsl::elm::NamedStepTrackBar("", {
"Off",
"Official Service Method",
"Hijack Method"
}, true, "GPU DVFS");
// Set initial value (default is 0 if not set)
int currentDvfsValue = getConfigIntValue("gpu_dvfs", 1);
// Ensure the value is within valid range (0-2)
currentDvfsValue = std::max(0, std::min(2, currentDvfsValue));
this->gpuDvfsTrackbar->setProgress(static_cast<u8>(currentDvfsValue));
// Set up the value change listener to update the INI file
this->gpuDvfsTrackbar->setValueChangedListener([this](u8 value) {
// Ensure value is within expected range
const int intValue = static_cast<int>(std::min(static_cast<u8>(2), value));
setConfigIntValue("gpu_dvfs", intValue);
this->lastContextUpdate = armGetSystemTick();
});
this->listElement->addItem(this->gpuDvfsTrackbar);
}
void MiscGui::refresh() {
BaseMenuGui::refresh();
// Update the enabled toggle state
if(this->context)
{
this->enabledToggle->setState(this->context->enabled);
}
// Update config values and toggle states every 60 frames (once per second at 60fps)
if (this->context && ++frameCounter >= 60)
{
frameCounter = 0;
updateConfigToggles();
// Update GPU DVFS trackbar
if (this->gpuDvfsTrackbar != nullptr) {
int currentDvfsValue = getConfigIntValue("gpu_dvfs", 1);
// Ensure the value is within valid range (0-2)
currentDvfsValue = std::max(0, std::min(2, currentDvfsValue));
this->gpuDvfsTrackbar->setProgress(static_cast<u8>(currentDvfsValue));
}
}
}

View File

@@ -0,0 +1,30 @@
#pragma once
#include "../../ipc.h"
#include "base_menu_gui.h"
#include <unordered_map>
#include <string>
class MiscGui : public BaseMenuGui
{
public:
MiscGui();
~MiscGui();
void listUI() override;
void refresh() override;
protected:
std::unordered_map<std::string, tsl::elm::ToggleListItem*> configToggles;
std::unordered_map<std::string, bool> configValues;
void addConfigToggle(const std::string& iniKey, const char* displayName);
void updateConfigToggles();
bool getConfigValue(const std::string& iniKey);
void setConfigValue(const std::string& iniKey, bool value);
int getConfigIntValue(const std::string& iniKey, int defaultValue);
void setConfigIntValue(const std::string& iniKey, int value);
tsl::elm::ToggleListItem* enabledToggle;
tsl::elm::NamedStepTrackBar* gpuDvfsTrackbar; // Add this line
u8 frameCounter = 60;
};

View File

@@ -0,0 +1,20 @@
/*
* --------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
* wrote this file. As long as you retain this notice you can do whatever you
* want with this stuff. If you meet any of us some day, and you think this
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
* --------------------------------------------------------------------------
*/
#pragma once
#include <tesla.hpp>
#define TEXT_COLOR tsl::gfx::Renderer::a(0xFFFF)
#define DESC_COLOR tsl::gfx::Renderer::a({ 0xC, 0xC, 0xC, 0xF })
#define VALUE_COLOR tsl::gfx::Renderer::a({ 0x5, 0xC, 0xA, 0xF })
#define SMALL_TEXT_SIZE 15
#define LABEL_SPACING 7
#define LABEL_FONT_SIZE 15