forked from NG-SD-Plugins/PowerTools
This commit is contained in:
parent
9590a90722
commit
1538f9a862
25 changed files with 726 additions and 482 deletions
89
README.md
89
README.md
|
@ -15,95 +15,6 @@ You will need that installed for this plugin to work.
|
|||
- Display supplementary battery info
|
||||
- Keep settings between restarts (stored in `~/.config/powertools/<appid>.json`)
|
||||
|
||||
## Cool, but that's too much work
|
||||
|
||||
Fair enough.
|
||||
In case you still want some of the functionality, without the nice GUI, here's some equivalent commands.
|
||||
These should all be run as superuser, i.e. run `sudo su` and then run these commands in that.
|
||||
|
||||
### Enable & Disable CPU threads
|
||||
|
||||
Enable: `echo 1 > /sys/devices/system/cpu/cpu{cpu_number}/online` where `{cpu_number}` is a number from 1 to 7 (inclusive).
|
||||
|
||||
Disable: `echo 0 > /sys/devices/system/cpu/cpu{cpu_number}/online` where `{cpu_number}` is a number from 1 to 7 (inclusive).
|
||||
|
||||
NOTE: You cannot enable or disable cpu0, hence why there are only 7 in the range for 8 cpu threads.
|
||||
|
||||
### Enable & Disable CPU boost
|
||||
|
||||
Enable: `echo 1 > /sys/devices/system/cpu/cpufreq/boost` enables boost across all threads.
|
||||
|
||||
Disable: `echo 0 > /sys/devices/system/cpu/cpufreq/boost` disables boost across all threads.
|
||||
|
||||
### Set CPU frequency
|
||||
|
||||
Use `cpupower` (usage: `cpupower --help`).
|
||||
This isn't strictly how PowerTools does it, but it's a multi-step process which can involve changing the CPU governor.
|
||||
All that can be done automatically by `cpupower frequency-set --freq {frequency}` where `{frequency}` is `1.7G`, `2.4G` or `2.8G`.
|
||||
|
||||
### Set GPU Power
|
||||
|
||||
Set Slow Powerplay Table (PPT):`echo {microwatts} > /sys/class/hwmon/hwmon4/power1_cap` where `{microwatts}` is a wattage in millionths of a Watt. This doesn't seem to do a lot.
|
||||
|
||||
Set Fast Powerplay Table (PPT): `echo {microwatts} > /sys/class/hwmon/hwmon4/power2_cap` where `{microwatts}` is a wattage in millionths of a Watt.
|
||||
|
||||
Get the entry limits for those two commands with `cat /sys/class/hwmon/hwmon4/power{number}_cap_max` where `{number}` is `1` (slowPPT) or `2` (fastPPT).
|
||||
|
||||
### Set Fan speed
|
||||
|
||||
NOTE: PowerTools no longer supports this, since [Fantastic](https://github.com/NGnius/Fantastic) does it much better.
|
||||
|
||||
Enable automatic control: `echo 0 > /sys/class/hwmon/hwmon5/recalculate` enables automatic fan control.
|
||||
|
||||
Disable automatic control: `echo 1 > /sys/class/hwmon/hwmon5/recalculate` disables automatic (temperature-based) fan control and starts using the set fan target instead.
|
||||
|
||||
Set the fan speed: `echo {rpm} > /sys/class/hwmon/hwmon5/fan1_target` where `{rpm}` is the RPM.
|
||||
|
||||
Read the actual fan RPM: `cat /sys/class/hwmon/hwmon5/fan1_input` gives the fan speed.
|
||||
|
||||
NOTE: There's a bug in the fan controller; if you enable automatic fan control it will forget any previously-set target despite it appearing to be set correctly (i.e. `cat /sys/class/hwmon/hwmon5/fan1_target` will display the correct value).
|
||||
When you disable automatic fan control, you will need to set the fan RPM again.
|
||||
|
||||
### Battery stats
|
||||
|
||||
Get the battery charge right now: `cat /sys/class/hwmon/hwmon2/device/charge_now` gives charge in uAh (uAh * 7.7/1000000 = charge in Wh).
|
||||
|
||||
Get the maximum battery capacity: `cat /sys/class/hwmon/hwmon2/device/charge_full` gives charge in uAh.
|
||||
|
||||
Get the design battery capacity: `cat /sys/class/hwmon/hwmon2/device/charge_full_design` gives charge in uAh.
|
||||
|
||||
Get whether the deck is plugged in: `cat /sys/class/hwmon/hwmon5/curr1_input` gives the charger current in mA.
|
||||
|
||||
NOTE: 7.7 is the voltage of the battery -- it's not just a magic number.
|
||||
|
||||
### Steam Deck kernel patches
|
||||
|
||||
This is how I figured out how the fan stuff works.
|
||||
I've only scratched the surface of what this code allows, I'm sure it has more useful information.
|
||||
https://lkml.org/lkml/2022/2/5/391
|
||||
|
||||
### Game launch detection
|
||||
|
||||
```typescript
|
||||
//@ts-ignore
|
||||
let lifetimeHook = SteamClient.GameSessions.RegisterForAppLifetimeNotifications((update) => {
|
||||
if (update.bRunning) {
|
||||
console.log("AppID " + update.unAppID.toString() + " is now running");
|
||||
} else {
|
||||
console.log("AppID " + update.unAppID.toString() + " is no longer running");
|
||||
// game exit code here
|
||||
// NOTE: custom games always have 0 as AppID, so AppID is bad to use as ID
|
||||
}
|
||||
});
|
||||
//@ts-ignore
|
||||
let startHook = SteamClient.Apps.RegisterForGameActionStart((actionType, id) => {
|
||||
//@ts-ignore
|
||||
let gameInfo: any = appStore.GetAppOverviewByGameID(id);
|
||||
// game start code here
|
||||
// NOTE: GameID (variable: id) is always unique, even for custom games, so it's better to use than AppID
|
||||
});
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
This is licensed under GNU GPLv3.
|
||||
|
|
2
main.py
2
main.py
|
@ -13,4 +13,4 @@ class Plugin:
|
|||
# startup
|
||||
self.backend_proc = subprocess.Popen([PARENT_DIR + "/bin/backend"])
|
||||
while True:
|
||||
asyncio.sleep(1)
|
||||
await asyncio.sleep(1)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "PowerTools",
|
||||
"version": "0.7.0",
|
||||
"version": "1.0.0-alpha",
|
||||
"description": "Power tweaks for power users",
|
||||
"scripts": {
|
||||
"build": "shx rm -rf dist && rollup -c",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "PowerTools",
|
||||
"author": "NGnius",
|
||||
"flags": ["root", "_debug"],
|
||||
"flags": ["root", "debug"],
|
||||
"publish": {
|
||||
"discord_id": "106537989684887552",
|
||||
"description": "Power tweaks for power users",
|
||||
|
|
4
powertools-rs/Cargo.lock
generated
4
powertools-rs/Cargo.lock
generated
|
@ -1054,8 +1054,6 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "usdpl-back"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbbc0781e83ba990f8239142e33173a2d2548701775f3db66702d1af4fd0319a"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"hex",
|
||||
|
@ -1068,8 +1066,6 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "usdpl-core"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "862153581fac266458521f49e5906a71c1eee1665cb4c7d71e9586bd34b45394"
|
||||
dependencies = [
|
||||
"aes-gcm-siv",
|
||||
"base64",
|
||||
|
|
|
@ -6,7 +6,7 @@ edition = "2021"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
usdpl-back = { version = "0.6.0", features = ["blocking"]}
|
||||
usdpl-back = { version = "0.6.0", features = ["blocking"], path = "../../usdpl-rs/usdpl-back"}
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
#!/bin/bash
|
||||
|
||||
cargo build --release --target x86_64-unknown-linux-musl
|
||||
mkdir ../bin &> /dev/null
|
||||
cp ./target/release/powertools-rs ../bin/backend
|
||||
#cargo build --release --target x86_64-unknown-linux-musl
|
||||
cargo build --target x86_64-unknown-linux-musl
|
||||
#cross build --release
|
||||
|
||||
mkdir -p ../bin
|
||||
#cp ./target/x86_64-unknown-linux-musl/release/powertools-rs ../bin/backend
|
||||
cp ./target/x86_64-unknown-linux-musl/debug/powertools-rs ../bin/backend
|
||||
#cp ./target/release/powertools-rs ../bin/backend
|
||||
|
|
|
@ -18,10 +18,12 @@ pub fn set_persistent(
|
|||
unwrap_lock(saver.lock(), "save channel").send(()),
|
||||
"Failed to send on save channel",
|
||||
);
|
||||
super::utility::map_empty_result(
|
||||
let result = super::utility::map_empty_result(
|
||||
settings_lock.on_set(),
|
||||
settings_lock.persistent,
|
||||
)
|
||||
);
|
||||
log::debug!("Persistent is now {}", settings_lock.persistent);
|
||||
result
|
||||
} else {
|
||||
vec!["set_persistent missing parameter".into()]
|
||||
}
|
||||
|
@ -46,9 +48,14 @@ pub fn load_settings(
|
|||
move |params_in: super::ApiParameterType| {
|
||||
if let Some(Primitive::String(path)) = params_in.get(0) {
|
||||
if let Some(Primitive::String(name)) = params_in.get(1) {
|
||||
super::utility::map_result(
|
||||
settings.load_file(path.into(), name.to_owned())
|
||||
match settings.load_file(path.into(), name.to_owned()) {
|
||||
Err(e) => vec![e.msg.into()],
|
||||
Ok(success) =>
|
||||
super::utility::map_empty_result(
|
||||
settings.clone().on_set(),
|
||||
success
|
||||
)
|
||||
}
|
||||
} else {
|
||||
vec!["load_settings missing name parameter".into()]
|
||||
}
|
||||
|
@ -59,6 +66,24 @@ pub fn load_settings(
|
|||
}
|
||||
}
|
||||
|
||||
/// Generate load default settings from file web method
|
||||
pub fn load_default_settings(
|
||||
settings: Settings,
|
||||
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||
move |_: super::ApiParameterType| {
|
||||
match settings.load_file(
|
||||
crate::consts::DEFAULT_SETTINGS_FILE.into(),
|
||||
crate::consts::DEFAULT_SETTINGS_NAME.to_owned()
|
||||
) {
|
||||
Err(e) => vec![e.msg.into()],
|
||||
Ok(success) => super::utility::map_empty_result(
|
||||
settings.clone().on_set(),
|
||||
success
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate get current settings name
|
||||
pub fn get_name(
|
||||
settings: Arc<Mutex<General>>,
|
||||
|
@ -71,3 +96,16 @@ pub fn get_name(
|
|||
.into()]
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate get current settings name
|
||||
pub fn lock_unlock_all(
|
||||
settings: Settings,
|
||||
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||
move |_: super::ApiParameterType| {
|
||||
let _lock = unwrap_lock(settings.general.lock(), "general");
|
||||
let _lock = unwrap_lock(settings.cpus.lock(), "cpus");
|
||||
let _lock = unwrap_lock(settings.gpu.lock(), "gpu");
|
||||
let _lock = unwrap_lock(settings.battery.lock(), "battery");
|
||||
vec![true.into()]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,19 +3,27 @@ use usdpl_back::core::serdes::Primitive;
|
|||
|
||||
use crate::settings::SettingError;
|
||||
|
||||
#[inline]
|
||||
pub fn map_result<T: Into<Primitive>>(result: Result<T, SettingError>) -> super::ApiParameterType {
|
||||
match result {
|
||||
Ok(val) => vec![val.into()],
|
||||
Err(e) => vec![e.msg.into()],
|
||||
Err(e) => {
|
||||
log::debug!("Mapping error to primitive: {}", e);
|
||||
vec![e.msg.into()]
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn map_empty_result<T: Into<Primitive>>(
|
||||
result: Result<(), SettingError>,
|
||||
success: T,
|
||||
) -> super::ApiParameterType {
|
||||
match result {
|
||||
Ok(_) => vec![success.into()],
|
||||
Err(e) => vec![e.msg.into()],
|
||||
Err(e) => {
|
||||
log::debug!("Mapping error to primitive: {}", e);
|
||||
vec![e.msg.into()]
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,3 +4,4 @@ pub const PACKAGE_NAME: &'static str = env!("CARGO_PKG_NAME");
|
|||
pub const PACKAGE_VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
pub const DEFAULT_SETTINGS_FILE: &str = "default_settings.json";
|
||||
pub const DEFAULT_SETTINGS_NAME: &str = "Default";
|
||||
|
|
|
@ -9,6 +9,8 @@ mod resume_worker;
|
|||
mod save_worker;
|
||||
mod utility;
|
||||
|
||||
use settings::OnSet;
|
||||
|
||||
use simplelog::{LevelFilter, WriteLogger};
|
||||
|
||||
use usdpl_back::core::serdes::Primitive;
|
||||
|
@ -32,118 +34,124 @@ fn main() -> Result<(), ()> {
|
|||
log::info!("Starting back-end ({} v{})", PACKAGE_NAME, PACKAGE_VERSION);
|
||||
println!("Starting back-end ({} v{})", PACKAGE_NAME, PACKAGE_VERSION);
|
||||
|
||||
let default_settings = persist::SettingsJson::open(settings_dir().join(DEFAULT_SETTINGS_FILE))
|
||||
let mut loaded_settings = persist::SettingsJson::open(utility::settings_dir().join(DEFAULT_SETTINGS_FILE))
|
||||
.map(|settings| settings::Settings::from_json(settings, DEFAULT_SETTINGS_FILE.into()))
|
||||
.unwrap_or_else(|_| settings::Settings::system_default(DEFAULT_SETTINGS_FILE.into()));
|
||||
|
||||
log::debug!("Settings: {:?}", default_settings);
|
||||
log::debug!("Settings: {:?}", loaded_settings);
|
||||
|
||||
let (_save_handle, save_sender) = save_worker::spawn(default_settings.clone());
|
||||
let _resume_handle = resume_worker::spawn(default_settings.clone());
|
||||
let (_save_handle, save_sender) = save_worker::spawn(loaded_settings.clone());
|
||||
let _resume_handle = resume_worker::spawn(loaded_settings.clone());
|
||||
|
||||
if let Err(e) = loaded_settings.on_set() {
|
||||
log::error!("Startup Settings.on_set() error: {}", e);
|
||||
}
|
||||
|
||||
Instance::new(PORT)
|
||||
.register("hello", |_: Vec<Primitive>| {
|
||||
vec![format!("Hello {}", PACKAGE_NAME).into()]
|
||||
.register("V_INFO", |_: Vec<Primitive>| {
|
||||
vec![format!("{} v{}", PACKAGE_NAME, PACKAGE_VERSION).into()]
|
||||
})
|
||||
// battery API functions
|
||||
.register("BATTERY_current_now", api::battery::current_now)
|
||||
.register(
|
||||
"BATTERY_set_charge_rate",
|
||||
api::battery::set_charge_rate(default_settings.battery.clone(), save_sender.clone()),
|
||||
api::battery::set_charge_rate(loaded_settings.battery.clone(), save_sender.clone()),
|
||||
)
|
||||
.register(
|
||||
"BATTERY_get_charge_rate",
|
||||
api::battery::get_charge_rate(default_settings.battery.clone()),
|
||||
api::battery::get_charge_rate(loaded_settings.battery.clone()),
|
||||
)
|
||||
.register(
|
||||
"BATTERY_unset_charge_rate",
|
||||
api::battery::unset_charge_rate(default_settings.battery.clone(), save_sender.clone()),
|
||||
api::battery::unset_charge_rate(loaded_settings.battery.clone(), save_sender.clone()),
|
||||
)
|
||||
// cpu API functions
|
||||
.register("CPU_count", api::cpu::max_cpus)
|
||||
.register(
|
||||
"CPU_set_online",
|
||||
api::cpu::set_cpu_online(default_settings.cpus.clone(), save_sender.clone())
|
||||
api::cpu::set_cpu_online(loaded_settings.cpus.clone(), save_sender.clone())
|
||||
)
|
||||
.register(
|
||||
"CPU_get_onlines",
|
||||
api::cpu::get_cpus_online(default_settings.cpus.clone())
|
||||
api::cpu::get_cpus_online(loaded_settings.cpus.clone())
|
||||
)
|
||||
.register(
|
||||
"CPU_set_clock_limits",
|
||||
api::cpu::set_clock_limits(default_settings.cpus.clone(), save_sender.clone())
|
||||
api::cpu::set_clock_limits(loaded_settings.cpus.clone(), save_sender.clone())
|
||||
)
|
||||
.register(
|
||||
"CPU_get_clock_limits",
|
||||
api::cpu::get_clock_limits(default_settings.cpus.clone())
|
||||
api::cpu::get_clock_limits(loaded_settings.cpus.clone())
|
||||
)
|
||||
.register(
|
||||
"CPU_unset_clock_limits",
|
||||
api::cpu::unset_clock_limits(default_settings.cpus.clone(), save_sender.clone())
|
||||
api::cpu::unset_clock_limits(loaded_settings.cpus.clone(), save_sender.clone())
|
||||
)
|
||||
.register(
|
||||
"CPU_set_governor",
|
||||
api::cpu::set_cpu_governor(default_settings.cpus.clone(), save_sender.clone())
|
||||
api::cpu::set_cpu_governor(loaded_settings.cpus.clone(), save_sender.clone())
|
||||
)
|
||||
.register(
|
||||
"CPU_get_governors",
|
||||
api::cpu::get_cpu_governors(default_settings.cpus.clone())
|
||||
api::cpu::get_cpu_governors(loaded_settings.cpus.clone())
|
||||
)
|
||||
// gpu API functions
|
||||
.register(
|
||||
"GPU_set_ppt",
|
||||
api::gpu::set_ppt(default_settings.gpu.clone(), save_sender.clone())
|
||||
api::gpu::set_ppt(loaded_settings.gpu.clone(), save_sender.clone())
|
||||
)
|
||||
.register(
|
||||
"GPU_get_ppt",
|
||||
api::gpu::get_ppt(default_settings.gpu.clone())
|
||||
api::gpu::get_ppt(loaded_settings.gpu.clone())
|
||||
)
|
||||
.register(
|
||||
"GPU_unset_ppt",
|
||||
api::gpu::unset_ppt(default_settings.gpu.clone(), save_sender.clone())
|
||||
api::gpu::unset_ppt(loaded_settings.gpu.clone(), save_sender.clone())
|
||||
)
|
||||
.register(
|
||||
"GPU_set_clock_limits",
|
||||
api::gpu::set_clock_limits(default_settings.gpu.clone(), save_sender.clone())
|
||||
api::gpu::set_clock_limits(loaded_settings.gpu.clone(), save_sender.clone())
|
||||
)
|
||||
.register(
|
||||
"GPU_get_clock_limits",
|
||||
api::gpu::get_clock_limits(default_settings.gpu.clone())
|
||||
api::gpu::get_clock_limits(loaded_settings.gpu.clone())
|
||||
)
|
||||
.register(
|
||||
"GPU_unset_clock_limits",
|
||||
api::gpu::unset_clock_limits(default_settings.gpu.clone(), save_sender.clone())
|
||||
api::gpu::unset_clock_limits(loaded_settings.gpu.clone(), save_sender.clone())
|
||||
)
|
||||
.register(
|
||||
"GPU_set_slow_memory",
|
||||
api::gpu::set_slow_memory(default_settings.gpu.clone(), save_sender.clone())
|
||||
api::gpu::set_slow_memory(loaded_settings.gpu.clone(), save_sender.clone())
|
||||
)
|
||||
.register(
|
||||
"GPU_get_slow_memory",
|
||||
api::gpu::get_slow_memory(default_settings.gpu.clone())
|
||||
api::gpu::get_slow_memory(loaded_settings.gpu.clone())
|
||||
)
|
||||
// general API functions
|
||||
.register(
|
||||
"GENERAL_set_persistent",
|
||||
api::general::set_persistent(default_settings.general.clone(), save_sender.clone())
|
||||
api::general::set_persistent(loaded_settings.general.clone(), save_sender.clone())
|
||||
)
|
||||
.register(
|
||||
"GENERAL_get_persistent",
|
||||
api::general::get_persistent(default_settings.general.clone())
|
||||
api::general::get_persistent(loaded_settings.general.clone())
|
||||
)
|
||||
.register(
|
||||
"GENERAL_load_settings",
|
||||
api::general::load_settings(default_settings.clone())
|
||||
api::general::load_settings(loaded_settings.clone())
|
||||
)
|
||||
.register(
|
||||
"GENERAL_load_default_settings",
|
||||
api::general::load_default_settings(loaded_settings.clone())
|
||||
)
|
||||
.register(
|
||||
"GENERAL_get_name",
|
||||
api::general::get_name(default_settings.general.clone())
|
||||
api::general::get_name(loaded_settings.general.clone())
|
||||
)
|
||||
.register(
|
||||
"GENERAL_wait_for_unlocks",
|
||||
api::general::lock_unlock_all(loaded_settings.clone())
|
||||
)
|
||||
.run_blocking()
|
||||
}
|
||||
|
||||
fn settings_dir() -> std::path::PathBuf {
|
||||
usdpl_back::api::dirs::home()
|
||||
.unwrap_or_else(|| "/home/deck".into())
|
||||
.join(".config/powertools/")
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ impl Default for SettingsJson {
|
|||
fn default() -> Self {
|
||||
Self {
|
||||
version: 0,
|
||||
name: "default".to_owned(),
|
||||
name: crate::consts::DEFAULT_SETTINGS_NAME.to_owned(),
|
||||
persistent: false,
|
||||
cpus: Vec::with_capacity(8),
|
||||
gpu: GpuJson::default(),
|
||||
|
|
|
@ -8,6 +8,7 @@ const ALLOWED_ERROR: f64 = 0.001;
|
|||
|
||||
pub fn spawn(settings: Settings) -> JoinHandle<()> {
|
||||
thread::spawn(move || {
|
||||
log::info!("resume_worker starting...");
|
||||
let duration = Duration::from_millis(5000);
|
||||
let mut start = Instant::now();
|
||||
loop {
|
||||
|
@ -17,11 +18,12 @@ pub fn spawn(settings: Settings) -> JoinHandle<()> {
|
|||
if old_start.as_secs_f64() > duration.as_secs_f64() * (1.0 + ALLOWED_ERROR) {
|
||||
// has just resumed from sleep
|
||||
unwrap_maybe_fatal(settings.on_resume(), "On resume failure");
|
||||
log::info!(
|
||||
log::debug!(
|
||||
"OnResume completed after sleeping for {}s",
|
||||
old_start.as_secs_f32()
|
||||
);
|
||||
}
|
||||
}
|
||||
//log::warn!("resume_worker completed!");
|
||||
})
|
||||
}
|
||||
|
|
|
@ -8,12 +8,17 @@ use crate::utility::{unwrap_lock, unwrap_maybe_fatal};
|
|||
pub fn spawn(settings: Settings) -> (JoinHandle<()>, Sender<()>) {
|
||||
let (sender, receiver): (Sender<()>, Receiver<()>) = mpsc::channel();
|
||||
let worker = thread::spawn(move || {
|
||||
log::info!("save_worker starting...");
|
||||
for _ in receiver.iter() {
|
||||
let save_path = unwrap_lock(settings.general.lock(), "general").path.clone();
|
||||
let save_json: SettingsJson = settings.clone().into();
|
||||
log::debug!("save_worker is saving...");
|
||||
let save_path = crate::utility::settings_dir()
|
||||
.join(unwrap_lock(settings.general.lock(), "general").path.clone());
|
||||
let settings_clone = settings.clone();
|
||||
let save_json: SettingsJson = settings_clone.into();
|
||||
unwrap_maybe_fatal(save_json.save(&save_path), "Failed to save settings");
|
||||
log::debug!("Saved settings to {}", save_path.display());
|
||||
}
|
||||
log::warn!("save_worker completed!");
|
||||
});
|
||||
(worker, sender)
|
||||
}
|
||||
|
|
|
@ -101,6 +101,7 @@ impl Cpu {
|
|||
})?;
|
||||
}
|
||||
// set governor
|
||||
if self.index == 0 || self.online {
|
||||
let governor_path = cpu_governor_path(self.index);
|
||||
usdpl_back::api::files::write_single(&governor_path, &self.governor).map_err(|e| {
|
||||
SettingError {
|
||||
|
@ -111,6 +112,7 @@ impl Cpu {
|
|||
setting: super::SettingVariant::Cpu,
|
||||
}
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -141,7 +143,7 @@ impl Cpu {
|
|||
if let Some(dash_index) = data.find('-') {
|
||||
let data = data.split_off(dash_index + 1);
|
||||
if let Ok(max_cpu) = data.parse::<usize>() {
|
||||
return Some(max_cpu);
|
||||
return Some(max_cpu + 1);
|
||||
}
|
||||
}
|
||||
log::warn!("Failed to parse CPU info from kernel, is Tux evil?");
|
||||
|
|
|
@ -40,7 +40,7 @@ impl OnSet for General {
|
|||
// remove settings file when persistence is turned off, to prevent it from be loaded next time.
|
||||
if !self.persistent && self.path.exists() {
|
||||
std::fs::remove_file(&self.path).map_err(|e| SettingError {
|
||||
msg: e.to_string(),
|
||||
msg: format!("Failed to delete `{}`: {}", self.path.display(), e),
|
||||
setting: SettingVariant::General,
|
||||
})?;
|
||||
}
|
||||
|
@ -67,16 +67,7 @@ impl OnSet for Settings {
|
|||
}
|
||||
}
|
||||
unwrap_lock(self.gpu.lock(), "gpu").on_set()?;
|
||||
{
|
||||
// general lock scope
|
||||
let gen_lock = unwrap_lock(self.general.lock(), "general");
|
||||
if !gen_lock.persistent && gen_lock.path.exists() {
|
||||
std::fs::remove_file(&gen_lock.path).map_err(|e| SettingError {
|
||||
msg: format!("Failed to delete `{}`: {}", gen_lock.path.display(), e),
|
||||
setting: SettingVariant::General,
|
||||
})?;
|
||||
}
|
||||
}
|
||||
unwrap_lock(self.general.lock(), "general").on_set()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -121,7 +112,7 @@ impl Settings {
|
|||
general: Arc::new(Mutex::new(General {
|
||||
persistent: false,
|
||||
path: json_path,
|
||||
name: "".to_owned(),
|
||||
name: crate::consts::DEFAULT_SETTINGS_NAME.to_owned(),
|
||||
})),
|
||||
cpus: Arc::new(Mutex::new(Cpu::system_default())),
|
||||
gpu: Arc::new(Mutex::new(Gpu::system_default())),
|
||||
|
@ -179,17 +170,26 @@ impl OnResume for Settings {
|
|||
impl Into<SettingsJson> for Settings {
|
||||
#[inline]
|
||||
fn into(self) -> SettingsJson {
|
||||
log::debug!("Locking settings to convert into json");
|
||||
let gen_lock = unwrap_lock(self.general.lock(), "general");
|
||||
log::debug!("Got general lock");
|
||||
let cpu_lock = unwrap_lock(self.cpus.lock(), "cpu");
|
||||
log::debug!("Got cpus lock");
|
||||
let gpu_lock = unwrap_lock(self.gpu.lock(), "gpu");
|
||||
log::debug!("Got gpu lock");
|
||||
let batt_lock = unwrap_lock(self.battery.lock(), "battery");
|
||||
log::debug!("Got battery lock");
|
||||
SettingsJson {
|
||||
version: LATEST_VERSION,
|
||||
name: unwrap_lock(self.general.lock(), "general").name.clone(),
|
||||
persistent: unwrap_lock(self.general.lock(), "general").persistent,
|
||||
cpus: unwrap_lock(self.cpus.lock(), "cpu")
|
||||
name: gen_lock.name.clone(),
|
||||
persistent: gen_lock.persistent,
|
||||
cpus: cpu_lock
|
||||
.clone()
|
||||
.drain(..)
|
||||
.map(|cpu| cpu.into())
|
||||
.collect(),
|
||||
gpu: unwrap_lock(self.gpu.lock(), "gpu").clone().into(),
|
||||
battery: unwrap_lock(self.battery.lock(), "battery").clone().into(),
|
||||
gpu: gpu_lock.clone().into(),
|
||||
battery: batt_lock.clone().into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,3 +23,9 @@ pub fn unwrap_lock<'a, T: Sized>(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn settings_dir() -> std::path::PathBuf {
|
||||
usdpl_back::api::dirs::home()
|
||||
.unwrap_or_else(|| "/home/deck".into())
|
||||
.join(".config/powertools/")
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {init_usdpl, target, init_embedded, call_backend} from "usdpl-front";
|
||||
import {init_usdpl, target_usdpl, init_embedded, call_backend} from "usdpl-front";
|
||||
|
||||
const USDPL_PORT: number = 44443;
|
||||
|
||||
|
@ -20,12 +20,16 @@ export async function initBackend() {
|
|||
// init usdpl
|
||||
await init_embedded();
|
||||
init_usdpl(USDPL_PORT);
|
||||
console.log("USDPL started for framework: " + target());
|
||||
console.log("USDPL started for framework: " + target_usdpl());
|
||||
//setReady(true);
|
||||
}
|
||||
|
||||
// API
|
||||
|
||||
export async function getInfo(): Promise<string> {
|
||||
return (await call_backend("V_INFO", []))[0];
|
||||
}
|
||||
|
||||
// Battery
|
||||
|
||||
export async function getBatteryCurrent(): Promise<number> {
|
||||
|
@ -126,6 +130,14 @@ export async function loadGeneralSettings(path: string, name: string): Promise<b
|
|||
return (await call_backend("GENERAL_load_settings", [path, name]))[0];
|
||||
}
|
||||
|
||||
export async function getGeneralPersistent(): Promise<boolean> {
|
||||
export async function loadGeneralDefaultSettings(): Promise<boolean> {
|
||||
return (await call_backend("GENERAL_load_default_settings", []))[0];
|
||||
}
|
||||
|
||||
export async function getGeneralSettingsName(): Promise<boolean> {
|
||||
return (await call_backend("GENERAL_get_name", []))[0];
|
||||
}
|
||||
|
||||
export async function waitForComplete(): Promise<boolean> {
|
||||
return (await call_backend("GENERAL_wait_for_unlocks", []))[0];
|
||||
}
|
||||
|
|
757
src/index.tsx
757
src/index.tsx
|
@ -1,7 +1,7 @@
|
|||
import {
|
||||
//ButtonItem,
|
||||
ButtonItem,
|
||||
definePlugin,
|
||||
DialogButton,
|
||||
//DialogButton,
|
||||
//Menu,
|
||||
//MenuItem,
|
||||
PanelSection,
|
||||
|
@ -19,161 +19,131 @@ import {
|
|||
import { VFC, useState } from "react";
|
||||
import { GiDrill } from "react-icons/gi";
|
||||
|
||||
import * as python from "./python";
|
||||
//import * as python from "./python";
|
||||
import * as backend from "./backend";
|
||||
import {set_value, get_value, target_usdpl, version_usdpl} from "usdpl-front";
|
||||
|
||||
//import logo from "../assets/logo.png";
|
||||
|
||||
// interface AddMethodArgs {
|
||||
// left: number;
|
||||
// right: number;
|
||||
// }
|
||||
|
||||
var firstTime: boolean = true;
|
||||
var versionGlobalHolder: string = "0.0.0-jank";
|
||||
var periodicHook: NodeJS.Timer | null = null;
|
||||
var lastGame: string = "";
|
||||
var lifetimeHook: any = null;
|
||||
var startHook: any = null;
|
||||
var usdplReady = false;
|
||||
|
||||
var smt_backup: boolean = true;
|
||||
var cpus_backup: number = 8;
|
||||
var boost_backup: boolean = true;
|
||||
var freq_backup: number = 8;
|
||||
var slowPPT_backup: number = 1;
|
||||
var fastPPT_backup: number = 1;
|
||||
var chargeNow_backup: number = 5200000;
|
||||
var chargeFull_backup: number = 5200000;
|
||||
var chargeDesign_backup: number = 5200000;
|
||||
var persistent_backup: boolean = false;
|
||||
var perGameProfile_backup: boolean = false;
|
||||
var smtAllowed = true;
|
||||
var smtGlobal = smtAllowed;
|
||||
|
||||
var reload = function(){};
|
||||
// usdpl persistent store keys
|
||||
|
||||
const Content: VFC<{ serverAPI: ServerAPI }> = ({serverAPI}) => {
|
||||
// const [result, setResult] = useState<number | undefined>();
|
||||
const BACKEND_INFO = "VINFO";
|
||||
|
||||
// const onClick = async () => {
|
||||
// const result = await serverAPI.callPluginMethod<AddMethodArgs, number>(
|
||||
// "add",
|
||||
// {
|
||||
// left: 2,
|
||||
// right: 2,
|
||||
// }
|
||||
// );
|
||||
// if (result.success) {
|
||||
// setResult(result.result);
|
||||
// }
|
||||
// };
|
||||
const CURRENT_BATT = "BATTERY_current_now";
|
||||
const CHARGE_RATE_BATT = "BATTERY_charge_rate";
|
||||
|
||||
python.setServer(serverAPI);
|
||||
const TOTAL_CPUS = "CPUs_total";
|
||||
const ONLINE_CPUS = "CPUs_online";
|
||||
const CLOCK_MIN_CPU = "CPUs_min_clock";
|
||||
const CLOCK_MAX_CPU = "CPUs_max_clock";
|
||||
const GOVERNOR_CPU = "CPUs_governor";
|
||||
|
||||
const [smtGlobal, setSMT_internal] = useState<boolean>(smt_backup);
|
||||
const setSMT = (value: boolean) => {
|
||||
smt_backup = value;
|
||||
setSMT_internal(value);
|
||||
};
|
||||
const FAST_PPT_GPU = "GPU_fastPPT";
|
||||
const SLOW_PPT_GPU = "GPU_slowPPT";
|
||||
const CLOCK_MIN_GPU = "GPU_min_clock";
|
||||
const CLOCK_MAX_GPU = "GPU_max_clock";
|
||||
const SLOW_MEMORY_GPU = "GPU_slow_memory";
|
||||
|
||||
const [cpusGlobal, setCPUs_internal] = useState<number>(cpus_backup);
|
||||
const setCPUs = (value: number) => {
|
||||
cpus_backup = value;
|
||||
setCPUs_internal(value);
|
||||
};
|
||||
const PERSISTENT_GEN = "GENERAL_persistent";
|
||||
const NAME_GEN = "GENERAL_name";
|
||||
|
||||
const [boostGlobal, setBoost_internal] = useState<boolean>(boost_backup);
|
||||
const setBoost = (value: boolean) => {
|
||||
boost_backup = value;
|
||||
setBoost_internal(value);
|
||||
};
|
||||
const reload = function() {
|
||||
if (!usdplReady) {return;}
|
||||
|
||||
const [freqGlobal, setFreq_internal] = useState<number>(freq_backup);
|
||||
const setFreq = (value: number) => {
|
||||
freq_backup = value;
|
||||
setFreq_internal(value);
|
||||
};
|
||||
backend.resolve(backend.getBatteryCurrent(), (rate: number) => { set_value(CURRENT_BATT, rate) });
|
||||
backend.resolve(backend.getBatteryChargeRate(), (rate: number) => { set_value(CHARGE_RATE_BATT, rate) });
|
||||
|
||||
const [slowPPTGlobal, setSlowPPT_internal] = useState<number>(slowPPT_backup);
|
||||
const setSlowPPT = (value: number) => {
|
||||
slowPPT_backup = value;
|
||||
setSlowPPT_internal(value);
|
||||
};
|
||||
|
||||
const [fastPPTGlobal, setFastPPT_internal] = useState<number>(fastPPT_backup);
|
||||
const setFastPPT = (value: number) => {
|
||||
fastPPT_backup = value;
|
||||
setFastPPT_internal(value);
|
||||
};
|
||||
|
||||
const [chargeNowGlobal, setChargeNow_internal] = useState<number>(chargeNow_backup);
|
||||
const setChargeNow = (value: number) => {
|
||||
chargeNow_backup = value;
|
||||
setChargeNow_internal(value);
|
||||
};
|
||||
|
||||
const [chargeFullGlobal, setChargeFull_internal] = useState<number>(chargeFull_backup);
|
||||
const setChargeFull = (value: number) => {
|
||||
chargeFull_backup = value;
|
||||
setChargeFull_internal(value);
|
||||
};
|
||||
|
||||
const [chargeDesignGlobal, setChargeDesign_internal] = useState<number>(chargeDesign_backup);
|
||||
const setChargeDesign = (value: number) => {
|
||||
chargeDesign_backup = value;
|
||||
setChargeDesign_internal(value);
|
||||
};
|
||||
|
||||
const [persistGlobal, setPersist_internal] = useState<boolean>(persistent_backup);
|
||||
const setPersist = (value: boolean) => {
|
||||
persistent_backup = value;
|
||||
setPersist_internal(value);
|
||||
};
|
||||
|
||||
const [perGameProfileGlobal, setPerGameProfile_internal] = useState<boolean>(perGameProfile_backup);
|
||||
const setPerGameProfile = (value: boolean) => {
|
||||
perGameProfile_backup = value;
|
||||
setPerGameProfile_internal(value);
|
||||
};
|
||||
|
||||
const [gameGlobal, setGame_internal] = useState<string>(lastGame);
|
||||
const setGame = (value: string) => {
|
||||
lastGame = value;
|
||||
setGame_internal(value);
|
||||
};
|
||||
|
||||
const [versionGlobal, setVersion_internal] = useState<string>(versionGlobalHolder);
|
||||
const setVersion = (value: string) => {
|
||||
versionGlobalHolder = value;
|
||||
setVersion_internal(value);
|
||||
};
|
||||
|
||||
reload = function () {
|
||||
python.execute(python.onViewReady());
|
||||
|
||||
python.resolve(python.getSMT(), setSMT);
|
||||
python.resolve(python.getCPUs(), setCPUs);
|
||||
python.resolve(python.getCPUBoost(), setBoost);
|
||||
python.resolve(python.getMaxBoost(), setFreq);
|
||||
|
||||
python.resolve(python.getGPUPowerI(1), setSlowPPT);
|
||||
python.resolve(python.getGPUPowerI(2), setFastPPT);
|
||||
|
||||
python.resolve(python.getPersistent(), setPersist);
|
||||
python.resolve(python.getPerGameProfile(), setPerGameProfile);
|
||||
};
|
||||
|
||||
|
||||
if (firstTime) {
|
||||
firstTime = false;
|
||||
|
||||
reload(); // technically it's just load, not reload ;)
|
||||
|
||||
python.resolve(python.getChargeNow(), setChargeNow);
|
||||
python.resolve(python.getChargeFull(), setChargeFull);
|
||||
python.resolve(python.getChargeDesign(), setChargeDesign);
|
||||
|
||||
python.resolve(python.getCurrentGame(), setGame);
|
||||
|
||||
python.resolve(python.getVersion(), setVersion);
|
||||
backend.resolve(backend.getCpuCount(), (count: number) => { set_value(TOTAL_CPUS, count)});
|
||||
backend.resolve(backend.getCpusOnline(), (statii: boolean[]) => {
|
||||
// TODO: allow for per-core control of online status
|
||||
let count = 0;
|
||||
for (let i = 0; i < statii.length; i++) {
|
||||
if (statii[i]) {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
set_value(ONLINE_CPUS, count);
|
||||
smtGlobal = statii.length > 3 && statii[0] == statii[1] && statii[2] == statii[3] && smtAllowed;
|
||||
});
|
||||
// TODO: allow for per-core control of clock limits
|
||||
backend.resolve(backend.getCpuClockLimits(0), (limits: number[]) => {
|
||||
set_value(CLOCK_MIN_CPU, limits[0]);
|
||||
set_value(CLOCK_MAX_CPU, limits[1]);
|
||||
});
|
||||
// TODO: allow for control of governor
|
||||
backend.resolve(backend.getCpusGovernor(), (governors: string[]) => { set_value(GOVERNOR_CPU, governors[0]) });
|
||||
|
||||
backend.resolve(backend.getGpuPpt(), (ppts: number[]) => {
|
||||
set_value(FAST_PPT_GPU, ppts[0]);
|
||||
set_value(SLOW_PPT_GPU, ppts[1]);
|
||||
});
|
||||
backend.resolve(backend.getGpuClockLimits(), (limits: number[]) => {
|
||||
set_value(CLOCK_MIN_GPU, limits[0]);
|
||||
set_value(CLOCK_MAX_GPU, limits[1]);
|
||||
});
|
||||
backend.resolve(backend.getGpuSlowMemory(), (status: boolean) => { set_value(SLOW_MEMORY_GPU, status) });
|
||||
|
||||
backend.resolve(backend.getGeneralPersistent(), (value: boolean) => { set_value(PERSISTENT_GEN, value) });
|
||||
backend.resolve(backend.getGeneralSettingsName(), (name: string) => { set_value(NAME_GEN, name) });
|
||||
|
||||
backend.resolve(backend.getInfo(), (info: string) => { set_value(BACKEND_INFO, info) });
|
||||
};
|
||||
|
||||
// init USDPL WASM and connection to back-end
|
||||
(async function(){
|
||||
await backend.initBackend();
|
||||
usdplReady = true;
|
||||
set_value(NAME_GEN, "Default");
|
||||
reload(); // technically this is only a load
|
||||
|
||||
// register Steam callbacks
|
||||
//@ts-ignore
|
||||
lifetimeHook = SteamClient.GameSessions.RegisterForAppLifetimeNotifications((update) => {
|
||||
if (update.bRunning) {
|
||||
//console.debug("AppID " + update.unAppID.toString() + " is now running");
|
||||
} else {
|
||||
//console.debug("AppID " + update.unAppID.toString() + " is no longer running");
|
||||
backend.resolve(
|
||||
backend.loadGeneralDefaultSettings(),
|
||||
(ok: boolean) => {console.debug("Loading default settings ok? " + ok)}
|
||||
);
|
||||
}
|
||||
});
|
||||
//@ts-ignore
|
||||
startHook = SteamClient.Apps.RegisterForGameActionStart((actionType, id) => {
|
||||
//@ts-ignore
|
||||
let gameInfo: any = appStore.GetAppOverviewByGameID(id);
|
||||
backend.resolve(
|
||||
backend.loadGeneralSettings(gameInfo.appid.toString() + ".json", gameInfo.display_name),
|
||||
(ok: boolean) => {console.debug("Loading settings ok? " + ok)}
|
||||
);
|
||||
});
|
||||
|
||||
console.debug("Registered PowerTools callbacks, hello!");
|
||||
})();
|
||||
|
||||
const periodicals = function() {
|
||||
backend.resolve(backend.getBatteryCurrent(), (rate: number) => { set_value(CURRENT_BATT, rate) });
|
||||
|
||||
backend.resolve(backend.getGeneralPersistent(), (value: boolean) => { set_value(PERSISTENT_GEN, value) });
|
||||
backend.resolve(backend.getGeneralSettingsName(), (name: string) => {
|
||||
const oldValue = get_value(NAME_GEN);
|
||||
set_value(NAME_GEN, name);
|
||||
if (name != oldValue) {
|
||||
reload();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
||||
|
||||
const [_idc, reloadGUI] = useState<any>("/shrug");
|
||||
|
||||
if (periodicHook != null) {
|
||||
clearInterval(periodicHook);
|
||||
|
@ -181,189 +151,387 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({serverAPI}) => {
|
|||
}
|
||||
|
||||
periodicHook = setInterval(function() {
|
||||
python.resolve(python.getChargeFull(), setChargeFull);
|
||||
python.resolve(python.getChargeNow(), setChargeNow);
|
||||
python.resolve(python.getCurrentGame(), (game: string) => {
|
||||
if (lastGame != game) {
|
||||
setGame(game);
|
||||
lastGame = game;
|
||||
reload();
|
||||
}
|
||||
});
|
||||
periodicals();
|
||||
reloadGUI("periodic" + (new Date()).getTime().toString());
|
||||
}, 1000);
|
||||
|
||||
const FieldWithSeparator = joinClassNames(gamepadDialogClasses.Field, gamepadDialogClasses.WithBottomSeparatorStandard);
|
||||
|
||||
const total_cpus = get_value(TOTAL_CPUS);
|
||||
|
||||
return (
|
||||
<PanelSection>
|
||||
{/* CPU */}
|
||||
{/* CPU */ /* TODO: set per-core stuff*/}
|
||||
<div className={staticClasses.PanelSectionTitle}>
|
||||
CPU
|
||||
</div>
|
||||
<PanelSectionRow>
|
||||
{smtAllowed && <PanelSectionRow>
|
||||
<ToggleField
|
||||
checked={smtGlobal}
|
||||
label="SMT"
|
||||
description="Enables odd-numbered CPUs"
|
||||
onChange={(smt: boolean) => {
|
||||
console.log("SMT is now " + smt.toString());
|
||||
python.execute(python.setCPUs(cpusGlobal, smt));
|
||||
python.resolve(python.getCPUs(), setCPUs);
|
||||
python.resolve(python.getSMT(), setSMT);
|
||||
console.debug("SMT is now " + smt.toString());
|
||||
const cpus = get_value(ONLINE_CPUS);
|
||||
set_value(ONLINE_CPUS, 0);
|
||||
smtGlobal = smt && smtAllowed;
|
||||
for (let i = 0; i < total_cpus; i++) {
|
||||
const online = (smtGlobal? i < cpus : (i % 2 == 0) && (i < cpus * 2))
|
||||
|| (!smtGlobal && cpus == 4);
|
||||
backend.resolve(backend.setCpuOnline(i, online), (value: boolean) => {
|
||||
if (value) {set_value(ONLINE_CPUS, get_value(ONLINE_CPUS) + 1)}
|
||||
})
|
||||
}
|
||||
backend.resolve(backend.waitForComplete(), (_: boolean[]) => {
|
||||
reloadGUI("SMT");
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</PanelSectionRow>
|
||||
</PanelSectionRow>}
|
||||
<PanelSectionRow>
|
||||
<SliderField
|
||||
label="Threads"
|
||||
value={cpusGlobal}
|
||||
value={get_value(ONLINE_CPUS)}
|
||||
step={1}
|
||||
max={smtGlobal? 8 : 4}
|
||||
max={smtGlobal? total_cpus : total_cpus/2}
|
||||
min={1}
|
||||
showValue={true}
|
||||
onChange={(cpus: number) => {
|
||||
console.log("CPU slider is now " + cpus.toString());
|
||||
if (cpus != cpusGlobal) {
|
||||
python.execute(python.setCPUs(cpus, smtGlobal));
|
||||
python.resolve(python.getCPUs(), setCPUs);
|
||||
console.debug("CPU slider is now " + cpus.toString());
|
||||
const onlines = get_value(ONLINE_CPUS);
|
||||
if (cpus != onlines) {
|
||||
set_value(ONLINE_CPUS, 0);
|
||||
for (let i = 0; i < total_cpus; i++) {
|
||||
const online = smtGlobal? i < cpus : (i % 2 == 0) && (i < cpus * 2);
|
||||
backend.resolve(backend.setCpuOnline(i, online), (value: boolean) => {
|
||||
if (value) {set_value(ONLINE_CPUS, get_value(ONLINE_CPUS) + 1)}
|
||||
})
|
||||
}
|
||||
backend.resolve(backend.waitForComplete(), (_: boolean[]) => {
|
||||
reloadGUI("CPUs");
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</PanelSectionRow>
|
||||
<PanelSectionRow>
|
||||
<ToggleField
|
||||
checked={boostGlobal}
|
||||
label="Boost"
|
||||
description="Allows the CPU to go above max frequency"
|
||||
onChange={(boost: boolean) => {
|
||||
console.log("Boost is now " + boost.toString());
|
||||
python.execute(python.setCPUBoost(boost));
|
||||
python.resolve(python.getCPUBoost(), setBoost);
|
||||
}}
|
||||
/>
|
||||
</PanelSectionRow>
|
||||
<PanelSectionRow>
|
||||
<SliderField
|
||||
label="Max Frequency"
|
||||
value={freqGlobal}
|
||||
max={2}
|
||||
min={0}
|
||||
notchCount={3}
|
||||
notchLabels={[
|
||||
{notchIndex: 0, label: "1.7GHz"},
|
||||
{notchIndex: 1, label: "2.4GHz"},
|
||||
{notchIndex: 2, label: "2.8GHz"},
|
||||
]}
|
||||
notchTicksVisible={true}
|
||||
onChange={(freq: number) => {
|
||||
console.log("CPU slider is now " + freq.toString());
|
||||
if (freq != freqGlobal) {
|
||||
python.execute(python.setMaxBoost(freq));
|
||||
python.resolve(python.getMaxBoost(), setFreq);
|
||||
checked={get_value(CLOCK_MIN_CPU) != null && get_value(CLOCK_MAX_CPU) != null}
|
||||
label="Frequency Limits"
|
||||
onChange={(value: boolean) => {
|
||||
if (value) {
|
||||
set_value(CLOCK_MIN_CPU, 1400);
|
||||
set_value(CLOCK_MAX_CPU, 3500);
|
||||
reloadGUI("CPUFreqToggle");
|
||||
} else {
|
||||
set_value(CLOCK_MIN_CPU, null);
|
||||
set_value(CLOCK_MAX_CPU, null);
|
||||
for (let i = 0; i < total_cpus; i++) {
|
||||
backend.resolve(backend.unsetCpuClockLimits(i), (_idc: any[]) => {});
|
||||
}
|
||||
backend.resolve(backend.waitForComplete(), (_: boolean[]) => {
|
||||
reloadGUI("CPUUnsetFreq");
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</PanelSectionRow>
|
||||
<PanelSectionRow>
|
||||
{get_value(CLOCK_MIN_CPU) != null && <SliderField
|
||||
label="Minimum (MHz)"
|
||||
value={get_value(CLOCK_MIN_CPU)}
|
||||
max={3500}
|
||||
min={1400}
|
||||
step={100}
|
||||
showValue={true}
|
||||
disabled={get_value(CLOCK_MIN_CPU) == null}
|
||||
onChange={(freq: number) => {
|
||||
console.debug("Min freq slider is now " + freq.toString());
|
||||
const freqNow = get_value(CLOCK_MIN_CPU);
|
||||
if (freq != freqNow) {
|
||||
for (let i = 0; i < total_cpus; i++) {
|
||||
backend.resolve(backend.setCpuClockLimits(i, freq, get_value(CLOCK_MAX_CPU)),
|
||||
(limits: number[]) => {
|
||||
set_value(CLOCK_MIN_CPU, limits[0]);
|
||||
set_value(CLOCK_MAX_CPU, limits[1]);
|
||||
});
|
||||
}
|
||||
backend.resolve(backend.waitForComplete(), (_: boolean[]) => {
|
||||
reloadGUI("CPUMinFreq");
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>}
|
||||
</PanelSectionRow>
|
||||
<PanelSectionRow>
|
||||
{get_value(CLOCK_MAX_CPU) != null && <SliderField
|
||||
label="Maximum (MHz)"
|
||||
value={get_value(CLOCK_MAX_CPU)}
|
||||
max={3500}
|
||||
min={500}
|
||||
step={100}
|
||||
showValue={true}
|
||||
disabled={get_value(CLOCK_MAX_CPU) == null}
|
||||
onChange={(freq: number) => {
|
||||
console.debug("Max freq slider is now " + freq.toString());
|
||||
const freqNow = get_value(CLOCK_MAX_CPU);
|
||||
if (freq != freqNow) {
|
||||
for (let i = 0; i < total_cpus; i++) {
|
||||
backend.resolve(backend.setCpuClockLimits(i, get_value(CLOCK_MIN_CPU), freq),
|
||||
(limits: number[]) => {
|
||||
set_value(CLOCK_MIN_CPU, limits[0]);
|
||||
set_value(CLOCK_MAX_CPU, limits[1]);
|
||||
});
|
||||
}
|
||||
backend.resolve(backend.waitForComplete(), (_: boolean[]) => {
|
||||
reloadGUI("CPUMaxFreq");
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>}
|
||||
</PanelSectionRow>
|
||||
{/* TODO: CPU governor */}
|
||||
{/* GPU */}
|
||||
<div className={staticClasses.PanelSectionTitle}>
|
||||
GPU
|
||||
</div>
|
||||
<PanelSectionRow>
|
||||
{/* index: 1 */}
|
||||
<SliderField
|
||||
label="SlowPPT Power"
|
||||
value={slowPPTGlobal}
|
||||
max={2}
|
||||
min={0}
|
||||
notchCount={3}
|
||||
notchLabels={[
|
||||
{notchIndex: 0, label: "Min"},
|
||||
{notchIndex: 1, label: "Auto"},
|
||||
{notchIndex: 2, label: "Max"},
|
||||
]}
|
||||
notchTicksVisible={true}
|
||||
onChange={(ppt: number) => {
|
||||
console.log("SlowPPT is now " + ppt.toString());
|
||||
if (ppt != slowPPTGlobal) {
|
||||
python.execute(python.setGPUPowerI(ppt, 1));
|
||||
python.resolve(python.getGPUPowerI(1), setSlowPPT);
|
||||
<ToggleField
|
||||
checked={get_value(SLOW_PPT_GPU) != null && get_value(FAST_PPT_GPU) != null}
|
||||
label="PowerPlay Limits"
|
||||
description="Override APU TDP settings"
|
||||
onChange={(value: boolean) => {
|
||||
if (value) {
|
||||
set_value(SLOW_PPT_GPU, 15000000);
|
||||
set_value(FAST_PPT_GPU, 15000000);
|
||||
reloadGUI("GPUPPTToggle");
|
||||
} else {
|
||||
set_value(SLOW_PPT_GPU, null);
|
||||
set_value(FAST_PPT_GPU, null);
|
||||
backend.resolve(backend.unsetGpuPpt(), (_: any[]) => {
|
||||
reloadGUI("GPUUnsetPPT");
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</PanelSectionRow>
|
||||
<PanelSectionRow>
|
||||
{/* index: 2 */}
|
||||
<SliderField
|
||||
label="FastPPT Power"
|
||||
value={fastPPTGlobal}
|
||||
max={2}
|
||||
min={0}
|
||||
notchCount={3}
|
||||
notchLabels={[
|
||||
{notchIndex: 0, label: "Min"},
|
||||
{notchIndex: 1, label: "Auto"},
|
||||
{notchIndex: 2, label: "Max"},
|
||||
]}
|
||||
notchTicksVisible={true}
|
||||
{ get_value(SLOW_PPT_GPU) != null && <SliderField
|
||||
label="SlowPPT (uW)"
|
||||
value={get_value(SLOW_PPT_GPU)}
|
||||
max={29000000}
|
||||
min={1000000}
|
||||
step={1000000}
|
||||
showValue={true}
|
||||
disabled={get_value(SLOW_PPT_GPU) == null}
|
||||
onChange={(ppt: number) => {
|
||||
console.log("FastPPT is now " + ppt.toString());
|
||||
if (ppt != fastPPTGlobal) {
|
||||
python.execute(python.setGPUPowerI(ppt, 2));
|
||||
python.resolve(python.getGPUPowerI(2), setFastPPT);
|
||||
console.debug("SlowPPT is now " + ppt.toString());
|
||||
const pptNow = get_value(SLOW_PPT_GPU);
|
||||
if (ppt != pptNow) {
|
||||
backend.resolve(backend.setGpuPpt(get_value(FAST_PPT_GPU), ppt),
|
||||
(limits: number[]) => {
|
||||
set_value(FAST_PPT_GPU, limits[0]);
|
||||
set_value(SLOW_PPT_GPU, limits[1]);
|
||||
reloadGUI("GPUSlowPPT");
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>}
|
||||
</PanelSectionRow>
|
||||
<PanelSectionRow>
|
||||
{get_value(FAST_PPT_GPU) != null && <SliderField
|
||||
label="FastPPT (uW)"
|
||||
value={get_value(FAST_PPT_GPU)}
|
||||
max={29000000}
|
||||
min={1000000}
|
||||
step={1000000}
|
||||
showValue={true}
|
||||
disabled={get_value(FAST_PPT_GPU) == null}
|
||||
onChange={(ppt: number) => {
|
||||
console.debug("FastPPT is now " + ppt.toString());
|
||||
const pptNow = get_value(FAST_PPT_GPU);
|
||||
if (ppt != pptNow) {
|
||||
backend.resolve(backend.setGpuPpt(get_value(SLOW_PPT_GPU), ppt),
|
||||
(limits: number[]) => {
|
||||
set_value(FAST_PPT_GPU, limits[0]);
|
||||
set_value(SLOW_PPT_GPU, limits[1]);
|
||||
reloadGUI("GPUFastPPT");
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>}
|
||||
</PanelSectionRow>
|
||||
<PanelSectionRow>
|
||||
<ToggleField
|
||||
checked={get_value(CLOCK_MIN_GPU) != null && get_value(CLOCK_MAX_GPU) != null}
|
||||
label="Frequency Limits"
|
||||
onChange={(value: boolean) => {
|
||||
if (value) {
|
||||
set_value(CLOCK_MIN_GPU, 200);
|
||||
set_value(CLOCK_MAX_GPU, 1600);
|
||||
reloadGUI("GPUFreqToggle");
|
||||
} else {
|
||||
set_value(CLOCK_MIN_GPU, null);
|
||||
set_value(CLOCK_MIN_GPU, null);
|
||||
backend.resolve(backend.unsetGpuClockLimits(), (_: any[]) => {
|
||||
reloadGUI("GPUUnsetFreq");
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</PanelSectionRow>
|
||||
<PanelSectionRow>
|
||||
{ get_value(CLOCK_MIN_GPU) != null && <SliderField
|
||||
label="Minimum (MHz)"
|
||||
value={get_value(CLOCK_MIN_GPU)}
|
||||
max={1600}
|
||||
min={200}
|
||||
step={100}
|
||||
showValue={true}
|
||||
disabled={get_value(CLOCK_MIN_GPU) == null}
|
||||
onChange={(val: number) => {
|
||||
console.debug("GPU Clock Min is now " + val.toString());
|
||||
const valNow = get_value(CLOCK_MIN_GPU);
|
||||
if (val != valNow) {
|
||||
backend.resolve(backend.setGpuClockLimits(val, get_value(CLOCK_MAX_GPU)),
|
||||
(limits: number[]) => {
|
||||
set_value(CLOCK_MIN_GPU, limits[0]);
|
||||
set_value(CLOCK_MAX_GPU, limits[1]);
|
||||
reloadGUI("GPUMinClock");
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>}
|
||||
</PanelSectionRow>
|
||||
<PanelSectionRow>
|
||||
{get_value(CLOCK_MAX_GPU) != null && <SliderField
|
||||
label="Maximum (MHz)"
|
||||
value={get_value(CLOCK_MAX_GPU)}
|
||||
max={1600}
|
||||
min={200}
|
||||
step={100}
|
||||
showValue={true}
|
||||
disabled={get_value(CLOCK_MAX_GPU) == null}
|
||||
onChange={(val: number) => {
|
||||
console.debug("GPU Clock Max is now " + val.toString());
|
||||
const valNow = get_value(CLOCK_MAX_GPU);
|
||||
if (val != valNow) {
|
||||
backend.resolve(backend.setGpuClockLimits(get_value(CLOCK_MIN_GPU), val),
|
||||
(limits: number[]) => {
|
||||
set_value(CLOCK_MIN_GPU, limits[0]);
|
||||
set_value(CLOCK_MAX_GPU, limits[1]);
|
||||
reloadGUI("GPUMaxClock");
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>}
|
||||
</PanelSectionRow>
|
||||
<PanelSectionRow>
|
||||
<ToggleField
|
||||
checked={get_value(SLOW_MEMORY_GPU)}
|
||||
label="Downclock Memory"
|
||||
description="Force RAM into low-power mode"
|
||||
onChange={(value: boolean) => {
|
||||
backend.resolve(backend.setGpuSlowMemory(value), (val: boolean) => {
|
||||
set_value(SLOW_MEMORY_GPU, val);
|
||||
reloadGUI("GPUSlowMemory");
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</PanelSectionRow>
|
||||
{/* Battery */}
|
||||
<div className={staticClasses.PanelSectionTitle}>
|
||||
Battery
|
||||
</div>
|
||||
<PanelSectionRow>
|
||||
{ false && <PanelSectionRow>
|
||||
<div className={FieldWithSeparator}>
|
||||
<div className={gamepadDialogClasses.FieldLabelRow}>
|
||||
<div className={gamepadDialogClasses.FieldLabel}>
|
||||
Now (Charge)
|
||||
</div>
|
||||
<div className={gamepadDialogClasses.FieldChildren}>
|
||||
{(7.7 * chargeNowGlobal / 1000000).toFixed(1).toString() + " Wh (" + (100 * chargeNowGlobal / chargeFullGlobal).toFixed(1).toString() + "%)"}
|
||||
{/* TODO: (7.7 * chargeNowGlobal / 1000000).toFixed(1).toString() + " Wh (" + (100 * chargeNowGlobal / chargeFullGlobal).toFixed(1).toString() + "%)"*/}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PanelSectionRow>
|
||||
<PanelSectionRow>
|
||||
</PanelSectionRow>}
|
||||
{ false && <PanelSectionRow>
|
||||
<div className={FieldWithSeparator}>
|
||||
<div className={gamepadDialogClasses.FieldLabelRow}>
|
||||
<div className={gamepadDialogClasses.FieldLabel}>
|
||||
Max (Design)
|
||||
</div>
|
||||
<div className={gamepadDialogClasses.FieldChildren}>
|
||||
{(7.7 * chargeFullGlobal / 1000000).toFixed(1).toString() + " Wh (" + (100 * chargeFullGlobal / chargeDesignGlobal).toFixed(1).toString() + "%)"}
|
||||
{/* TODO: (7.7 * chargeFullGlobal / 1000000).toFixed(1).toString() + " Wh (" + (100 * chargeFullGlobal / chargeDesignGlobal).toFixed(1).toString() + "%)"*/}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PanelSectionRow>}
|
||||
<PanelSectionRow>
|
||||
<ToggleField
|
||||
checked={get_value(CHARGE_RATE_BATT) != null}
|
||||
label="Charge Current Limits"
|
||||
description="Control battery charge rate"
|
||||
onChange={(value: boolean) => {
|
||||
if (value) {
|
||||
set_value(CHARGE_RATE_BATT, 2500);
|
||||
reloadGUI("BATTChargeRateToggle");
|
||||
} else {
|
||||
set_value(CHARGE_RATE_BATT, null);
|
||||
backend.resolve(backend.unsetBatteryChargeRate(), (_: any[]) => {
|
||||
reloadGUI("BATTUnsetChargeRate");
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{ get_value(CHARGE_RATE_BATT) != null && <SliderField
|
||||
label="Maximum (mA)"
|
||||
value={get_value(CHARGE_RATE_BATT)}
|
||||
max={2500}
|
||||
min={250}
|
||||
step={50}
|
||||
showValue={true}
|
||||
disabled={get_value(CHARGE_RATE_BATT) == null}
|
||||
onChange={(val: number) => {
|
||||
console.debug("Charge rate is now " + val.toString());
|
||||
const rateNow = get_value(CHARGE_RATE_BATT);
|
||||
if (val != rateNow) {
|
||||
backend.resolve(backend.setBatteryChargeRate(val),
|
||||
(rate: number) => {
|
||||
set_value(CHARGE_RATE_BATT, rate);
|
||||
reloadGUI("BATTChargeRate");
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>}
|
||||
</PanelSectionRow>
|
||||
<PanelSectionRow>
|
||||
<div className={FieldWithSeparator}>
|
||||
<div className={gamepadDialogClasses.FieldLabelRow}>
|
||||
<div className={gamepadDialogClasses.FieldLabel}>
|
||||
Current
|
||||
</div>
|
||||
<div className={gamepadDialogClasses.FieldChildren}>
|
||||
{get_value(CURRENT_BATT)} mA
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PanelSectionRow>
|
||||
{/* Persistence */}
|
||||
<div className={staticClasses.PanelSectionTitle}>
|
||||
Miscellaneous
|
||||
</div>
|
||||
<PanelSectionRow>
|
||||
<ToggleField
|
||||
checked={persistGlobal}
|
||||
checked={get_value(PERSISTENT_GEN)}
|
||||
label="Persistent"
|
||||
description="Restores settings after an app or OS restart"
|
||||
onChange={(persist: boolean) => {
|
||||
console.log("Persist is now " + persist.toString());
|
||||
python.execute(python.setPersistent(persist));
|
||||
python.resolve(python.getPersistent(), setPersist);
|
||||
}}
|
||||
/>
|
||||
</PanelSectionRow>
|
||||
<PanelSectionRow>
|
||||
<ToggleField
|
||||
checked={perGameProfileGlobal}
|
||||
label="Use per-game profile"
|
||||
onChange={(p: boolean) => {
|
||||
console.log("Per game profile is now " + p.toString());
|
||||
python.execute(python.setPerGameProfile(p));
|
||||
python.resolve(python.getPerGameProfile(), setPerGameProfile);
|
||||
reload();
|
||||
console.debug("Persist is now " + persist.toString());
|
||||
backend.resolve(
|
||||
backend.setGeneralPersistent(persist),
|
||||
(val: boolean) => {set_value(PERSISTENT_GEN, val)}
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</PanelSectionRow>
|
||||
|
@ -374,12 +542,12 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({serverAPI}) => {
|
|||
Now Playing
|
||||
</div>
|
||||
<div className={gamepadDialogClasses.FieldChildren}>
|
||||
{gameGlobal}
|
||||
{get_value(NAME_GEN)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PanelSectionRow>
|
||||
{/* Version */}
|
||||
{/* Version Info */}
|
||||
<div className={staticClasses.PanelSectionTitle}>
|
||||
Debug
|
||||
</div>
|
||||
|
@ -387,68 +555,75 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({serverAPI}) => {
|
|||
<div className={FieldWithSeparator}>
|
||||
<div className={gamepadDialogClasses.FieldLabelRow}>
|
||||
<div className={gamepadDialogClasses.FieldLabel}>
|
||||
PowerTools
|
||||
Native
|
||||
</div>
|
||||
<div className={gamepadDialogClasses.FieldChildren}>
|
||||
v{versionGlobal}
|
||||
{get_value(BACKEND_INFO)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PanelSectionRow>
|
||||
<PanelSectionRow>
|
||||
<div className={FieldWithSeparator}>
|
||||
<div className={gamepadDialogClasses.FieldLabelRow}>
|
||||
<div className={gamepadDialogClasses.FieldLabel}>
|
||||
Framework
|
||||
</div>
|
||||
<div className={gamepadDialogClasses.FieldChildren}>
|
||||
{target_usdpl()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PanelSectionRow>
|
||||
<PanelSectionRow>
|
||||
<div className={FieldWithSeparator}>
|
||||
<div className={gamepadDialogClasses.FieldLabelRow}>
|
||||
<div className={gamepadDialogClasses.FieldLabel}>
|
||||
USDPL
|
||||
</div>
|
||||
<div className={gamepadDialogClasses.FieldChildren}>
|
||||
v{version_usdpl()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PanelSectionRow>
|
||||
<PanelSectionRow>
|
||||
<ButtonItem
|
||||
layout="below"
|
||||
onClick={(_: MouseEvent) => {
|
||||
console.debug("Loading default PowerTools settings");
|
||||
backend.resolve(
|
||||
backend.setGeneralPersistent(false),
|
||||
(val: boolean) => {
|
||||
set_value(PERSISTENT_GEN, val);
|
||||
backend.resolve(backend.loadGeneralDefaultSettings(), (_: any[]) => {
|
||||
reload();
|
||||
backend.resolve(backend.waitForComplete(), (_: any[]) => {reloadGUI("LoadDefaults")});
|
||||
});
|
||||
}
|
||||
);
|
||||
}}
|
||||
>
|
||||
Defaults
|
||||
</ButtonItem>
|
||||
</PanelSectionRow>
|
||||
</PanelSection>
|
||||
);
|
||||
};
|
||||
|
||||
const DeckyPluginRouterTest: VFC = () => {
|
||||
return (
|
||||
<div style={{ marginTop: "50px", color: "white" }}>
|
||||
Hello World!
|
||||
<DialogButton onClick={() => {}}>
|
||||
Go to Store
|
||||
</DialogButton>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default definePlugin((serverApi: ServerAPI) => {
|
||||
serverApi.routerHook.addRoute("/decky-plugin-test", DeckyPluginRouterTest, {
|
||||
exact: true,
|
||||
});
|
||||
|
||||
python.setServer(serverApi);
|
||||
|
||||
//@ts-ignore
|
||||
lifetimeHook = SteamClient.GameSessions.RegisterForAppLifetimeNotifications((update) => {
|
||||
if (update.bRunning) {
|
||||
console.log("AppID " + update.unAppID.toString() + " is now running");
|
||||
} else {
|
||||
console.log("AppID " + update.unAppID.toString() + " is no longer running");
|
||||
python.execute(python.onGameStop(null));
|
||||
}
|
||||
});
|
||||
//@ts-ignore
|
||||
startHook = SteamClient.Apps.RegisterForGameActionStart((actionType, id) => {
|
||||
//@ts-ignore
|
||||
let gameInfo: any = appStore.GetAppOverviewByGameID(id);
|
||||
python.execute(python.onGameStart(id, gameInfo));
|
||||
});
|
||||
|
||||
console.log("Registered PowerTools callbacks, hello!");
|
||||
|
||||
return {
|
||||
title: <div className={staticClasses.Title}>PowerTools</div>,
|
||||
content: <Content serverAPI={serverApi} />,
|
||||
icon: <GiDrill />,
|
||||
onDismount() {
|
||||
console.log("PowerTools shutting down");
|
||||
console.debug("PowerTools shutting down");
|
||||
clearInterval(periodicHook!);
|
||||
periodicHook = null;
|
||||
lifetimeHook!.unregister();
|
||||
startHook!.unregister();
|
||||
serverApi.routerHook.removeRoute("/decky-plugin-test");
|
||||
firstTime = true;
|
||||
lastGame = "";
|
||||
console.log("Unregistered PowerTools callbacks, goodbye.");
|
||||
console.debug("Unregistered PowerTools callbacks, goodbye.");
|
||||
},
|
||||
};
|
||||
});
|
||||
|
|
9
src/usdpl_front/README.md
Normal file
9
src/usdpl_front/README.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
[![Crates.io](https://img.shields.io/crates/v/usdpl-front?style=flat-square)](https://crates.io/crates/usdpl-front)
|
||||
|
||||
# usdpl-front-front
|
||||
|
||||
Front-end library to be called from Javascript.
|
||||
Targets WASM.
|
||||
|
||||
In true Javascript tradition, this part of the library does not support error handling.
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
"NGnius (Graham) <ngniusness@gmail.com>"
|
||||
],
|
||||
"description": "Universal Steam Deck Plugin Library front-end designed for WASM",
|
||||
"version": "0.6.0",
|
||||
"version": "0.6.2",
|
||||
"license": "GPL-3.0-only",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
25
src/usdpl_front/usdpl_front.d.ts
vendored
25
src/usdpl_front/usdpl_front.d.ts
vendored
|
@ -9,7 +9,25 @@ export function init_usdpl(port: number): void;
|
|||
* Get the targeted plugin framework, or "any" if unknown
|
||||
* @returns {string}
|
||||
*/
|
||||
export function target(): string;
|
||||
export function target_usdpl(): string;
|
||||
/**
|
||||
* Get the UDSPL front-end version
|
||||
* @returns {string}
|
||||
*/
|
||||
export function version_usdpl(): string;
|
||||
/**
|
||||
* Get the targeted plugin framework, or "any" if unknown
|
||||
* @param {string} key
|
||||
* @param {any} value
|
||||
* @returns {any}
|
||||
*/
|
||||
export function set_value(key: string, value: any): any;
|
||||
/**
|
||||
* Get the targeted plugin framework, or "any" if unknown
|
||||
* @param {string} key
|
||||
* @returns {any}
|
||||
*/
|
||||
export function get_value(key: string): any;
|
||||
/**
|
||||
* Call a function on the back-end.
|
||||
* Returns null (None) if this fails for any reason.
|
||||
|
@ -24,7 +42,10 @@ export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembl
|
|||
export interface InitOutput {
|
||||
readonly memory: WebAssembly.Memory;
|
||||
readonly init_usdpl: (a: number) => void;
|
||||
readonly target: (a: number) => void;
|
||||
readonly target_usdpl: (a: number) => void;
|
||||
readonly version_usdpl: (a: number) => void;
|
||||
readonly set_value: (a: number, b: number, c: number) => number;
|
||||
readonly get_value: (a: number, b: number) => number;
|
||||
readonly call_backend: (a: number, b: number, c: number, d: number) => number;
|
||||
readonly __wbindgen_export_0: (a: number) => number;
|
||||
readonly __wbindgen_export_1: (a: number, b: number, c: number) => number;
|
||||
|
|
File diff suppressed because one or more lines are too long
Binary file not shown.
5
src/usdpl_front/usdpl_front_bg.wasm.d.ts
vendored
5
src/usdpl_front/usdpl_front_bg.wasm.d.ts
vendored
|
@ -2,7 +2,10 @@
|
|||
/* eslint-disable */
|
||||
export const memory: WebAssembly.Memory;
|
||||
export function init_usdpl(a: number): void;
|
||||
export function target(a: number): void;
|
||||
export function target_usdpl(a: number): void;
|
||||
export function version_usdpl(a: number): void;
|
||||
export function set_value(a: number, b: number, c: number): number;
|
||||
export function get_value(a: number, b: number): number;
|
||||
export function call_backend(a: number, b: number, c: number, d: number): number;
|
||||
export function __wbindgen_export_0(a: number): number;
|
||||
export function __wbindgen_export_1(a: number, b: number, c: number): number;
|
||||
|
|
Loading…
Reference in a new issue