Merge sys-clk-oc and add upto 2903 clocks (please don't use)

I do have to come up with a better name too...
This commit is contained in:
souldbminersmwc
2025-08-22 18:31:38 -04:00
parent 822556e6e4
commit 4738eea080
289 changed files with 50229 additions and 4772 deletions

View File

@@ -0,0 +1,63 @@
/*
sys-clk manager, a sys-clk frontend homebrew
Copyright (C) 2019-2020 natinusala
Copyright (C) 2019 p-sam
Copyright (C) 2019 m4xw
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "about_tab.h"
#include "logo.h"
#include "ipc/ipc.h"
#include "ipc/client.h"
#include <borealis.hpp>
AboutTab::AboutTab()
{
this->addView(new Logo(LogoStyle::ABOUT));
// Subtitle
brls::Label *subTitle = new brls::Label(
brls::LabelStyle::REGULAR,
"Nintendo Switch overclocking / underclocking system module and frontend app by the RetroNX Team",
true
);
subTitle->setHorizontalAlign(NVG_ALIGN_CENTER);
this->addView(subTitle);
// Copyright
brls::Label *copyright = new brls::Label(
brls::LabelStyle::DESCRIPTION,
"System module licensed under the Beerware license\n" \
"Frontend app licensed under GPL-3.0\n" \
"\u00A9 2019 - 2020 natinusala, p-sam, m4xw",
true
);
copyright->setHorizontalAlign(NVG_ALIGN_CENTER);
this->addView(copyright);
// Links
this->addView(new brls::Header("Links and Resources"));
brls::Label *links = new brls::Label(
brls::LabelStyle::SMALL,
"\uE016 User guide and code source can be found on our GitHub repository\n" \
"\uE016 The sys-clk manager is powered by Borealis, an hardware accelerated UI library\n" \
"\uE016 Join the RetroNX Discord server for support, to request features or just hang out!",
true
);
this->addView(links);
}

View File

@@ -0,0 +1,34 @@
/*
sys-clk manager, a sys-clk frontend homebrew
Copyright (C) 2019-2020 natinusala
Copyright (C) 2019 p-sam
Copyright (C) 2019 m4xw
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis.hpp>
class AboutTab : public brls::List
{
public:
AboutTab();
View* getDefaultFocus() override
{
return nullptr;
}
};

View File

@@ -0,0 +1,179 @@
/*
sys-clk manager, a sys-clk frontend homebrew
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
Copyright (C) 2019 m4xw
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "advanced_settings_tab.h"
#include "utils.h"
#include <sysclk.h>
AdvancedSettingsTab::AdvancedSettingsTab()
{
// Get context
SysClkContext context;
Result rc = sysclkIpcGetCurrentContext(&context);
if (R_FAILED(rc))
{
brls::Logger::error("Unable to get context");
errorResult("sysclkIpcGetCurrentContext", rc);
brls::Application::crash("Could not get the current sys-clk context, please check that it is correctly installed and enabled.");
return;
}
// Create UI
// Disclaimer
this->addView(new brls::Label(brls::LabelStyle::REGULAR, "\uE140 Please only alter these settings if you know what you are doing.", true));
// Temporary overrides
this->addView(new brls::Header("Temporary overrides"));
// CPU
brls::SelectListItem *cpuFreqListItem = createFreqListItem(SysClkModule_CPU, context.overrideFreqs[SysClkModule_CPU] / 1000000);
cpuFreqListItem->getValueSelectedEvent()->subscribe([](int result){
Result rc = result == 0 ?
sysclkIpcRemoveOverride(SysClkModule_CPU) :
sysclkIpcSetOverride(SysClkModule_CPU, g_freq_table_hz[SysClkModule_CPU][result]);
if (R_FAILED(rc))
{
brls::Logger::error("Unable to update CPU Override");
errorResult(result == 0 ? "sysclkIpcRemoveOverride" : "sysclkIpcSetOverride", rc);
// TODO: Reset selected value
}
});
// GPU
brls::SelectListItem *gpuFreqListItem = createFreqListItem(SysClkModule_GPU, context.overrideFreqs[SysClkModule_GPU] / 1000000);
gpuFreqListItem->getValueSelectedEvent()->subscribe([](int result){
Result rc = result == 0 ?
sysclkIpcRemoveOverride(SysClkModule_GPU) :
sysclkIpcSetOverride(SysClkModule_GPU, g_freq_table_hz[SysClkModule_GPU][result]);
if (R_FAILED(rc))
{
brls::Logger::error("Unable to update GPU Override");
errorResult(result == 0 ? "sysclkIpcRemoveOverride" : "sysclkIpcSetOverride", rc);
// TODO: Reset selected value
}
});
// MEM
brls::SelectListItem *memFreqListItem = createFreqListItem(SysClkModule_MEM, context.overrideFreqs[SysClkModule_MEM] / 1000000);
memFreqListItem->getValueSelectedEvent()->subscribe([](int result)
{
Result rc = result == 0 ?
sysclkIpcRemoveOverride(SysClkModule_MEM) :
sysclkIpcSetOverride(SysClkModule_MEM, g_freq_table_hz[SysClkModule_MEM][result]);
if (R_FAILED(rc))
{
brls::Logger::error("Unable to update MEM Override");
errorResult(result == 0 ? "sysclkIpcRemoveOverride" : "sysclkIpcSetOverride", rc);
// TODO: Reset selected value
}
});
this->addView(cpuFreqListItem);
this->addView(gpuFreqListItem);
this->addView(memFreqListItem);
// Config
this->addView(new brls::Header("Configuration"));
// Logging
// TODO: add a logger view and put the button to enter it here
// Config entries
// TODO: add constraints to the swkbd if possible (min / max)
sysclkIpcGetConfigValues(&this->configValues);
for (int i = 0; i < SysClkConfigValue_EnumMax; i++)
{
SysClkConfigValue config = (SysClkConfigValue) i;
std::string label = std::string(sysclkFormatConfigValue(config, true));
std::string description = this->getDescriptionForConfig(config);
uint64_t defaultValue = this->configValues.values[config];
brls::IntegerInputListItem* configItem = new brls::IntegerInputListItem(label, defaultValue, label, description);
configItem->setReduceDescriptionSpacing(true);
configItem->getClickEvent()->subscribe([this, configItem, config](View* view)
{
try
{
int value = std::stoi(configItem->getValue());
// Validate the value
if (value < 0)
{
brls::Application::notify("\uE5CD Couldn't save configuration: invalid value (is negative)");
configItem->setValue(std::to_string(this->configValues.values[config]));
return;
}
uint64_t uvalue = (uint64_t) value;
if (!sysclkValidConfigValue(config, uvalue))
{
brls::Application::notify("\uE5CD Couldn't save configuration: invalid value");
configItem->setValue(std::to_string(this->configValues.values[config]));
return;
}
// Save the config
this->configValues.values[config] = uvalue;
sysclkIpcSetConfigValues(&this->configValues);
brls::Application::notify("\uE14B Configuration saved");
}
catch(const std::exception& e)
{
brls::Logger::error("Unable to parse config value %s: %s", configItem->getValue().c_str(), e.what());
}
});
this->addView(configItem);
}
}
std::string AdvancedSettingsTab::getDescriptionForConfig(SysClkConfigValue config)
{
switch (config)
{
case SysClkConfigValue_CsvWriteIntervalMs:
return "How often to update /config/sys-clk/context.csv (in milliseconds)\n\uE016 Use 0 to disable";
case SysClkConfigValue_TempLogIntervalMs:
return "How often to log temperatures (in milliseconds)\n\uE016 Use 0 to disable";
case SysClkConfigValue_FreqLogIntervalMs:
return "How often to log real frequencies (in milliseconds)\n\uE016 Use 0 to disable";
case SysClkConfigValue_PowerLogIntervalMs:
return "How often to log power consumption (in milliseconds)\n\uE016 Use 0 to disable";
case SysClkConfigValue_PollingIntervalMs:
return "How fast to check and apply profiles (in milliseconds)";
default:
return "";
}
}

View File

@@ -0,0 +1,39 @@
/*
sys-clk manager, a sys-clk frontend homebrew
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
Copyright (C) 2019 m4xw
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis.hpp>
#include "ipc/client.h"
class AdvancedSettingsTab : public brls::List
{
public:
AdvancedSettingsTab();
private:
std::string getDescriptionForConfig(SysClkConfigValue config);
bool isLoggingEnabled();
void setLoggingEnabled(bool value);
SysClkConfigValueList configValues;
};

View File

@@ -0,0 +1,166 @@
/*
sys-clk manager, a sys-clk frontend homebrew
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
Copyright (C) 2019 m4xw
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "app_profile_frame.h"
#include <borealis.hpp>
#include "utils.h"
#include "ipc/client.h"
#include <cstring>
AppProfileFrame::AppProfileFrame(Title* title) : ThumbnailFrame(), title(title)
{
this->setTitle("Edit application profile");
this->setIcon(new brls::MaterialIcon("\uE315"));
// Get the freqs
Result rc = sysclkIpcGetProfiles(title->tid, &this->profiles);
if (R_FAILED(rc))
errorResult("sysclkIpcGetProfiles", rc);
// Setup the right sidebar
this->getSidebar()->setThumbnail(title->icon, sizeof(title->icon));
this->getSidebar()->setTitle(std::string(title->name));
this->getSidebar()->setSubtitle(formatTid(title->tid));
this->getSidebar()->getButton()->setState(brls::ButtonState::DISABLED);
this->getSidebar()->getButton()->getClickEvent()->subscribe([this, title](brls::View* view)
{
SysClkTitleProfileList profiles;
memcpy(&profiles, &this->profiles, sizeof(SysClkTitleProfileList));
for (int p = 0; p < SysClkProfile_EnumMax; p++)
{
for (int m = 0; m < SysClkModule_EnumMax; m++)
profiles.mhzMap[p][m] /= 1000000;
}
Result rc = sysclkIpcSetProfiles(title->tid, &profiles);
if (R_SUCCEEDED(rc))
{
// TODO: set the tick mark color to blue/green once borealis has rich text support
brls::Application::notify("\uE14B Profile saved");
brls::Application::popView(brls::ViewAnimation::SLIDE_RIGHT);
}
else
{
errorResult("sysclkIpcSetProfiles", rc);
brls::Application::notify("An error occured while saving the profile - see logs for more details");
}
});
// Setup the list
brls::List* list = new brls::List();
this->addFreqs(list, SysClkProfile_Docked);
this->addFreqs(list, SysClkProfile_Handheld);
this->addFreqs(list, SysClkProfile_HandheldCharging);
this->addFreqs(list, SysClkProfile_HandheldChargingOfficial);
this->addFreqs(list, SysClkProfile_HandheldChargingUSB);
this->setContentView(list);
}
void AppProfileFrame::addFreqs(brls::List* list, SysClkProfile profile)
{
// Get the freqs
list->addView(new brls::Header(std::string(sysclkFormatProfile(profile, true))));
// CPU
brls::SelectListItem* cpuListItem = createFreqListItem(SysClkModule_CPU, this->profiles.mhzMap[profile][SysClkModule_CPU]);
this->profiles.mhzMap[profile][SysClkModule_CPU] *= 1000000;
cpuListItem->getValueSelectedEvent()->subscribe([this, profile](int result) {
this->onProfileChanged();
this->profiles.mhzMap[profile][SysClkModule_CPU] = result == 0 ? result : g_freq_table_hz[SysClkModule_CPU][result];
brls::Logger::debug("Caching freq for module %d and profile %d to %" PRIu32, SysClkModule_CPU, profile, this->profiles.mhzMap[profile][SysClkModule_CPU]);
});
list->addView(cpuListItem);
// GPU
brls::SelectListItem* gpuListItem = createFreqListItem(SysClkModule_GPU, this->profiles.mhzMap[profile][SysClkModule_GPU]);
this->profiles.mhzMap[profile][SysClkModule_GPU] *= 1000000;
gpuListItem->getValueSelectedEvent()->subscribe([this, profile](int result) {
this->onProfileChanged();
this->profiles.mhzMap[profile][SysClkModule_GPU] = result == 0 ? result : g_freq_table_hz[SysClkModule_GPU][result];
brls::Logger::debug("Caching freq for module %d and profile %d to %" PRIu32, SysClkModule_GPU, profile, this->profiles.mhzMap[profile][SysClkModule_GPU]);
});
list->addView(gpuListItem);
// MEM
brls::SelectListItem* memListItem = createFreqListItem(SysClkModule_MEM, this->profiles.mhzMap[profile][SysClkModule_MEM]);
this->profiles.mhzMap[profile][SysClkModule_MEM] *= 1000000;
memListItem->getValueSelectedEvent()->subscribe([this, profile](int result) {
this->onProfileChanged();
this->profiles.mhzMap[profile][SysClkModule_MEM] = result == 0 ? result : g_freq_table_hz[SysClkModule_MEM][result];
brls::Logger::debug("Caching freq for module %d and profile %d to %" PRIu32, SysClkModule_MEM, profile, this->profiles.mhzMap[profile][SysClkModule_MEM]);
});
list->addView(memListItem);
}
void AppProfileFrame::onProfileChanged()
{
this->getSidebar()->getButton()->setState(brls::ButtonState::ENABLED);
this->updateActionHint(brls::Key::B, "Cancel");
}
bool AppProfileFrame::onCancel()
{
if (this->hasProfileChanged())
{
brls::Dialog* dialog = new brls::Dialog("You have unsaved changes to this profile!\nAre you sure you want to discard them?");
dialog->addButton("No", [dialog](brls::View* view){
dialog->close();
});
dialog->addButton("Yes", [dialog](brls::View* view){
dialog->close([](){
brls::Application::popView(brls::ViewAnimation::SLIDE_RIGHT);
});
});
dialog->open();
}
else
{
brls::Application::popView(brls::ViewAnimation::SLIDE_RIGHT);
}
return true;
}
bool AppProfileFrame::hasProfileChanged()
{
return this->getSidebar()->getButton()->getState() == brls::ButtonState::ENABLED;
}

View File

@@ -0,0 +1,46 @@
/*
sys-clk manager, a sys-clk frontend homebrew
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
Copyright (C) 2019 m4xw
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis.hpp>
#include <sysclk.h>
#include "app_profiles_tab.h"
class AppProfileFrame : public brls::ThumbnailFrame
{
public:
AppProfileFrame(Title* title);
bool onCancel() override;
private:
Title* title;
SysClkTitleProfileList profiles;
bool hasProfileChanged();
void addFreqs(brls::List* list, SysClkProfile profile);
void onProfileChanged();
};

View File

@@ -0,0 +1,244 @@
/*
sys-clk manager, a sys-clk frontend homebrew
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
Copyright (C) 2019 m4xw
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "app_profiles_tab.h"
#include "app_profile_frame.h"
#include "ipc/client.h"
#include "ipc/ipc.h"
#include <cstring>
#include "utils.h"
#define PROFILE_BADGE "\uE3E0"
AppProfilesTab::AppProfilesTab()
{
// Filter toggle
this->filterListItem = new brls::ToggleListItem("Show applications with no profile", this->showEmptyProfiles, "", "Yes", "No");
filterListItem->getClickEvent()->subscribe([this](View* v)
{
this->refreshFilter();
});
this->addView(filterListItem);
// Spacing
this->addView(new brls::ListItemGroupSpacing());
// Applications list
NsApplicationRecord record;
uint64_t tid;
NsApplicationControlData controlData;
NacpLanguageEntry* langEntry = NULL;
Result rc;
size_t i = 0;
int recordCount = 0;
size_t controlSize = 0;
while (true)
{
// Record
rc = nsListApplicationRecord(&record, sizeof(record), i, &recordCount);
if (R_FAILED(rc))
{
errorResult("nsListApplicationRecord", rc);
break;
}
if(recordCount <= 0)
break;
tid = record.application_id;
// Control data
rc = nsGetApplicationControlData(NsApplicationControlSource_Storage, tid, &controlData, sizeof(controlData), &controlSize);
if (R_FAILED(rc))
{
errorResult("nsGetApplicationControlData", rc);
break;
}
// Language entry
rc = nacpGetLanguageEntry(&controlData.nacp, &langEntry);
if (R_FAILED(rc))
{
errorResult("nacpGetLanguageEntry", rc);
break;
}
// Name
if (!langEntry->name)
{
i++;
continue;
}
Title* title = (Title*) malloc(sizeof(Title));
title->tid = tid;
memset(title->name, 0, sizeof(title->name));
strncpy(title->name, langEntry->name, sizeof(title->name)-1);
memcpy(title->icon, controlData.icon, sizeof(title->icon));
// Profile
rc = sysclkIpcGetProfileCount(tid, &title->profileCount);
if (R_FAILED(rc))
{
errorResult("sysclkIpcGetProfileCount", rc);
free(title);
break;
}
// Add the ListItem
brls::ListItem *listItem = new brls::ListItem(formatListItemTitle(std::string(title->name)), "", formatTid(title->tid));
title->listItem = listItem;
this->items.push_back(listItem);
if (title->profileCount > 0)
{
listItem->setValue(PROFILE_BADGE);
this->profilesItems.push_back(listItem);
}
else
{
this->emptyProfilesItems.push_back(listItem);
if (!this->showEmptyProfiles)
listItem->collapse(false);
}
listItem->setThumbnail(title->icon, sizeof(title->icon));
listItem->getClickEvent()->subscribe([this, title](View* view) {
this->editingTitle = title;
AppProfileFrame* profileFrame = new AppProfileFrame(title);
brls::Application::pushView(profileFrame, brls::ViewAnimation::SLIDE_LEFT);
});
this->addView(listItem);
this->titles.push_back(title);
i++;
}
// Empty list message
this->emptyListLabel = new brls::Label(brls::LabelStyle::REGULAR, "", true);
this->addView(emptyListLabel);
this->updateEmptyListLabel(false);
}
void AppProfilesTab::updateEmptyListLabel(bool animate)
{
if (this->items.empty())
{
this->emptyListLabel->setText("\uE140 You don't have any application installed on your Nintendo Switch.");
this->emptyListLabel->show([](){}, animate);
}
else if (!this->showEmptyProfiles && this->profilesItems.empty())
{
this->emptyListLabel->setText("\uE140 You don't have any application with a defined profile at the moment.");
this->emptyListLabel->show([](){}, animate);
}
else
{
this->emptyListLabel->hide([](){}, animate);
}
}
AppProfilesTab::~AppProfilesTab()
{
for (Title* title : this->titles)
free(title);
this->titles.clear();
}
void AppProfilesTab::willAppear(bool resetState)
{
if (this->editingTitle != nullptr)
{
bool hadProfiles = this->editingTitle->profileCount > 0;
Result rc = sysclkIpcGetProfileCount(this->editingTitle->tid, &this->editingTitle->profileCount);
bool hasProfiles = this->editingTitle->profileCount > 0;
if (R_FAILED(rc))
{
errorResult("sysclkIpcGetProfileCount", rc);
this->editingTitle = nullptr;
return;
}
// Update the profile badge
if (hasProfiles)
this->editingTitle->listItem->setValue(PROFILE_BADGE);
else
this->editingTitle->listItem->setValue("");
// Update lists
// Remove from emptyProfilesItems if it didn't have a profile
// but has one now
// Add to profilesItems
if (!hadProfiles && hasProfiles)
{
this->emptyProfilesItems.erase(std::remove(this->emptyProfilesItems.begin(), this->emptyProfilesItems.end(), this->editingTitle->listItem), this->emptyProfilesItems.end());
this->profilesItems.push_back(this->editingTitle->listItem);
}
// Add to emptyProfilesItems if it had a profile but doesn't
// has one now
// Remove from profilesItems
if (hadProfiles && !hasProfiles)
{
this->emptyProfilesItems.push_back(this->editingTitle->listItem);
this->profilesItems.erase(std::remove(this->profilesItems.begin(), this->profilesItems.end(), this->editingTitle->listItem), this->profilesItems.end());
}
// Refresh the filter
this->refreshFilter();
// Cleanup
this->editingTitle = nullptr;
}
}
void AppProfilesTab::refreshFilter()
{
this->showEmptyProfiles = this->filterListItem->getToggleState();
for (brls::ListItem *listItem : this->emptyProfilesItems)
{
if (this->showEmptyProfiles)
listItem->expand();
else
listItem->collapse();
this->updateEmptyListLabel();
}
}

View File

@@ -0,0 +1,62 @@
/*
sys-clk manager, a sys-clk frontend homebrew
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
Copyright (C) 2019 m4xw
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis.hpp>
#include "ipc/ipc.h"
typedef struct
{
uint64_t tid;
NsApplicationName name;
NsApplicationIcon icon;
uint8_t profileCount;
brls::ListItem* listItem;
} Title;
class AppProfilesTab : public brls::List
{
private:
bool showEmptyProfiles = true;
std::vector<Title*> titles;
std::vector<brls::ListItem*> items;
std::vector<brls::ListItem*> emptyProfilesItems;
std::vector<brls::ListItem*> profilesItems;
brls::Label *emptyListLabel;
brls::ToggleListItem *filterListItem;
void updateEmptyListLabel(bool animate = true);
Title* editingTitle = nullptr;
void refreshFilter();
public:
AppProfilesTab();
~AppProfilesTab();
void willAppear(bool resetState) override;
};

View File

@@ -0,0 +1,64 @@
/*
sys-clk manager, a sys-clk frontend homebrew
Copyright (C) 2019-2020 natinusala
Copyright (C) 2019 p-sam
Copyright (C) 2019 m4xw
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "cheat_sheet_tab.h"
#include <borealis.hpp>
CheatSheetTab::CheatSheetTab()
{
// CPU
this->addView(new brls::Header("CPU Clocks"));
brls::Table *cpuTable = new brls::Table();
cpuTable->addRow(brls::TableRowType::BODY, "Maximum", "1785 MHz");
cpuTable->addRow(brls::TableRowType::BODY, "Official Docked and Handheld", "1020 MHz");
this->addView(cpuTable);
// GPU
this->addView(new brls::Header("GPU Clocks"));
brls::Table *gpuTable = new brls::Table();
gpuTable->addRow(brls::TableRowType::BODY, "Maximum", "921 MHz");
gpuTable->addRow(brls::TableRowType::BODY, "Official Docked", "768 MHz");
gpuTable->addRow(brls::TableRowType::BODY, "Maximum Mariko Handheld", "614 MHz");
gpuTable->addRow(brls::TableRowType::BODY, "Maximum Erista Handheld", "460 MHz");
gpuTable->addRow(brls::TableRowType::BODY, "Official Handheld", "384 MHz");
this->addView(gpuTable);
// MEM
this->addView(new brls::Header("MEM Clocks"));
brls::Table *memTable = new brls::Table();
memTable->addRow(brls::TableRowType::BODY, "Maximum, Official Docked", "1600 MHz");
memTable->addRow(brls::TableRowType::BODY, "Official Handheld", "1331 MHz");
this->addView(memTable);
}
void CheatSheetTab::customSpacing(brls::View* current, brls::View* next, int* spacing)
{
if (dynamic_cast<brls::Table*>(current))
*spacing = 0;
else
List::customSpacing(current, next, spacing);
}

View File

@@ -0,0 +1,36 @@
/*
sys-clk manager, a sys-clk frontend homebrew
Copyright (C) 2019-2020 natinusala
Copyright (C) 2019 p-sam
Copyright (C) 2019 m4xw
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis.hpp>
class CheatSheetTab : public brls::List
{
public:
CheatSheetTab();
void customSpacing(brls::View* current, brls::View* next, int* spacing) override;
View* getDefaultFocus() override
{
return nullptr;
}
};

View File

@@ -0,0 +1,34 @@
/*
sys-clk manager, a sys-clk frontend homebrew
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
Copyright (C) 2019 m4xw
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "ipc.h"
#if defined(__SWITCH__) && defined(__cplusplus)
extern "C" {
#endif
#include <sysclk.h>
#include <sysclk/client/ipc.h>
#if defined(__SWITCH__) && defined(__cplusplus)
}
#endif

View File

@@ -0,0 +1,43 @@
/*
sys-clk manager, a sys-clk frontend homebrew
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
Copyright (C) 2019 m4xw
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "types.h"
#ifdef __SWITCH__
#ifdef __cplusplus
extern "C" {
#endif
#include <switch.h>
#ifdef __cplusplus
}
#endif
#else
#include "pc_shim/nacp.h"
#include "pc_shim/ns.h"
#include "pc_shim/types.h"
#endif

View File

@@ -0,0 +1,396 @@
/*
sys-clk manager, a sys-clk frontend homebrew
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
Copyright (C) 2019 m4xw
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "client.h"
#include <string.h>
#include <stdlib.h>
static SysClkShimServer* g_server = NULL;
bool sysclkIpcRunning()
{
return true;
}
Result sysclkIpcInitialize()
{
if(!g_server)
{
g_server = new SysClkShimServer();
g_server->SetContextApplicationId(0x0100000000001000ULL);
g_server->SetContextHz(SysClkModule_CPU, 1020000000);
g_server->SetContextHz(SysClkModule_GPU, 307200000);
g_server->SetContextHz(SysClkModule_MEM, 1065600000);
g_server->SetContextTemp(SysClkThermalSensor_PCB, 45700);
g_server->SetContextTemp(SysClkThermalSensor_SOC, 48200);
g_server->SetContextEnabled(true);
g_server->SetProfile(0x010000000000F002, SysClkModule_CPU, SysClkProfile_Docked, 1224);
g_server->SetProfile(0x010000000000F002, SysClkModule_GPU, SysClkProfile_Docked, 921);
g_server->SetProfile(0x010000000000F002, SysClkModule_MEM, SysClkProfile_Docked, 1600);
g_server->SetProfile(0x010000000000F002, SysClkModule_CPU, SysClkProfile_Handheld, 1224);
g_server->SetProfile(0x010000000000F002, SysClkModule_MEM, SysClkProfile_Handheld, 1600);
g_server->SetConfigValue(SysClkConfigValue_CsvWriteIntervalMs, 5000);
g_server->AddFreq(SysClkModule_MEM, 665600000);
g_server->AddFreq(SysClkModule_MEM, 800000000);
g_server->AddFreq(SysClkModule_MEM, 1065600000);
g_server->AddFreq(SysClkModule_MEM, 1331200000);
g_server->AddFreq(SysClkModule_MEM, 1600000000);
g_server->AddFreq(SysClkModule_CPU, 612000000);
g_server->AddFreq(SysClkModule_CPU, 714000000);
g_server->AddFreq(SysClkModule_CPU, 816000000);
g_server->AddFreq(SysClkModule_CPU, 918000000);
g_server->AddFreq(SysClkModule_CPU, 1020000000);
g_server->AddFreq(SysClkModule_CPU, 1122000000);
g_server->AddFreq(SysClkModule_CPU, 1224000000);
g_server->AddFreq(SysClkModule_CPU, 1326000000);
g_server->AddFreq(SysClkModule_CPU, 1428000000);
g_server->AddFreq(SysClkModule_CPU, 1581000000);
g_server->AddFreq(SysClkModule_CPU, 1683000000);
g_server->AddFreq(SysClkModule_CPU, 1785000000);
g_server->AddFreq(SysClkModule_GPU, 76800000);
g_server->AddFreq(SysClkModule_GPU, 153600000);
g_server->AddFreq(SysClkModule_GPU, 230400000);
g_server->AddFreq(SysClkModule_GPU, 307200000);
g_server->AddFreq(SysClkModule_GPU, 384000000);
g_server->AddFreq(SysClkModule_GPU, 460800000);
g_server->AddFreq(SysClkModule_GPU, 537600000);
g_server->AddFreq(SysClkModule_GPU, 614400000);
g_server->AddFreq(SysClkModule_GPU, 691200000);
g_server->AddFreq(SysClkModule_GPU, 768000000);
g_server->AddFreq(SysClkModule_GPU, 844800000);
g_server->AddFreq(SysClkModule_GPU, 921600000);
}
return 0;
}
void sysclkIpcExit()
{
if(g_server)
delete g_server;
}
Result sysclkIpcGetAPIVersion(u32* out_ver)
{
*out_ver = SYSCLK_IPC_API_VERSION;
return 0;
}
Result sysclkIpcGetVersionString(char* out, size_t len)
{
strncpy(out, "- shim server -", len-1);
return 0;
}
Result sysclkIpcGetCurrentContext(SysClkContext* out_context)
{
g_server->CopyContext(out_context);
return 0;
}
Result sysclkIpcGetProfileCount(u64 tid, u8* out_count)
{
*out_count = g_server->CountProfiles(tid);
return 0;
}
Result sysclkIpcGetProfiles(u64 tid, SysClkTitleProfileList* out_profiles)
{
g_server->GetProfiles(tid, out_profiles);
return 0;
}
Result sysclkIpcSetProfiles(u64 tid, SysClkTitleProfileList* profiles)
{
g_server->SetProfiles(tid, profiles);
return 0;
}
Result sysclkIpcSetEnabled(bool enabled)
{
g_server->SetContextEnabled(enabled);
if(enabled)
{
g_server->SetContextTemp(SysClkThermalSensor_PCB, 45700);
g_server->SetContextTemp(SysClkThermalSensor_SOC, 48200);
}
else
{
g_server->SetContextTemp(SysClkThermalSensor_PCB, 34200);
g_server->SetContextTemp(SysClkThermalSensor_SOC, 42800);
}
return 0;
}
Result sysclkIpcExitCmd()
{
return 0;
}
Result sysclkIpcSetOverride(SysClkModule module, u32 hz)
{
g_server->SetContextOverride(module, hz);
return 0;
}
Result sysclkIpcGetConfigValues(SysClkConfigValueList* out_configValues)
{
g_server->GetConfigValues(out_configValues);
return 0;
}
Result sysclkIpcSetConfigValues(SysClkConfigValueList* configValues)
{
g_server->SetConfigValues(configValues);
return 0;
}
Result sysclkIpcGetFreqList(SysClkModule module, u32* list, u32 maxCount, u32* outCount)
{
g_server->GetFreqList(module, list, maxCount, outCount);
return 0;
}
SysClkShimServer::SysClkShimServer()
{
this->store = std::map<std::tuple<u64, SysClkModule, SysClkProfile>, u32>();
this->SetContextApplicationId(0);
this->SetContextHz(SysClkModule_CPU, 0);
this->SetContextHz(SysClkModule_GPU, 0);
this->SetContextHz(SysClkModule_MEM, 0);
this->SetContextRealHz(SysClkModule_CPU, 0);
this->SetContextRealHz(SysClkModule_GPU, 0);
this->SetContextRealHz(SysClkModule_MEM, 0);
this->SetContextOverride(SysClkModule_CPU, 0);
this->SetContextOverride(SysClkModule_GPU, 0);
this->SetContextOverride(SysClkModule_MEM, 0);
this->SetContextTemp(SysClkThermalSensor_PCB, 0);
this->SetContextTemp(SysClkThermalSensor_SOC, 0);
this->SetContextProfile(SysClkProfile_Handheld);
this->SetContextEnabled(false);
for(int kval = 0; kval < SysClkConfigValue_EnumMax; kval++)
{
this->configValues[kval] = sysclkDefaultConfigValue((SysClkConfigValue)kval);
}
}
void SysClkShimServer::SetContextApplicationId(u64 tid)
{
this->context.applicationId = tid;
}
void SysClkShimServer::SetContextHz(SysClkModule module, u32 hz)
{
if(module < SysClkModule_EnumMax)
{
this->context.freqs[module] = hz;
}
}
void SysClkShimServer::SetContextRealHz(SysClkModule module, u32 hz)
{
if(module < SysClkModule_EnumMax)
{
this->context.realFreqs[module] = hz;
}
}
void SysClkShimServer::SetContextTemp(SysClkThermalSensor sensor, u32 temp)
{
if(sensor < SysClkThermalSensor_EnumMax)
{
this->context.temps[sensor] = temp;
}
}
void SysClkShimServer::CopyContext(SysClkContext* out_context)
{
memcpy(out_context, &this->context, sizeof(SysClkContext));
}
void SysClkShimServer::SetContextProfile(SysClkProfile profile)
{
if(profile < SysClkProfile_EnumMax)
{
this->context.profile = profile;
}
}
void SysClkShimServer::SetContextEnabled(bool enabled)
{
this->context.enabled = enabled;
}
void SysClkShimServer::SetProfile(uint64_t applicationId, SysClkModule module, SysClkProfile profile, u32 mhz)
{
std::tuple<u64, SysClkModule, SysClkProfile> key = std::make_tuple(applicationId, module, profile);
if(mhz > 0)
{
this->store[key] = mhz;
}
else
{
this->store.erase(key);
}
}
u32 SysClkShimServer::GetProfileMHz(uint64_t applicationId, SysClkModule module, SysClkProfile profile)
{
std::tuple<u64, SysClkModule, SysClkProfile> key = std::make_tuple(applicationId, module, profile);
std::map<std::tuple<u64, SysClkModule, SysClkProfile>, u32>::iterator it = this->store.find(key);
if(it != this->store.end())
{
return it->second;
}
return 0;
}
void SysClkShimServer::GetProfiles(u64 applicationId, SysClkTitleProfileList* out_profiles)
{
for(unsigned int profile = 0; profile < SysClkProfile_EnumMax; profile++)
{
for(unsigned int module = 0; module < SysClkModule_EnumMax; module++)
{
out_profiles->mhzMap[profile][module] = this->GetProfileMHz(applicationId, (SysClkModule)module, (SysClkProfile)profile);
}
}
}
void SysClkShimServer::SetProfiles(u64 applicationId, SysClkTitleProfileList* profiles)
{
for(unsigned int profile = 0; profile < SysClkProfile_EnumMax; profile++)
{
for(unsigned int module = 0; module < SysClkModule_EnumMax; module++)
{
this->SetProfile(applicationId, (SysClkModule)module, (SysClkProfile)profile, profiles->mhzMap[profile][module]);
}
}
}
u8 SysClkShimServer::CountProfiles(u64 applicationId)
{
return std::accumulate(
std::begin(this->store),
std::end(this->store),
0,
[applicationId] (u8 value, const std::map<std::tuple<u64, SysClkModule, SysClkProfile>, u32>::value_type& p)
{
if(std::get<0>(p.first) == applicationId)
{
value++;
}
return value;
}
);
}
void SysClkShimServer::SetContextOverride(SysClkModule module, u32 hz)
{
if(SYSCLK_ENUM_VALID(SysClkModule, module))
{
this->context.overrideFreqs[module] = hz;
}
}
u64 SysClkShimServer::GetConfigValue(SysClkConfigValue kval)
{
if(SYSCLK_ENUM_VALID(SysClkConfigValue, kval))
{
return this->configValues[kval];
}
return 0;
}
void SysClkShimServer::SetConfigValue(SysClkConfigValue kval, u64 val)
{
if(SYSCLK_ENUM_VALID(SysClkConfigValue, kval))
{
if(sysclkValidConfigValue(kval, this->configValues[kval]))
{
this->configValues[kval] = val;
}
else
{
this->configValues[kval] = sysclkDefaultConfigValue(kval);
}
}
}
void SysClkShimServer::GetConfigValues(SysClkConfigValueList* out_configValues)
{
for(unsigned int kval = 0; kval < SysClkConfigValue_EnumMax; kval++)
{
out_configValues->values[kval] = this->GetConfigValue((SysClkConfigValue)kval);
}
}
void SysClkShimServer::SetConfigValues(SysClkConfigValueList* configValues)
{
for(unsigned int kval = 0; kval < SysClkConfigValue_EnumMax; kval++)
{
this->SetConfigValue((SysClkConfigValue)kval, configValues->values[kval]);
}
}
void SysClkShimServer::AddFreq(SysClkModule module, u32 hz)
{
if(SYSCLK_ENUM_VALID(SysClkModule, module))
{
this->freqs[module].push_back(hz);
}
}
void SysClkShimServer::GetFreqList(SysClkModule module, u32* list, u32 maxCount, u32* outCount)
{
u32 count = 0;
if(SYSCLK_ENUM_VALID(SysClkModule, module))
{
std::vector<u32>::iterator iter = this->freqs[module].begin();
while(iter < this->freqs[module].end() && *outCount < maxCount)
{
*list = *iter;
list++;
iter++;
count++;
}
}
if(outCount)
{
*outCount = count;
}
}

View File

@@ -0,0 +1,57 @@
/*
sys-clk manager, a sys-clk frontend homebrew
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
Copyright (C) 2019 m4xw
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <map>
#include <vector>
#include <numeric>
#include "../client.h"
class SysClkShimServer
{
public:
SysClkShimServer();
void SetContextApplicationId(u64 tid);
void SetContextHz(SysClkModule module, u32 hz);
void SetContextRealHz(SysClkModule module, u32 hz);
void SetContextTemp(SysClkThermalSensor sensor, u32 temp);
void SetContextProfile(SysClkProfile profile);
void SetContextEnabled(bool enabled);
void SetContextOverride(SysClkModule module, u32 hz);
void CopyContext(SysClkContext* out_context);
void SetProfile(uint64_t applicationId, SysClkModule module, SysClkProfile profile, u32 mhz);
u32 GetProfileMHz(uint64_t applicationId, SysClkModule module, SysClkProfile profile);
void GetProfiles(u64 applicationId, SysClkTitleProfileList* out_profiles);
void SetProfiles(u64 applicationId, SysClkTitleProfileList* profiles);
u8 CountProfiles(u64 applicationId);
u64 GetConfigValue(SysClkConfigValue kval);
void SetConfigValue(SysClkConfigValue kval, u64 val);
void GetConfigValues(SysClkConfigValueList* out_configValues);
void SetConfigValues(SysClkConfigValueList* configValues);
void AddFreq(SysClkModule module, u32 hz);
void GetFreqList(SysClkModule module, u32* list, u32 maxCount, u32* outCount);
protected:
SysClkContext context;
std::vector<u32> freqs[SysClkModule_EnumMax];
std::map<std::tuple<u64, SysClkModule, SysClkProfile>, u32> store;
u64 configValues[SysClkConfigValue_EnumMax];
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -0,0 +1,44 @@
/*
sys-clk manager, a sys-clk frontend homebrew
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
Copyright (C) 2019 m4xw
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "types.h"
typedef struct
{
char name[0x200];
} NacpLanguageEntry;
typedef struct
{
NacpLanguageEntry entry;
} NacpStruct;
static inline Result nacpGetLanguageEntry(NacpStruct* nacp, NacpLanguageEntry** langentry)
{
if(nacp && langentry)
{
*langentry = &nacp->entry;
return 0;
}
return 0xBAD;
}

View File

@@ -0,0 +1,120 @@
/*
sys-clk manager, a sys-clk frontend homebrew
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
Copyright (C) 2019 m4xw
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "ns.h"
#include "types.h"
#include <string.h>
#include <stdlib.h>
#define GAME_ICON_PATH "src/ipc/pc_shim/game.jpg"
static NsShimStore* g_store = NULL;
Result nsInitialize()
{
if(!g_store)
{
g_store = new NsShimStore();
g_store->AddRecord(0x010000000000F001ULL, "Secret Maryo Chronicles", GAME_ICON_PATH);
g_store->AddRecord(0x010000000000F002ULL, "Zelda: Mystery of Solarus", GAME_ICON_PATH);
g_store->AddRecord(0x010000000000F003ULL, "AM2R", GAME_ICON_PATH);
g_store->AddRecord(0x010000000000F004ULL, "Mother 4", GAME_ICON_PATH);
g_store->AddRecord(0x01007EF00011E000ULL, "Wrath of the Wild", GAME_ICON_PATH);
}
return 0;
}
void nsExit()
{
if(g_store)
delete g_store;
}
Result nsListApplicationRecord(NsApplicationRecord* out_records, s32 size, s32 offset, s32* out_count)
{
return g_store->ListRecord(out_records, size, offset, out_count) ? 0 : 0xBAD;
}
Result nsGetApplicationControlData(NsApplicationControlSource flag, u64 titleID, NsApplicationControlData* out_data, size_t size, u64* out_size)
{
return g_store->GetControlData(out_data, size, titleID, out_size) ? 0 : 0xBAD;
}
NsShimStore::NsShimStore()
{
this->store = std::map<u64, std::pair<std::string, std::string>>();
}
void NsShimStore::AddRecord(u64 titleID, std::string name, std::string iconPath)
{
this->store[titleID] = std::make_pair(name, iconPath);
}
bool NsShimStore::ListRecord(NsApplicationRecord* out_records, s32 size, s32 offset, s32* out_count)
{
if(!out_records || !out_count)
{
return false;
}
size_t left = size;
size_t count = 0;
std::map<u64, std::pair<std::string, std::string>>::iterator it = this->store.begin();
std::advance(it, offset);
while(left >= sizeof(NsApplicationRecord) && it != this->store.end())
{
out_records->application_id = it->first;
left -= sizeof(NsApplicationRecord);
out_records++;
count++;
std::advance(it, 1);
}
*out_count = count;
return true;
}
bool NsShimStore::GetControlData(NsApplicationControlData* out_data, size_t size, u64 tid, size_t* out_size)
{
if(!out_data || !out_size || size != sizeof(NsApplicationControlData))
{
return false;
}
std::map<u64, std::pair<std::string, std::string>>::iterator it = this->store.find(tid);
if(it == this->store.end())
{
return false;
}
memset(out_data, 0, size);
strncpy(out_data->nacp.entry.name, it->second.first.c_str(), sizeof(out_data->nacp.entry.name)-1);
FILE* f = fopen(it->second.second.c_str(), "rb");
if(!f)
{
printf("[NsShimStore] Could not open file '%s'\n", it->second.second.c_str());
return false;
}
*out_size = fread(out_data->icon, sizeof(out_data->icon), 1, f);
fclose(f);
return true;
}

View File

@@ -0,0 +1,60 @@
/*
sys-clk manager, a sys-clk frontend homebrew
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
Copyright (C) 2019 m4xw
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <map>
#include <string>
#include "../types.h"
#include "types.h"
#include "nacp.h"
typedef enum
{
NsApplicationControlSource_Storage = 1
} NsApplicationControlSource;
typedef struct
{
u64 application_id;
} NsApplicationRecord;
typedef struct
{
NacpStruct nacp;
NsApplicationIcon icon;
} NsApplicationControlData;
Result nsInitialize();
Result nsListApplicationRecord(NsApplicationRecord* out_records, s32 size, s32 offset, s32* out_count);
Result nsGetApplicationControlData(NsApplicationControlSource flag, u64 titleID, NsApplicationControlData* out_data, size_t size, u64* out_size);
void nsExit();
class NsShimStore
{
public:
NsShimStore();
void AddRecord(u64 titleID, std::string name, std::string iconPath);
bool ListRecord(NsApplicationRecord* out_records, s32 size, s32 offset, s32* out_count);
bool GetControlData(NsApplicationControlData* out_data, size_t size, u64 titleID, size_t* out_size);
protected:
std::map<u64, std::pair<std::string, std::string>> store;
};

View File

@@ -0,0 +1,32 @@
/*
sys-clk manager, a sys-clk frontend homebrew
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
Copyright (C) 2019 m4xw
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <cstdint>
#define R_FAILED(res) ((res) != 0)
#define R_SUCCEEDED(res) ((res) == 0)
typedef std::uint32_t Result;
typedef std::uint32_t u32;
typedef std::int32_t s32;
typedef std::uint64_t u64;
typedef std::uint8_t u8;

View File

@@ -0,0 +1,26 @@
/*
sys-clk manager, a sys-clk frontend homebrew
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
Copyright (C) 2019 m4xw
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
typedef char NsApplicationName[0x201];
typedef uint8_t NsApplicationIcon[0x20000];

View File

@@ -0,0 +1,75 @@
/*
sys-clk manager, a sys-clk frontend homebrew
Copyright (C) 2019 natinusala
Copyright (C) 2019-2020 p-sam
Copyright (C) 2019 m4xw
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "logo.h"
Logo::Logo(LogoStyle style) {
this->logoLabel = new brls::Label(brls::LabelStyle::LIST_ITEM, "sys-clk", style == LogoStyle::ABOUT);
this->logoLabel->setParent(this);
int logoFont = brls::Application::findFont(LOGO_FONT_NAME);
if (logoFont >= 0)
{
this->logoLabel->setFont(logoFont);
}
if (style == LogoStyle::ABOUT)
{
this->logoLabel->setFontSize(LOGO_ABOUT_FONT_SIZE);
this->logoLabel->setHorizontalAlign(NVG_ALIGN_CENTER);
}
if (style == LogoStyle::HEADER)
{
this->logoLabel->setFontSize(LOGO_HEADER_FONT_SIZE);
this->descLabel = new brls::Label(brls::LabelStyle::LIST_ITEM, "manager");
this->descLabel->setParent(this);
this->descLabel->setFontSize(LOGO_DESC_FONT_SIZE);
}
}
Logo::~Logo() {
delete this->logoLabel;
if (this->descLabel)
delete this->descLabel;
}
void Logo::draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, brls::Style* style, brls::FrameContext* ctx)
{
this->logoLabel->frame(ctx);
if (this->descLabel)
this->descLabel->frame(ctx);
}
void Logo::layout(NVGcontext* vg, brls::Style* style, brls::FontStash* stash)
{
this->logoLabel->setBoundaries(this->x, this->y + LOGO_OFFSET, this->width, this->height);
this->logoLabel->layout(vg, style, stash);
this->height = this->logoLabel->getHeight();
if (this->descLabel)
{
this->descLabel->layout(vg, style, stash);
this->descLabel->setBoundaries(this->x + LOGO_HEADER_SPACING + this->logoLabel->getWidth(), this->y + style->AppletFrame.titleOffset - 1, this->descLabel->getWidth(), height);
}
}

View File

@@ -0,0 +1,52 @@
/*
sys-clk manager, a sys-clk frontend homebrew
Copyright (C) 2019 natinusala
Copyright (C) 2019-2020 p-sam
Copyright (C) 2019 m4xw
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis.hpp>
#include "utils.h"
enum class LogoStyle
{
HEADER = 0,
ABOUT
};
#define LOGO_FONT_NAME "logo"
#define LOGO_FONT_PATH APP_ASSET("fira/FiraSans-Medium-rnx.ttf")
#define LOGO_HEADER_FONT_SIZE 45
#define LOGO_HEADER_SPACING 12
#define LOGO_ABOUT_FONT_SIZE 55
#define LOGO_DESC_FONT_SIZE 28
#define LOGO_OFFSET 2
class Logo : public brls::View
{
protected:
brls::Label* logoLabel = nullptr;
brls::Label* descLabel = nullptr;
void layout(NVGcontext* vg, brls::Style* style, brls::FontStash* stash);
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, brls::Style* style, brls::FrameContext* ctx) override;
public:
Logo(LogoStyle style);
virtual ~Logo();
};

View File

@@ -0,0 +1,109 @@
/*
sys-clk manager, a sys-clk frontend homebrew
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
Copyright (C) 2019 m4xw
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <borealis.hpp>
#include "main_frame.h"
#include "logo.h"
#include "ipc/client.h"
#include "ipc/ipc.h"
int main(int argc, char* argv[])
{
// Init the app
if (!brls::Application::init(APP_TITLE))
{
brls::Logger::error("Unable to init Borealis application");
return EXIT_FAILURE;
}
// Setup verbose logging on PC
#ifndef __SWITCH__
brls::Logger::setLogLevel(brls::LogLevel::DEBUG);
#endif
if (brls::Application::loadFont(LOGO_FONT_NAME, LOGO_FONT_PATH) < 0)
{
brls::Logger::error("failed to load logo font");
}
uint32_t apiVersion;
// Check that sys-clk is running
if (!sysclkIpcRunning())
{
brls::Logger::error("sys-clk is not running");
brls::Application::crash("sys-clk does not seem to be running, please check that it is correctly installed and enabled.");
}
// Initialize sys-clk IPC client
else if (R_FAILED(sysclkIpcInitialize()) || R_FAILED(sysclkIpcGetAPIVersion(&apiVersion)))
{
brls::Logger::error("Unable to initialize sys-clk IPC client");
brls::Application::crash("Could not connect to sys-clk, please check that it is correctly installed and enabled.");
}
else if (SYSCLK_IPC_API_VERSION != apiVersion)
{
brls::Logger::error("sys-clk IPC API version mismatch (expected: %u; actual: %u)", SYSCLK_IPC_API_VERSION, apiVersion);
brls::Application::crash("The manager is not compatible with the currently running sysmodule of sys-clk, please check that you have correctly installed the latest version (reboot?).");
}
else if (R_FAILED(cacheFreqList()))
{
brls::Logger::error("Failed to get the freq list from sys-clk");
brls::Application::crash("Failed to get the freq list from sys-clk, please check that you have correctly installed the latest version (reboot?).");
}
else
{
// Set version string
char version[0x100] = {0};
Result rc = sysclkIpcGetVersionString(version, sizeof(version));
if (R_SUCCEEDED(rc))
{
brls::Application::setCommonFooter(std::string(version));
}
else
{
brls::Logger::error("Unable to get sys-clk version string");
brls::Application::setCommonFooter("[unknown]");
}
// Initialize services with a PC shim
nsInitialize();
// Create root view
MainFrame *mainFrame = new MainFrame();
// Add the root view to the stack
brls::Application::pushView(mainFrame);
}
// Run the app
while (brls::Application::mainLoop());
// Exit
nsExit();
sysclkIpcExit();
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,56 @@
/*
sys-clk manager, a sys-clk frontend homebrew
Copyright (C) 2019-2020 natinusala
Copyright (C) 2019 p-sam
Copyright (C) 2019 m4xw
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "main_frame.h"
#include "status_tab.h"
#include "advanced_settings_tab.h"
#include "app_profiles_tab.h"
#include "cheat_sheet_tab.h"
#include "about_tab.h"
#include "logo.h"
#include "ipc/client.h"
MainFrame::MainFrame() : TabFrame()
{
// Start refresh task
this->refreshTask = new RefreshTask();
this->refreshTask->start();
// Load UI
this->setIcon(new Logo(LogoStyle::HEADER));
AppProfilesTab *tab = new AppProfilesTab();
this->addTab("Status", new StatusTab(this->refreshTask));
this->addTab("Application Profiles", tab);
this->addTab("Advanced Settings", new AdvancedSettingsTab());
this->addSeparator();
this->addTab("Cheat Sheet", new CheatSheetTab());
this->addTab("About", new AboutTab());
}
MainFrame::~MainFrame()
{
this->refreshTask->stop();
}

View File

@@ -0,0 +1,35 @@
/*
sys-clk manager, a sys-clk frontend homebrew
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
Copyright (C) 2019 m4xw
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis.hpp>
#include "refresh_task.h"
class MainFrame : public brls::TabFrame
{
private:
RefreshTask *refreshTask;
public:
MainFrame();
~MainFrame();
};

View File

@@ -0,0 +1,105 @@
/*
sys-clk manager, a sys-clk frontend homebrew
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
Copyright (C) 2019 m4xw
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "refresh_task.h"
#include "utils.h"
#define REFRESH_INTERVAL 400
RefreshTask::RefreshTask() : RepeatingTask(REFRESH_INTERVAL)
{
}
void RefreshTask::onStart()
{
Result rc = sysclkIpcGetCurrentContext(&this->oldContext);
if (R_FAILED(rc))
{
brls::Logger::error("Unable to get context");
errorResult("sysclkIpcGetCurrentContext", rc);
}
}
void RefreshTask::run(retro_time_t currentTime)
{
RepeatingTask::run(currentTime);
// Get new context
SysClkContext context;
if (R_SUCCEEDED(sysclkIpcGetCurrentContext(&context)))
{
// CPU Freq
if (context.freqs[SysClkModule_CPU] != this->oldContext.freqs[SysClkModule_CPU])
this->freqUpdateEvent.fire(SysClkModule_CPU, context.freqs[SysClkModule_CPU]);
// GPU Freq
if (context.freqs[SysClkModule_GPU] != this->oldContext.freqs[SysClkModule_GPU])
this->freqUpdateEvent.fire(SysClkModule_GPU, context.freqs[SysClkModule_GPU]);
// MEM Freq
if (context.freqs[SysClkModule_MEM] != this->oldContext.freqs[SysClkModule_MEM])
this->freqUpdateEvent.fire(SysClkModule_MEM, context.freqs[SysClkModule_MEM]);
// Real CPU Freq
if (context.realFreqs[SysClkModule_CPU] != this->oldContext.realFreqs[SysClkModule_CPU])
this->realFreqUpdateEvent.fire(SysClkModule_CPU, context.realFreqs[SysClkModule_CPU]);
// Real GPU Freq
if (context.realFreqs[SysClkModule_GPU] != this->oldContext.realFreqs[SysClkModule_GPU])
this->realFreqUpdateEvent.fire(SysClkModule_GPU, context.realFreqs[SysClkModule_GPU]);
// Real MEM Freq
if (context.realFreqs[SysClkModule_MEM] != this->oldContext.realFreqs[SysClkModule_MEM])
this->realFreqUpdateEvent.fire(SysClkModule_MEM, context.realFreqs[SysClkModule_MEM]);
// Application ID
if (context.applicationId != this->oldContext.applicationId)
this->appIdUpdateEvent.fire(context.applicationId);
// Profile
if (context.profile != this->oldContext.profile)
this->profileUpdateEvent.fire(context.profile);
// Only notify temp changes every other tick
if (this->shouldNotifyTempChange)
{
// PCB Temp
if (context.temps[SysClkThermalSensor_PCB] != this->oldContext.temps[SysClkThermalSensor_PCB])
this->tempUpdateEvent.fire(SysClkThermalSensor_PCB, context.temps[SysClkThermalSensor_PCB]);
//SoC Temp
if (context.temps[SysClkThermalSensor_SOC] != this->oldContext.temps[SysClkThermalSensor_SOC])
this->tempUpdateEvent.fire(SysClkThermalSensor_SOC, context.temps[SysClkThermalSensor_SOC]);
//Skin Temp
if (context.temps[SysClkThermalSensor_Skin] != this->oldContext.temps[SysClkThermalSensor_Skin])
this->tempUpdateEvent.fire(SysClkThermalSensor_Skin, context.temps[SysClkThermalSensor_Skin]);
}
this->shouldNotifyTempChange = !this->shouldNotifyTempChange;
this->oldContext = context;
}
else
{
brls::Logger::error("Unable to refresh context");
}
}

View File

@@ -0,0 +1,88 @@
/*
sys-clk manager, a sys-clk frontend homebrew
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
Copyright (C) 2019 m4xw
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis.hpp>
#include "ipc/client.h"
#include <functional>
typedef brls::Event<SysClkModule, uint32_t> FreqUpdateEvent;
typedef brls::Event<uint64_t> AppIdUpdateEvent;
typedef brls::Event<SysClkProfile> ProfileUpdateEvent;
typedef brls::Event<SysClkThermalSensor, uint32_t> TempUpdateEvent;
class RefreshTask : public brls::RepeatingTask
{
private:
SysClkContext oldContext;
FreqUpdateEvent freqUpdateEvent;
FreqUpdateEvent realFreqUpdateEvent;
AppIdUpdateEvent appIdUpdateEvent;
ProfileUpdateEvent profileUpdateEvent;
TempUpdateEvent tempUpdateEvent;
bool shouldNotifyTempChange = true;
public:
RefreshTask();
~RefreshTask() {}
void onStart() override;
void run(retro_time_t currentTime) override;
inline FreqUpdateEvent::Subscription registerFreqListener(FreqUpdateEvent::Callback cb) {
return this->freqUpdateEvent.subscribe(cb);
}
inline void unregisterFreqListener(FreqUpdateEvent::Subscription subscription) {
this->freqUpdateEvent.unsubscribe(subscription);
}
inline FreqUpdateEvent::Subscription registerRealFreqListener(FreqUpdateEvent::Callback cb) {
return this->realFreqUpdateEvent.subscribe(cb);
}
inline void unregisterRealFreqListener(FreqUpdateEvent::Subscription subscription) {
this->realFreqUpdateEvent.unsubscribe(subscription);
}
inline AppIdUpdateEvent::Subscription registerAppIdListener(AppIdUpdateEvent::Callback cb) {
return this->appIdUpdateEvent.subscribe(cb);
}
inline void unregisterAppIdListener(AppIdUpdateEvent::Subscription subscription) {
this->appIdUpdateEvent.unsubscribe(subscription);
}
inline ProfileUpdateEvent::Subscription registerProfileListener(ProfileUpdateEvent::Callback cb) {
return this->profileUpdateEvent.subscribe(cb);
}
inline void unregisterProfileListener(ProfileUpdateEvent::Subscription subscription) {
this->profileUpdateEvent.unsubscribe(subscription);
}
inline TempUpdateEvent::Subscription registerTempListener(TempUpdateEvent::Callback cb) {
return this->tempUpdateEvent.subscribe(cb);
}
inline void unregisterTempListener(TempUpdateEvent::Subscription subscription) {
this->tempUpdateEvent.unsubscribe(subscription);
}
};

View File

@@ -0,0 +1,337 @@
/*
sys-clk manager, a sys-clk frontend homebrew
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
Copyright (C) 2019 m4xw
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "status_tab.h"
#include "ipc/client.h"
#include "utils.h"
#define DANGEROUS_TEMP_THRESHOLD 84000
#define DANGEROUS_TEMP_COLOR nvgRGB(255, 85, 0)
StatusTab::StatusTab(RefreshTask *refreshTask) :
brls::List(),
refreshTask(refreshTask)
{
// Get context
SysClkContext context;
Result rc = sysclkIpcGetCurrentContext(&context);
if (R_FAILED(rc))
{
brls::Logger::error("Unable to get context");
errorResult("sysclkIpcGetCurrentContext", rc);
brls::Application::crash("Could not get the current sys-clk context, please check that it is correctly installed and enabled.");
return;
}
// Customize list
this->setSpacing(10);
this->setMarginBottom(0);
// Enabled option
brls::ToggleListItem *serviceEnabledListItem = new brls::ToggleListItem("Enable service", context.enabled, "", "Yes", "No");
serviceEnabledListItem->getClickEvent()->subscribe([this, serviceEnabledListItem](View* view)
{
bool enabled = serviceEnabledListItem->getToggleState();
brls::Logger::info("New service state = %d", enabled);
Result rc = sysclkIpcSetEnabled(enabled);
if (R_FAILED(rc))
{
brls::Logger::error("Unable to set enabled state");
errorResult("sysclkIpcSetEnabled", rc);
// TODO: Put it back to on / off
}
this->refreshTask->fireNow();
});
this->addView(serviceEnabledListItem);
// Frequencies
brls::Header *freqsHeader = new brls::Header("Frequencies");
this->addView(freqsHeader);
brls::BoxLayout *freqsBox = new brls::BoxLayout(brls::BoxLayoutOrientation::VERTICAL);
freqsBox->setSpacing(0);
freqsBox->setHeight(60);
this->addView(freqsBox);
StatusGrid *freqsLayout = new StatusGrid();
freqsLayout->setSpacing(22);
freqsLayout->setHeight(40);
this->cpuFreqCell = new StatusCell("CPU", formatFreq(context.freqs[SysClkModule_CPU]));
this->gpuFreqCell = new StatusCell("GPU", formatFreq(context.freqs[SysClkModule_GPU]));
this->memFreqCell = new StatusCell("MEM", formatFreq(context.freqs[SysClkModule_MEM]));
freqsLayout->addView(this->cpuFreqCell);
freqsLayout->addView(this->gpuFreqCell);
freqsLayout->addView(this->memFreqCell);
freqsBox->addView(freqsLayout);
StatusGrid *realFreqsLayout = new StatusGrid();
realFreqsLayout->setSpacing(22);
realFreqsLayout->setHeight(24);
this->realCpuFreqCell = new StatusCell("\uE090", formatFreq(context.realFreqs[SysClkModule_CPU]));
this->realGpuFreqCell = new StatusCell("\uE090", formatFreq(context.realFreqs[SysClkModule_GPU]));
this->realMemFreqCell = new StatusCell("\uE090", formatFreq(context.realFreqs[SysClkModule_MEM]));
realFreqsLayout->addView(this->realCpuFreqCell);
realFreqsLayout->addView(this->realGpuFreqCell);
realFreqsLayout->addView(this->realMemFreqCell);
freqsBox->addView(realFreqsLayout);
// Temperatures
brls::Header *temperaturesHeader = new brls::Header("Temperatures");
this->addView(temperaturesHeader);
StatusGrid *tempsLayout = new StatusGrid();
tempsLayout->setSpacing(22);
tempsLayout->setHeight(40);
this->skinTempCell = new StatusCell("Skin", formatTemp(context.temps[SysClkThermalSensor_Skin]));
this->socTempCell = new StatusCell("SOC", formatTemp(context.temps[SysClkThermalSensor_SOC]));
this->pcbTempCell = new StatusCell("PCB", formatTemp(context.temps[SysClkThermalSensor_PCB]));
if (context.temps[SysClkThermalSensor_SOC] > DANGEROUS_TEMP_THRESHOLD)
this->socTempCell->setValueColor(DANGEROUS_TEMP_COLOR);
if (context.temps[SysClkThermalSensor_PCB] > DANGEROUS_TEMP_THRESHOLD)
this->pcbTempCell->setValueColor(DANGEROUS_TEMP_COLOR);
if (context.temps[SysClkThermalSensor_Skin] > DANGEROUS_TEMP_THRESHOLD)
this->skinTempCell->setValueColor(DANGEROUS_TEMP_COLOR);
tempsLayout->addView(this->socTempCell);
tempsLayout->addView(this->pcbTempCell);
tempsLayout->addView(this->skinTempCell);
this->addView(tempsLayout);
// Power
brls::Header *powerHeader = new brls::Header("Power");
this->addView(powerHeader);
StatusGrid *powerLayout = new StatusGrid();
powerLayout->setSpacing(22);
powerLayout->setHeight(40);
this->nowPowerCell = new StatusCell("Now", formatPower(context.power[SysClkPowerSensor_Now]));
this->avgPowerCell = new StatusCell("Avg", formatPower(context.power[SysClkPowerSensor_Avg]));
powerLayout->addView(new StatusCell("", ""));
powerLayout->addView(this->nowPowerCell);
powerLayout->addView(this->avgPowerCell);
this->addView(powerLayout);
// Info
brls::Header *systemHeader = new brls::Header("System");
this->addView(systemHeader);
InfoGrid *infoLayout = new InfoGrid();
infoLayout->setSpacing(22);
infoLayout->setHeight(40);
this->profileCell = new StatusCell("Profile", formatProfile(context.profile));
this->tidCell = new StatusCell("Application ID", formatTid(context.applicationId));
infoLayout->addView(this->profileCell);
infoLayout->addView(this->tidCell);
this->addView(infoLayout);
// Setup refresh task
this->freqListenerSub = refreshTask->registerFreqListener([this](SysClkModule module, uint32_t freq) {
switch(module)
{
case SysClkModule_CPU:
this->cpuFreqCell->setValue(formatFreq(freq));
break;
case SysClkModule_GPU:
this->gpuFreqCell->setValue(formatFreq(freq));
break;
case SysClkModule_MEM:
this->memFreqCell->setValue(formatFreq(freq));
break;
default:
break;
}
});
this->realFreqListenerSub = refreshTask->registerFreqListener([this](SysClkModule module, uint32_t freq) {
switch(module)
{
case SysClkModule_CPU:
this->realCpuFreqCell->setValue(formatFreq(freq));
break;
case SysClkModule_GPU:
this->realGpuFreqCell->setValue(formatFreq(freq));
break;
case SysClkModule_MEM:
this->realMemFreqCell->setValue(formatFreq(freq));
break;
default:
break;
}
});
this->appIdListenerSub = refreshTask->registerAppIdListener([this](uint64_t tid) {
this->tidCell->setValue(formatTid(tid));
});
this->profileListenerSub = refreshTask->registerProfileListener([this](SysClkProfile profile) {
this->profileCell->setValue(formatProfile(profile));
});
this->tempListenerSub = refreshTask->registerTempListener([this](SysClkThermalSensor sensor, uint32_t temp) {
switch (sensor)
{
case SysClkThermalSensor_PCB:
this->pcbTempCell->setValue(formatTemp(temp));
if (temp > DANGEROUS_TEMP_THRESHOLD)
this->pcbTempCell->setValueColor(DANGEROUS_TEMP_COLOR);
else
this->pcbTempCell->resetValueColor();
break;
case SysClkThermalSensor_SOC:
this->socTempCell->setValue(formatTemp(temp));
if (temp > DANGEROUS_TEMP_THRESHOLD)
this->socTempCell->setValueColor(DANGEROUS_TEMP_COLOR);
else
this->socTempCell->resetValueColor();
break;
case SysClkThermalSensor_Skin:
this->skinTempCell->setValue(formatTemp(temp));
if (temp > DANGEROUS_TEMP_THRESHOLD)
this->skinTempCell->setValueColor(DANGEROUS_TEMP_COLOR);
else
this->skinTempCell->resetValueColor();
break;
default:
break;
}
});
}
StatusTab::~StatusTab()
{
refreshTask->unregisterFreqListener(this->freqListenerSub);
refreshTask->unregisterRealFreqListener(this->realFreqListenerSub);
refreshTask->unregisterAppIdListener(this->appIdListenerSub);
refreshTask->unregisterProfileListener(this->profileListenerSub);
refreshTask->unregisterTempListener(this->tempListenerSub);
}
StatusGrid::StatusGrid()
: BoxLayout(brls::BoxLayoutOrientation::HORIZONTAL)
{
}
void StatusGrid::layout(NVGcontext* vg, brls::Style *style, brls::FontStash *stash)
{
// Distribute space equally between children
if (!this->children.empty())
{
size_t size = this->children.size();
unsigned width = (this->getWidth()- this->marginLeft - this->marginRight - this->getSpacing() * (size - 1)) / size;
for (brls::BoxLayoutChild *child : this->children)
{
if (!child->fill)
child->view->setWidth(width);
}
}
// Layout normally
BoxLayout::layout(vg, style, stash);
}
InfoGrid::InfoGrid() : BoxLayout(brls::BoxLayoutOrientation::HORIZONTAL)
{
}
void InfoGrid::layout(NVGcontext* vg, brls::Style *style, brls::FontStash *stash)
{
// Give first child one third of the view, give second child the rest
if (this->children.size() >= 2)
{
size_t size = 3;
unsigned firstWidth = (this->getWidth()- this->marginLeft - this->marginRight - this->getSpacing() * (size - 1)) / size;
unsigned secondWidth = firstWidth * 2 + this->getSpacing();
this->children[0]->view->setWidth(firstWidth);
this->children[1]->view->setWidth(secondWidth);
}
// Layout normally
BoxLayout::layout(vg, style, stash);
}
StatusCell::StatusCell(std::string label, std::string value) : label(label), value(value)
{
this->resetValueColor();
}
void StatusCell::setValue(std::string value)
{
this->value = value;
}
void StatusCell::draw(NVGcontext *vg, int x, int y, unsigned width, unsigned height, brls::Style *style, brls::FrameContext *ctx)
{
unsigned padding = 5;
// Label
nvgBeginPath(vg);
nvgFillColor(vg, a(ctx->theme->tableBodyTextColor));
nvgFontSize(vg, 16);
nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_BASELINE);
nvgText(vg, x + padding, y + height / 2, this->label.c_str(), nullptr);
// Value
nvgBeginPath(vg);
nvgFillColor(vg, a(this->valueColor));
nvgFontSize(vg, 20);
nvgTextAlign(vg, NVG_ALIGN_RIGHT | NVG_ALIGN_BASELINE);
nvgText(vg, x + width - padding, y + height / 2, this->value.c_str(), nullptr);
}
void StatusCell::setValueColor(NVGcolor color)
{
this->valueColor = color;
}
void StatusCell::resetValueColor()
{
this->valueColor = brls::Application::getThemeValues()->textColor;
}

View File

@@ -0,0 +1,93 @@
/*
sys-clk manager, a sys-clk frontend homebrew
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
Copyright (C) 2019 m4xw
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis.hpp>
#include "refresh_task.h"
class StatusCell : public brls::View
{
private:
std::string label;
std::string value;
NVGcolor valueColor;
public:
StatusCell(std::string label, std::string value);
void draw(NVGcontext *vg, int x, int y, unsigned width, unsigned height, brls::Style *style, brls::FrameContext *ctx) override;
void setValue(std::string value);
void setValueColor(NVGcolor color);
void resetValueColor();
};
class InfoGrid : public brls::BoxLayout
{
public:
InfoGrid();
void layout(NVGcontext* vg, brls::Style *style, brls::FontStash *stash) override;
};
class StatusGrid : public brls::BoxLayout
{
public:
StatusGrid();
void layout(NVGcontext* vg, brls::Style *style, brls::FontStash *stash) override;
};
class StatusTab : public brls::List
{
private:
RefreshTask *refreshTask;
FreqUpdateEvent::Subscription freqListenerSub;
FreqUpdateEvent::Subscription realFreqListenerSub;
AppIdUpdateEvent::Subscription appIdListenerSub;
ProfileUpdateEvent::Subscription profileListenerSub;
TempUpdateEvent::Subscription tempListenerSub;
StatusCell *cpuFreqCell;
StatusCell *gpuFreqCell;
StatusCell *memFreqCell;
StatusCell *realCpuFreqCell;
StatusCell *realGpuFreqCell;
StatusCell *realMemFreqCell;
StatusCell *socTempCell;
StatusCell *pcbTempCell;
StatusCell *skinTempCell;
StatusCell *nowPowerCell;
StatusCell *avgPowerCell;
StatusCell *profileCell;
StatusCell *tidCell;
public:
StatusTab(RefreshTask *refreshTask);
~StatusTab();
};

View File

@@ -0,0 +1,145 @@
/*
sys-clk manager, a sys-clk frontend homebrew
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
Copyright (C) 2019 m4xw
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "utils.h"
#include <borealis.hpp>
#include "ipc/client.h"
uint32_t g_freq_table_hz[SysClkModule_EnumMax][SYSCLK_FREQ_LIST_MAX+1];
Result cacheFreqList()
{
Result rc;
for(uint32_t i = 0; i < SysClkModule_EnumMax; i++)
{
rc = sysclkIpcGetFreqList((SysClkModule)i, &g_freq_table_hz[i][1], SYSCLK_FREQ_LIST_MAX, &g_freq_table_hz[i][0]);
if(R_FAILED(rc))
{
return rc;
}
}
return 0;
}
std::string formatFreq(uint32_t freq)
{
char str[16];
snprintf(str, sizeof(str), "%.1f MHz", (float)freq / 1000000.0f);
return std::string(str);
}
std::string formatTid(uint64_t tid)
{
char str[17];
snprintf(str, sizeof(str), "%016lX", tid);
return std::string(str);
}
std::string formatProfile(SysClkProfile profile)
{
return std::string(sysclkFormatProfile(profile, true));
}
std::string formatTemp(uint32_t temp)
{
char str[16];
snprintf(str, sizeof(str), "%.1f °C", (float)temp / 1000.0f);
return std::string(str);
}
std::string formatPower(int32_t power)
{
char str[16];
snprintf(str, sizeof(str), "%d mW", power);
return std::string(str);
}
void errorResult(std::string tag, Result rc)
{
#ifdef __SWITCH__
brls::Logger::error("[0x%x] %s failed - %04d-%04d", rc, tag.c_str(), R_MODULE(rc), R_DESCRIPTION(rc));
#else
brls::Logger::error("[0x%x] %s failed - xxxx-xxxx", rc, tag.c_str());
#endif
}
// TODO: Merge ticker for single line labels in Borealis and remove usage of this
std::string formatListItemTitle(const std::string str, size_t maxScore)
{
size_t score = 0;
for (size_t i = 0; i < str.length(); i++)
{
score += std::isupper(str[i]) ? 4 : 3;
if(score > maxScore)
{
return str.substr(0, i-1) + "\u2026";
}
}
return str;
}
brls::SelectListItem* createFreqListItem(SysClkModule module, uint32_t selectedFreqInMHz, std::string defaultString)
{
std::string name;
switch (module)
{
case SysClkModule_CPU:
name = "CPU Frequency";
break;
case SysClkModule_GPU:
name = "GPU Frequency";
break;
case SysClkModule_MEM:
name = "MEM Frequency";
break;
default:
return nullptr;
}
uint32_t* table = &g_freq_table_hz[module][0];
size_t selected = 0;
size_t i = 1;
std::vector<std::string> clocks;
clocks.push_back(defaultString);
while (i <= table[0])
{
uint32_t freq = table[i];
if (freq / 1000000 == selectedFreqInMHz)
selected = i;
char clock[16];
snprintf(clock, sizeof(clock), "%d MHz", freq / 1000000);
clocks.push_back(std::string(clock));
i++;
}
return new brls::SelectListItem(name, clocks, selected);
}

View File

@@ -0,0 +1,44 @@
/*
sys-clk manager, a sys-clk frontend homebrew
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
Copyright (C) 2019 m4xw
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <string>
#include <sysclk.h>
#include "ipc/ipc.h"
#include <borealis.hpp>
#define APP_ASSET(p) APP_RESOURCES p
extern uint32_t g_freq_table_hz[SysClkModule_EnumMax][SYSCLK_FREQ_LIST_MAX+1];
Result cacheFreqList();
std::string formatListItemTitle(const std::string str, size_t maxScore = 140);
brls::SelectListItem* createFreqListItem(SysClkModule module, uint32_t selectedFreqInMHz, std::string defaultString = "Do not override");
std::string formatFreq(uint32_t freq);
std::string formatTid(uint64_t tid);
std::string formatProfile(SysClkProfile profile);
std::string formatTemp(uint32_t temp);
std::string formatPower(int32_t power);
void errorResult(std::string tag, Result rc);