Compare commits
18 commits
Author | SHA1 | Date | |
---|---|---|---|
924f1aaa51 | |||
364d9650f2 | |||
dd0c3998f0 | |||
a0f565895b | |||
32d09219da | |||
b637f4f5f9 | |||
8c9a7b7c68 | |||
f620dc1854 | |||
6c8b335417 | |||
dd6672f9ba | |||
f4a3f06aae | |||
255958f96b | |||
2bec0087a0 | |||
9972ac7854 | |||
438d91f639 | |||
1c6a889006 | |||
3d744f31aa | |||
c88402e580 |
43 changed files with 3668 additions and 2272 deletions
2
.github/FUNDING.yml
vendored
Normal file
2
.github/FUNDING.yml
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
github: NGnius
|
||||
liberapay: NGnius
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -44,6 +44,7 @@ yalc.lock
|
|||
/backend-rs/target
|
||||
backend
|
||||
/bin/backend
|
||||
/src/rust/target
|
||||
|
||||
# packaged teasers
|
||||
*.zip
|
||||
|
|
1595
backend-rs/Cargo.lock
generated
1595
backend-rs/Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -1,17 +1,34 @@
|
|||
[package]
|
||||
name = "fantastic-rs"
|
||||
version = "0.4.0"
|
||||
version = "0.6.0-alpha1"
|
||||
edition = "2021"
|
||||
authors = ["NGnius (Graham) <ngniusness@gmail.com>"]
|
||||
description = "Backend (superuser) functionality for Fantastic"
|
||||
license = "GPL-3.0-only"
|
||||
repository = "https://git.ngni.us/NG-SD-Plugins/Fantastic"
|
||||
keywords = ["utility", "fan-control", "root", "decky"]
|
||||
readme = "../README.md"
|
||||
|
||||
|
||||
[dependencies]
|
||||
usdpl-back = { version = "0.10", features = ["blocking"]}
|
||||
usdpl-back = { version = "1.0", features = ["blocking"], path = "../../usdpl-rs/usdpl-back"}
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
||||
nrpc = { version = "1.0", path = "../../nRPC/nrpc", features = ["async-trait"] }
|
||||
prost = "0.11"
|
||||
tokio = { version = "1", features = ["sync", "rt"] }
|
||||
|
||||
sysfuss = { version = "0.4", features = ["derive"], path = "../../sysfs-nav" }
|
||||
sysinfo = "0.31"
|
||||
|
||||
# logging
|
||||
log = "0.4"
|
||||
simplelog = "0.12"
|
||||
|
||||
[build-dependencies]
|
||||
usdpl-build = { version = "1.0", path = "../../usdpl-rs/usdpl-build" }
|
||||
|
||||
[profile.release]
|
||||
debug = false
|
||||
strip = true
|
||||
|
|
9
backend-rs/build.rs
Normal file
9
backend-rs/build.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
fn main() {
|
||||
//println!("CWD: {}", std::env::current_dir().unwrap().display());
|
||||
usdpl_build::back::build([
|
||||
format!("{}/protos/fantastic.proto", std::env::current_dir().unwrap().display())
|
||||
].into_iter(),
|
||||
[
|
||||
format!("{}/protos/", std::env::current_dir().unwrap().display())
|
||||
].into_iter())
|
||||
}
|
|
@ -1,11 +1,13 @@
|
|||
#!/bin/bash
|
||||
|
||||
#cargo build --release --target x86_64-unknown-linux-musl
|
||||
cargo build --target x86_64-unknown-linux-musl
|
||||
#cargo build --target x86_64-unknown-linux-musl
|
||||
#cross build --release
|
||||
cargo build
|
||||
|
||||
mkdir -p ../bin
|
||||
#cp ./target/x86_64-unknown-linux-musl/release/fantastic-rs ../bin/backend
|
||||
cp ./target/x86_64-unknown-linux-musl/debug/fantastic-rs ../bin/backend
|
||||
#cp ./target/release/fantastic-rs ../bin/backend
|
||||
#cp --preserve=mode ./target/x86_64-unknown-linux-musl/release/fantastic-rs ../bin/backend
|
||||
#cp --preserve=mode ./target/x86_64-unknown-linux-musl/debug/fantastic-rs ../bin/backend
|
||||
#cp --preserve=mode ./target/release/fantastic-rs ../bin/backend
|
||||
cp --preserve=mode ./target/debug/fantastic-rs ../bin/backend
|
||||
|
||||
|
|
136
backend-rs/protos/fantastic.proto
Normal file
136
backend-rs/protos/fantastic.proto
Normal file
|
@ -0,0 +1,136 @@
|
|||
syntax = "proto3";
|
||||
|
||||
package fantastic;
|
||||
|
||||
// The most amazing fan service
|
||||
service Fan {
|
||||
// Send back the exact same message as received
|
||||
rpc echo (EchoMessage) returns (EchoMessage);
|
||||
|
||||
// Hello world
|
||||
rpc hello (NameMessage) returns (HelloResponse);
|
||||
|
||||
// Fantastic version info
|
||||
rpc version (Empty) returns (VersionMessage);
|
||||
|
||||
// Fantastic version number string
|
||||
rpc version_str (Empty) returns (VersionDisplayMessage);
|
||||
|
||||
// Rust name (fantastic)
|
||||
rpc name (Empty) returns (NameMessage);
|
||||
|
||||
// Get fan speed
|
||||
rpc get_fan_rpm (Empty) returns (stream RpmMessage);
|
||||
|
||||
// Get system temperature
|
||||
rpc get_temperature (Empty) returns (stream TemperatureMessage);
|
||||
|
||||
// Set custom fan control enabled
|
||||
rpc set_enable (EnablementMessage) returns (EnablementMessage);
|
||||
|
||||
// Get custon fan control status
|
||||
rpc get_enable (Empty) returns (EnablementMessage);
|
||||
|
||||
// Set fan control interpolation
|
||||
rpc set_interpolate (EnablementMessage) returns (EnablementMessage);
|
||||
|
||||
// Get fan control interpolation
|
||||
rpc get_interpolate (Empty) returns (EnablementMessage);
|
||||
|
||||
// Get fan control curve
|
||||
rpc get_curve_x (Empty) returns (CurveMessageX);
|
||||
|
||||
// Get fan control curve
|
||||
rpc get_curve_y (Empty) returns (CurveMessageY);
|
||||
|
||||
// Add a new point to the fan curve
|
||||
rpc add_curve_point (GraphPoint) returns (Empty);
|
||||
|
||||
// Remove a point from the fan curve
|
||||
rpc remove_curve_point (IndexMessage) returns (Empty);
|
||||
|
||||
/*
|
||||
.register("echo", api::echo)
|
||||
.register("hello", api::hello)
|
||||
.register("version", api::version)
|
||||
.register("name", api::name)
|
||||
.register("get_fan_rpm", api::get_fan_rpm)
|
||||
.register("get_temperature", api::get_temperature)
|
||||
.register("set_enable", api::set_enable_gen(&runtime))
|
||||
.register("get_enable", api::get_enable_gen(&runtime))
|
||||
.register("set_interpolate", api::set_interpolate_gen(&runtime))
|
||||
.register("get_interpolate", api::get_interpolate_gen(&runtime))
|
||||
.register("get_curve", api::get_curve_gen(&runtime))
|
||||
.register("add_curve_point", api::add_curve_point_gen(&runtime))
|
||||
.register("remove_curve_point", api::remove_curve_point_gen(&runtime))
|
||||
*/
|
||||
}
|
||||
|
||||
// The request and response message for Echo
|
||||
message EchoMessage {
|
||||
string msg = 1;
|
||||
}
|
||||
|
||||
message NameMessage {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
message HelloResponse {
|
||||
string phrase = 1;
|
||||
}
|
||||
|
||||
message Empty {
|
||||
bool ok = 1;
|
||||
}
|
||||
|
||||
message VersionMessage {
|
||||
int32 major = 1;
|
||||
int32 minor = 2;
|
||||
int32 patch = 3;
|
||||
//string display = 4;
|
||||
}
|
||||
|
||||
message VersionDisplayMessage {
|
||||
string display = 1;
|
||||
}
|
||||
|
||||
message VersionStr {
|
||||
string version_str = 1;
|
||||
}
|
||||
|
||||
message RpmMessage {
|
||||
uint32 rpm = 1;
|
||||
}
|
||||
|
||||
message TemperatureMessage {
|
||||
double temperature = 1;
|
||||
}
|
||||
|
||||
message EnablementMessage {
|
||||
bool is_enabled = 1;
|
||||
}
|
||||
|
||||
message GraphPoint {
|
||||
double x = 1;
|
||||
double y = 2;
|
||||
}
|
||||
|
||||
/*message CurveMessage {
|
||||
//repeated GraphPoint points = 1;
|
||||
repeated double x = 1;
|
||||
repeated double y = 2;
|
||||
}*/
|
||||
|
||||
message CurveMessageX {
|
||||
//repeated GraphPoint points = 1;
|
||||
repeated double x = 1;
|
||||
}
|
||||
|
||||
message CurveMessageY {
|
||||
//repeated GraphPoint points = 1;
|
||||
repeated double y = 2;
|
||||
}
|
||||
|
||||
message IndexMessage {
|
||||
uint32 index = 1;
|
||||
}
|
11
backend-rs/src/adapters/fans/dev_mode.rs
Normal file
11
backend-rs/src/adapters/fans/dev_mode.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
pub struct DevModeFan;
|
||||
|
||||
impl super::Adapter for DevModeFan {
|
||||
fn on_enable_toggled(&self, settings: &crate::datastructs::Settings) {
|
||||
log::info!("on_enable_toggled invoked with settings {:?}", settings);
|
||||
}
|
||||
|
||||
fn control_fan(&self, settings: &crate::datastructs::Settings, sensor: &crate::adapters::traits::SensorReading) {
|
||||
log::info!("control_fan invoked with settings {:?} and sensor {:?}", settings, sensor);
|
||||
}
|
||||
}
|
10
backend-rs/src/adapters/fans/mod.rs
Normal file
10
backend-rs/src/adapters/fans/mod.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
mod dev_mode;
|
||||
pub use dev_mode::DevModeFan;
|
||||
|
||||
mod steam_deck;
|
||||
pub use steam_deck::SteamDeckFan;
|
||||
|
||||
mod rog_ally;
|
||||
pub use rog_ally::RogAllyFan;
|
||||
|
||||
pub(self) use super::FanAdapter as Adapter;
|
159
backend-rs/src/adapters/fans/rog_ally/adapter.rs
Normal file
159
backend-rs/src/adapters/fans/rog_ally/adapter.rs
Normal file
|
@ -0,0 +1,159 @@
|
|||
use sysfuss::{BasicEntityPath, HwMonAttribute, HwMonAttributeItem, HwMonAttributeType, HwMonPath, SysAttributeExt};
|
||||
use sysfuss::{SysPath, SysEntityAttributesExt};
|
||||
|
||||
use crate::datastructs::{Settings, GraphPoint};
|
||||
|
||||
const ASUS_CUSTOM_FAN_CURVE_HWMON_NAME: &str = "asus_custom_fan_curve";
|
||||
const ASUS_REGULAR_HWMON_NAME: &str = "asus";
|
||||
|
||||
pub struct RogAllyFan {
|
||||
hwmon_curve: HwMonPath,
|
||||
hwmon_asus: HwMonPath,
|
||||
}
|
||||
|
||||
impl RogAllyFan {
|
||||
pub fn maybe_find() -> Option<Self> {
|
||||
let syspath = SysPath::path(crate::sys::SYSFS_ROOT);
|
||||
match syspath.hwmon_by_name(ASUS_CUSTOM_FAN_CURVE_HWMON_NAME)
|
||||
{
|
||||
Err(e) => {
|
||||
log::error!("sysfs hwmon iter error while finding Asus ROG Ally fan curve: {}", e);
|
||||
None
|
||||
},
|
||||
Ok(hwmon_curve) => {
|
||||
match syspath.hwmon_by_name(ASUS_REGULAR_HWMON_NAME) {
|
||||
Err(e) => {
|
||||
log::error!("sysfs hwmon iter error while finding Asus fan: {}", e);
|
||||
None
|
||||
},
|
||||
Ok(hwmon_asus) => {
|
||||
Some(Self {
|
||||
hwmon_curve,
|
||||
hwmon_asus,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// returns true when the point probably doesn't exist at all
|
||||
// (instead of it just running out of pwm# entries)
|
||||
fn set_gp(gp_i: u64, gp: &GraphPoint, basic_curve: &BasicEntityPath) -> bool {
|
||||
for pwm_i in 1..u64::MAX {
|
||||
let temp_attr = pwm_point_temp(pwm_i, gp_i);
|
||||
let pwm_attr = pwm_point_pwm(pwm_i, gp_i);
|
||||
if !(temp_attr.exists(basic_curve) && pwm_attr.exists(basic_curve)) {
|
||||
return pwm_i == 1;
|
||||
}
|
||||
let pwm_val = (gp.y * 255.0).round() as u8;
|
||||
if let Err(e) = basic_curve.set(&pwm_attr as &str, pwm_val) {
|
||||
let attr_path = pwm_attr.path(basic_curve);
|
||||
log::error!("failed to write to {}: {}", attr_path.display(), e);
|
||||
}
|
||||
let temp_val = (gp.x * 100.0).round() as u8;
|
||||
if let Err(e) = basic_curve.set(&temp_attr as &str, temp_val) {
|
||||
let attr_path = temp_attr.path(basic_curve);
|
||||
log::error!("failed to write to {}: {}", attr_path.display(), e);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
impl super::super::Adapter for RogAllyFan {
|
||||
fn on_enable_toggled(&self, settings: &Settings) {
|
||||
let value_to_set = settings.enable as u8;
|
||||
for i in 1..u64::MAX {
|
||||
let attr = pwm_enable_attr_name(i);
|
||||
if self.hwmon_curve.exists(&attr) {
|
||||
if let Err(e) = self.hwmon_curve.set(attr, value_to_set) {
|
||||
log::error!("failed to write to {}: {}", self.hwmon_curve.path_to(attr).display(), e);
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn control_fan(&self, settings: &Settings, _sensor: &crate::adapters::traits::SensorReading) {
|
||||
let basic_curve = BasicEntityPath::new(self.hwmon_curve.as_ref());
|
||||
if settings.curve.is_empty() {
|
||||
for i in 1..u64::MAX {
|
||||
let out_of_points = if i == 1 {
|
||||
Self::set_gp(i as _, &GraphPoint { x: 0.0, y: 0.0 }, &basic_curve)
|
||||
} else {
|
||||
Self::set_gp(i as _, &GraphPoint { x: 1.0, y: 1.0 }, &basic_curve)
|
||||
};
|
||||
if out_of_points { break; }
|
||||
}
|
||||
}
|
||||
for (i, gp) in settings.curve.iter().enumerate() {
|
||||
Self::set_gp(i as _, gp, &basic_curve);
|
||||
}
|
||||
// set remaining pwm points to highest in curve
|
||||
for i in settings.curve.len()..usize::MAX {
|
||||
if Self::set_gp(i as _, &GraphPoint { x: 1.0, y: 1.0 }, &basic_curve) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn sensor<'a: 'b, 'b>(&'a self) -> Option<&'b dyn crate::adapters::SensorAdapter> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl super::super::super::SensorAdapter for RogAllyFan {
|
||||
fn read(&self) -> Result<crate::adapters::SensorReading, std::io::Error> {
|
||||
let mut readings = Vec::new();
|
||||
let mut last_error = None;
|
||||
for i in 1..u64::MAX {
|
||||
let attr = fan_reading_attr_name(i);
|
||||
let reading = match self.hwmon_asus.attribute::<u64>(attr) {
|
||||
Ok(reading) => reading,
|
||||
Err(sysfuss::EitherErr2::First(e1)) => {
|
||||
log::debug!("failed to read attribute {}: {} (ok; breaking loop...)", self.hwmon_asus.path_to(attr).display(), e1);
|
||||
last_error = Some(e1);
|
||||
break;
|
||||
},
|
||||
Err(sysfuss::EitherErr2::Second(e2)) => {
|
||||
log::debug!("failed to read attribute {}: {} (ok; breaking loop...)", self.hwmon_asus.path_to(attr).display(), e2);
|
||||
last_error = Some(std::io::Error::other(e2));
|
||||
break;
|
||||
},
|
||||
};
|
||||
readings.push(reading);
|
||||
}
|
||||
let mut reading_sum = 0;
|
||||
for reading in readings.iter() {
|
||||
reading_sum += *reading;
|
||||
}
|
||||
if readings.is_empty() {
|
||||
Err(last_error.unwrap())
|
||||
} else {
|
||||
Ok(crate::adapters::SensorReading {
|
||||
value: (reading_sum / (readings.len() as u64)) as f64,
|
||||
meaning: crate::adapters::SensorType::Fan,
|
||||
name: "ROG Ally Fan",
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fn pwm_enable_attr_name(index: u64) -> HwMonAttribute {
|
||||
HwMonAttribute::new(HwMonAttributeType::Pwm, index, HwMonAttributeItem::Enable)
|
||||
}
|
||||
|
||||
fn fan_reading_attr_name(index: u64) -> HwMonAttribute {
|
||||
HwMonAttribute::new(HwMonAttributeType::Fan, index, HwMonAttributeItem::Input)
|
||||
}
|
||||
|
||||
fn pwm_point_temp(index: u64, point_index: u64) -> String {
|
||||
format!("pwm{}_auto_point{}_temp", index, point_index)
|
||||
}
|
||||
|
||||
fn pwm_point_pwm(index: u64, point_index: u64) -> String {
|
||||
format!("pwm{}_auto_point{}_pwm", index, point_index)
|
||||
}
|
2
backend-rs/src/adapters/fans/rog_ally/mod.rs
Normal file
2
backend-rs/src/adapters/fans/rog_ally/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
mod adapter;
|
||||
pub use adapter::RogAllyFan;
|
297
backend-rs/src/adapters/fans/steam_deck/adapter.rs
Normal file
297
backend-rs/src/adapters/fans/steam_deck/adapter.rs
Normal file
|
@ -0,0 +1,297 @@
|
|||
use sysfuss::{HwMonPath, HwMonAttribute, HwMonAttributeType, HwMonAttributeItem, BasicEntityPath};
|
||||
use sysfuss::{SysPath, capability::attributes, SysEntityAttributesExt};
|
||||
|
||||
use crate::datastructs::{Settings, GraphPoint};
|
||||
|
||||
const VALVE_FAN_SERVICE: &str = "jupiter-fan-control.service";
|
||||
|
||||
pub struct SteamDeckFan {
|
||||
hwmon: HwMonPath,
|
||||
}
|
||||
|
||||
impl SteamDeckFan {
|
||||
pub fn maybe_find() -> Option<Self> {
|
||||
find_hwmon(crate::sys::SYSFS_ROOT).map(|hwmon| Self { hwmon })
|
||||
}
|
||||
|
||||
pub fn maybe_find_thermal_zone() -> Option<BasicEntityPath> {
|
||||
find_thermal_zone(crate::sys::SYSFS_ROOT)
|
||||
}
|
||||
}
|
||||
|
||||
impl super::super::Adapter for SteamDeckFan {
|
||||
fn on_enable_toggled(&self, settings: &Settings) {
|
||||
on_set_enable(settings, &self.hwmon)
|
||||
}
|
||||
|
||||
fn control_fan(&self, settings: &Settings, sensor: &crate::adapters::traits::SensorReading) {
|
||||
enforce_jupiter_status(true);
|
||||
do_fan_control(settings, &self.hwmon, sensor.value)
|
||||
}
|
||||
|
||||
fn sensor<'a: 'b, 'b>(&'a self) -> Option<&'b dyn crate::adapters::SensorAdapter> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl super::super::super::SensorAdapter for SteamDeckFan {
|
||||
fn read(&self) -> Result<crate::adapters::SensorReading, std::io::Error> {
|
||||
Ok(crate::adapters::SensorReading {
|
||||
value: read_fan(&self.hwmon)? as f64,
|
||||
meaning: crate::adapters::SensorType::Fan,
|
||||
name: "Steam Deck Fan",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const RECALCULATE_ATTR: HwMonAttribute = HwMonAttribute::custom("recalculate");
|
||||
const FAN1_INPUT_ATTR: HwMonAttribute = HwMonAttribute::new(HwMonAttributeType::Fan, 1, HwMonAttributeItem::Input);
|
||||
const FAN1_LABEL_ATTR: HwMonAttribute = HwMonAttribute::new(HwMonAttributeType::Fan, 1, HwMonAttributeItem::Label);
|
||||
const FAN1_TARGET_ATTR: HwMonAttribute = HwMonAttribute::custom("fan1_target");
|
||||
|
||||
const HWMON_NEEDS: [HwMonAttribute; 3] = [
|
||||
//RECALCULATE_ATTR,
|
||||
FAN1_INPUT_ATTR,
|
||||
FAN1_TARGET_ATTR,
|
||||
FAN1_LABEL_ATTR,
|
||||
];
|
||||
|
||||
fn find_hwmon<P: AsRef<std::path::Path>>(path: P) -> Option<HwMonPath> {
|
||||
let syspath = SysPath::path(path);
|
||||
match syspath.hwmon(attributes(HWMON_NEEDS.into_iter()))
|
||||
{
|
||||
Err(e) => {
|
||||
log::error!("sysfs hwmon iter error while finding Steam Deck fan: {}", e);
|
||||
None
|
||||
},
|
||||
Ok(mut iter) => {
|
||||
if let Some(entity) = iter.next() {
|
||||
log::info!("Found Steam Deck fan hwmon {}", entity.as_ref().display());
|
||||
Some(entity)
|
||||
} else {
|
||||
log::error!("sysfs hwmon iter empty while finding Steam Deck fan: [no capable results]");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn find_thermal_zone<P: AsRef<std::path::Path>>(path: P) -> Option<BasicEntityPath> {
|
||||
let syspath = SysPath::path(path);
|
||||
match syspath.class("thermal",
|
||||
|ent: &BasicEntityPath| ent.exists(&"temp".to_owned()) && ent.exists(&"type".to_owned()) && ent.attribute("type".to_owned()).map(|val: String| val.to_lowercase() != "processor").unwrap_or(false))
|
||||
{
|
||||
Err(e) => {
|
||||
log::error!("sysfs thermal class iter error while finding Steam Deck thermal zone: {}", e);
|
||||
None
|
||||
},
|
||||
Ok(mut iter) => {
|
||||
if let Some(entity) = iter.next() {
|
||||
log::info!("Found thermal zone {}", entity.as_ref().display());
|
||||
Some(entity)
|
||||
} else {
|
||||
log::error!("sysfs thermal class iter empty while finding Steam Deck thermal zone: [no capable results]");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn on_set_enable(settings: &Settings, hwmon: &HwMonPath) {
|
||||
// stop/start jupiter fan control (since the client-side way of doing this was removed :( )
|
||||
enforce_jupiter_status(settings.enable);
|
||||
if let Err(e) = write_fan_recalc(hwmon, settings.enable) {
|
||||
log::error!("runtime failed to write to fan recalculate file: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
fn enforce_jupiter_status(enabled: bool) {
|
||||
// enabled refers to whether this plugin's functionality is enabled,
|
||||
// not the jupiter fan control service
|
||||
let service_status = detect_jupiter_fan_service();
|
||||
log::debug!("fan control service is enabled? {}", service_status);
|
||||
if enabled == service_status {
|
||||
// do not run Valve's fan service along with Fantastic, since they fight
|
||||
if enabled {
|
||||
stop_fan_service();
|
||||
} else {
|
||||
start_fan_service();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn detect_jupiter_fan_service() -> bool {
|
||||
match std::process::Command::new("systemctl")
|
||||
.args(["is-active", VALVE_FAN_SERVICE])
|
||||
.output() {
|
||||
Ok(cmd) => String::from_utf8_lossy(&cmd.stdout).trim() == "active",
|
||||
Err(e) => {
|
||||
log::error!("`systemctl is-active {}` err: {}", VALVE_FAN_SERVICE, e);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn start_fan_service() {
|
||||
match std::process::Command::new("systemctl")
|
||||
.args(["start", VALVE_FAN_SERVICE])
|
||||
.output() {
|
||||
Err(e) => log::error!("`systemctl start {}` err: {}", VALVE_FAN_SERVICE, e),
|
||||
Ok(out) => log::debug!("started `{}`:\nstdout:{}\nstderr:{}", VALVE_FAN_SERVICE, String::from_utf8_lossy(&out.stdout), String::from_utf8_lossy(&out.stderr)),
|
||||
}
|
||||
}
|
||||
|
||||
fn stop_fan_service() {
|
||||
match std::process::Command::new("systemctl")
|
||||
.args(["stop", VALVE_FAN_SERVICE])
|
||||
.output() {
|
||||
Err(e) => log::error!("`systemctl stop {}` err: {}", VALVE_FAN_SERVICE, e),
|
||||
Ok(out) => log::debug!("stopped `{}`:\nstdout:{}\nstderr:{}", VALVE_FAN_SERVICE, String::from_utf8_lossy(&out.stdout), String::from_utf8_lossy(&out.stderr)),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_fan_recalc(hwmon: &HwMonPath, enabled: bool) -> Result<(), std::io::Error> {
|
||||
hwmon.set(RECALCULATE_ATTR, enabled as u8)
|
||||
//write_single(format!("/sys/class/hwmon/hwmon{}/recalculate", HWMON_INDEX), enabled as u8)
|
||||
}
|
||||
|
||||
fn write_fan_target(hwmon: &HwMonPath, rpm: u64) -> Result<(), std::io::Error> {
|
||||
hwmon.set(FAN1_TARGET_ATTR, rpm)
|
||||
//write_single(format!("/sys/class/hwmon/hwmon{}/fan1_target", HWMON_INDEX), rpm)
|
||||
}
|
||||
|
||||
fn read_fan(hwmon: &HwMonPath) -> std::io::Result<u64> {
|
||||
match hwmon.attribute(FAN1_INPUT_ATTR){
|
||||
Ok(x) => Ok(x),
|
||||
Err(sysfuss::EitherErr2::First(e)) => {
|
||||
log::error!("Failed Steam Deck read_fan(): {}", e);
|
||||
Err(e)
|
||||
},
|
||||
Err(sysfuss::EitherErr2::Second(e)) => {
|
||||
log::error!("Failed Steam Deck read_fan(): {}", e);
|
||||
Err(std::io::Error::other(e))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn fan_range() -> std::ops::Range<f64> {
|
||||
let sys = sysinfo::System::new_with_specifics(
|
||||
sysinfo::RefreshKind::new().with_cpu(sysinfo::CpuRefreshKind::everything()),
|
||||
);
|
||||
if sys.cpus().is_empty() {
|
||||
1.0..7123.0
|
||||
} else {
|
||||
let cpu_name = sys.cpus()[0].brand();
|
||||
if cpu_name.contains("AMD Custom APU 0405") {
|
||||
// LCD
|
||||
1.0..7000.0
|
||||
} else if cpu_name.contains("AMD Custom APU 0932") {
|
||||
// OLED
|
||||
1.0..9000.0
|
||||
} else {
|
||||
// Hopefully never reached
|
||||
1.0..7001.0
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn do_fan_control(settings: &Settings, hwmon: &HwMonPath, thermal_zone: f64) {
|
||||
/*
|
||||
curve = self.settings["curve"]
|
||||
fan_ratio = 0 # unnecessary in Python, but stupid without
|
||||
if len(curve) == 0:
|
||||
fan_ratio = 1
|
||||
else:
|
||||
index = -1
|
||||
temperature_ratio = (thermal_zone(0) - TEMPERATURE_MINIMUM) / (TEMPERATURE_MAXIMUM - TEMPERATURE_MINIMUM)
|
||||
for i in range(len(curve)-1, -1, -1):
|
||||
if curve[i]["x"] < temperature_ratio:
|
||||
index = i
|
||||
break
|
||||
if self.settings["interpolate"]:
|
||||
fan_ratio = self.interpolate_fan(self, index, temperature_ratio)
|
||||
else:
|
||||
fan_ratio = self.step_fan(self, index, temperature_ratio)
|
||||
set_fan_target(int((fan_ratio * FAN_MAXIMUM) + FAN_MINIMUM))
|
||||
*/
|
||||
let fan_bounds = fan_range();
|
||||
let temperature_ratio = ((thermal_zone as f64)/1000.0) / 100.0 /* temperature range in C */;
|
||||
let mut index = None;
|
||||
for i in (0..settings.curve.len()).rev() {
|
||||
if settings.curve[i].x < temperature_ratio {
|
||||
index = Some(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
let fan_ratio = if settings.interpolate {
|
||||
interpolate_fan(settings, index, temperature_ratio)
|
||||
} else {
|
||||
step_fan(settings, index, temperature_ratio)
|
||||
};
|
||||
let fan_speed: u64 = ((fan_ratio * (fan_bounds.end - fan_bounds.start)) + fan_bounds.start) as _;
|
||||
if let Err(e) = write_fan_target(hwmon, fan_speed) {
|
||||
log::error!("Failed to write to Steam Deck fan target file: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
fn interpolate_fan(settings: &Settings, index: Option<usize>, t_ratio: f64) -> f64 {
|
||||
/*
|
||||
curve = self.settings["curve"]
|
||||
upper_point = {"x": 1.0, "y": 0.0}
|
||||
lower_point = {"x": 0.0, "y": 1.0}
|
||||
if index != -1: # guaranteed to not be empty
|
||||
lower_point = curve[index]
|
||||
if index != len(curve) - 1:
|
||||
upper_point = curve[index+1]
|
||||
#logging.debug(f"lower_point: {lower_point}, upper_point: {upper_point}")
|
||||
upper_y = 1-upper_point["y"]
|
||||
lower_y = 1-lower_point["y"]
|
||||
slope_m = (upper_y - lower_y) / (upper_point["x"] - lower_point["x"])
|
||||
y_intercept_b = lower_y - (slope_m * lower_point["x"])
|
||||
logging.debug(f"interpolation: y = {slope_m}x + {y_intercept_b}")
|
||||
return (slope_m * temperature_ratio) + y_intercept_b
|
||||
*/
|
||||
let (upper, lower) = if let Some(i) = index {
|
||||
(if i != settings.curve.len() - 1 {
|
||||
settings.curve[i+1].clone()
|
||||
} else {
|
||||
GraphPoint{x: 1.0, y: 1.0}
|
||||
},
|
||||
settings.curve[i].clone())
|
||||
} else {
|
||||
(if settings.curve.is_empty() {
|
||||
GraphPoint{x: 1.0, y: 1.0}
|
||||
} else {
|
||||
settings.curve[0].clone()
|
||||
},
|
||||
GraphPoint{x: 0.0, y: 0.0})
|
||||
};
|
||||
let slope_m = (upper.y - lower.y) / (upper.x - lower.x);
|
||||
let y_intercept_b = lower.y - (slope_m * lower.x);
|
||||
log::debug!("interpolation: y = {}x + {} (between {:?} and {:?})", slope_m, y_intercept_b, upper, lower);
|
||||
(slope_m * t_ratio) + y_intercept_b
|
||||
}
|
||||
|
||||
fn step_fan(settings: &Settings, index: Option<usize>, _t_ratio: f64) -> f64 {
|
||||
/*
|
||||
curve = self.settings["curve"]
|
||||
if index != -1:
|
||||
return 1 - curve[index]["y"]
|
||||
else:
|
||||
if len(curve) == 0:
|
||||
return 1
|
||||
else:
|
||||
return 0.5
|
||||
*/
|
||||
// step fan, what are you doing?
|
||||
if let Some(index) = index {
|
||||
settings.curve[index].y
|
||||
} else {
|
||||
if settings.curve.is_empty() {
|
||||
1.0
|
||||
} else {
|
||||
0.5
|
||||
}
|
||||
}
|
||||
}
|
2
backend-rs/src/adapters/fans/steam_deck/mod.rs
Normal file
2
backend-rs/src/adapters/fans/steam_deck/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
mod adapter;
|
||||
pub use adapter::SteamDeckFan;
|
5
backend-rs/src/adapters/mod.rs
Normal file
5
backend-rs/src/adapters/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
mod traits;
|
||||
pub use traits::{FanAdapter, SensorAdapter, SensorReading, SensorType};
|
||||
|
||||
pub mod fans;
|
||||
pub mod sensors;
|
12
backend-rs/src/adapters/sensors/dev_mode.rs
Normal file
12
backend-rs/src/adapters/sensors/dev_mode.rs
Normal file
|
@ -0,0 +1,12 @@
|
|||
pub struct DevModeSensor;
|
||||
|
||||
impl super::Adapter for DevModeSensor {
|
||||
fn read(&self) -> Result<super::Reading, std::io::Error> {
|
||||
log::info!("read invoked");
|
||||
return Ok(super::Reading {
|
||||
value: 42_000.0,
|
||||
meaning: super::super::SensorType::Temperature,
|
||||
name: "DevModeSensor",
|
||||
})
|
||||
}
|
||||
}
|
8
backend-rs/src/adapters/sensors/mod.rs
Normal file
8
backend-rs/src/adapters/sensors/mod.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
mod dev_mode;
|
||||
pub use dev_mode::DevModeSensor;
|
||||
|
||||
mod thermal_zone;
|
||||
pub use thermal_zone::ThermalZoneSensor;
|
||||
|
||||
pub(self) use super::SensorAdapter as Adapter;
|
||||
pub(self) use super::SensorReading as Reading;
|
44
backend-rs/src/adapters/sensors/thermal_zone.rs
Normal file
44
backend-rs/src/adapters/sensors/thermal_zone.rs
Normal file
|
@ -0,0 +1,44 @@
|
|||
use sysfuss::BasicEntityPath;
|
||||
use sysfuss::SysEntityAttributesExt;
|
||||
|
||||
pub struct ThermalZoneSensor {
|
||||
zone: BasicEntityPath,
|
||||
}
|
||||
|
||||
impl ThermalZoneSensor {
|
||||
pub fn new<P: AsRef<std::path::Path>>(zone_path: P) -> Self {
|
||||
Self { zone: BasicEntityPath::new(zone_path) }
|
||||
}
|
||||
|
||||
pub fn new_if_exists<P: AsRef<std::path::Path>>(zone_path: P) -> Option<Self> {
|
||||
if zone_path.as_ref().exists() {
|
||||
Some(Self { zone: BasicEntityPath::new(zone_path) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl super::Adapter for ThermalZoneSensor {
|
||||
fn read(&self) -> Result<super::Reading, std::io::Error> {
|
||||
Ok(super::Reading {
|
||||
value: read_thermal_zone(&self.zone)? as f64,
|
||||
meaning: super::super::SensorType::Temperature,
|
||||
name: "thermal_zone",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn read_thermal_zone(entity: &BasicEntityPath) -> std::io::Result<u64> {
|
||||
match entity.attribute("temp".to_owned()) {
|
||||
Ok(x) => Ok(x),
|
||||
Err(sysfuss::EitherErr2::First(e)) => {
|
||||
log::error!("Failed read_thermal_zone(): {}", e);
|
||||
Err(e)
|
||||
},
|
||||
Err(sysfuss::EitherErr2::Second(e)) => {
|
||||
log::error!("Failed read_thermal_zone(): {}", e);
|
||||
Err(std::io::Error::other(e))
|
||||
},
|
||||
}
|
||||
}
|
30
backend-rs/src/adapters/traits.rs
Normal file
30
backend-rs/src/adapters/traits.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
use crate::datastructs::Settings;
|
||||
|
||||
pub trait FanAdapter: Send + Sync {
|
||||
/// Handle fan enable UI toggle
|
||||
fn on_enable_toggled(&self, settings: &Settings);
|
||||
/// Apply fan settings to fan (probably through udev/sysfs)
|
||||
fn control_fan(&self, settings: &Settings, sensor: &SensorReading);
|
||||
/// Get fan speed sensor
|
||||
fn sensor<'a: 'b, 'b>(&'a self) -> Option<&'b dyn SensorAdapter> { None }
|
||||
}
|
||||
|
||||
pub trait SensorAdapter: Send + Sync {
|
||||
/// Read sensor value
|
||||
fn read(&self) -> Result<SensorReading, std::io::Error>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct SensorReading {
|
||||
pub value: f64,
|
||||
pub meaning: SensorType,
|
||||
pub name: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum SensorType {
|
||||
Temperature, // milli-degrees Celcius
|
||||
Fan, // revolutions per minute (RPM)
|
||||
#[allow(dead_code)]
|
||||
Unknown,
|
||||
}
|
|
@ -1,259 +1,238 @@
|
|||
use usdpl_back::core::serdes::Primitive;
|
||||
use crate::services::fantastic::*;
|
||||
|
||||
use usdpl_back::nrpc::_helpers::futures::{StreamExt, FutureExt};
|
||||
|
||||
use super::control::ControlRuntime;
|
||||
use super::json::GraphPointJson;
|
||||
|
||||
pub const VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
||||
pub const NAME: &'static str = env!("CARGO_PKG_NAME");
|
||||
|
||||
pub fn hello(params: Vec<Primitive>) -> Vec<Primitive> {
|
||||
if let Some(Primitive::String(name)) = params.get(0) {
|
||||
vec![Primitive::String(format!("Hello {}", name))]
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
const FAN_READ_PERIOD: std::time::Duration = std::time::Duration::from_millis(1000);
|
||||
const TEMPERATURE_READ_PERIOD: std::time::Duration = std::time::Duration::from_millis(2000);
|
||||
|
||||
pub struct FanService {
|
||||
ctrl: ControlRuntime,
|
||||
}
|
||||
|
||||
pub fn echo(params: Vec<Primitive>) -> Vec<Primitive> {
|
||||
params
|
||||
}
|
||||
|
||||
pub fn version(_: Vec<Primitive>) -> Vec<Primitive> {
|
||||
vec![VERSION.into()]
|
||||
}
|
||||
|
||||
pub fn name(_: Vec<Primitive>) -> Vec<Primitive> {
|
||||
vec![NAME.into()]
|
||||
}
|
||||
|
||||
pub fn get_fan_rpm(_: Vec<Primitive>) -> Vec<Primitive> {
|
||||
if let Some(rpm) = crate::sys::read_fan() {
|
||||
log::debug!("get_fan_rpm() success: {}", rpm);
|
||||
vec![rpm.into()]
|
||||
} else {
|
||||
log::error!("get_fan_rpm failed to read fan speed");
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_temperature(_: Vec<Primitive>) -> Vec<Primitive> {
|
||||
if let Some(temperature) = crate::sys::read_thermal_zone(0) {
|
||||
let real_temp = temperature as f64 / 1000.0;
|
||||
log::debug!("get_temperature() success: {}", real_temp);
|
||||
vec![real_temp.into()]
|
||||
} else {
|
||||
log::error!("get_fan_rpm failed to read fan speed");
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_enable_gen(runtime: &ControlRuntime) -> impl Fn(Vec<Primitive>) -> Vec<Primitive> {
|
||||
let runtime_settings = runtime.settings_clone();
|
||||
let runtime_state = runtime.state_clone();
|
||||
move |params| {
|
||||
if let Some(Primitive::Bool(enabled)) = params.get(0) {
|
||||
let mut settings = match runtime_settings.write() {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
log::error!("set_enable failed to acquire settings write lock: {}", e);
|
||||
return vec![];
|
||||
}
|
||||
};
|
||||
if settings.enable != *enabled {
|
||||
settings.enable = *enabled;
|
||||
let mut state = match runtime_state.write() {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
log::error!("set_enable failed to acquire state write lock: {}", e);
|
||||
return vec![];
|
||||
}
|
||||
};
|
||||
state.dirty = true;
|
||||
log::debug!("set_enable({}) success", enabled);
|
||||
}
|
||||
vec![(*enabled).into()]
|
||||
} else {
|
||||
Vec::new()
|
||||
impl FanService {
|
||||
pub fn new(runtime: ControlRuntime) -> Self {
|
||||
runtime.run();
|
||||
Self {
|
||||
ctrl: runtime,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_enable_gen(runtime: &ControlRuntime) -> impl Fn(Vec<Primitive>) -> Vec<Primitive> {
|
||||
let runtime_settings = runtime.settings_clone();
|
||||
move |_| {
|
||||
let lock = match runtime_settings.read() {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
log::error!("get_enable failed to acquire settings read lock: {}", e);
|
||||
return vec![];
|
||||
}
|
||||
};
|
||||
log::debug!("get_enable() success");
|
||||
vec![lock.enable.into()]
|
||||
}
|
||||
fn once_true() -> impl std::iter::Iterator<Item = bool> {
|
||||
// iters over [true, false, false, ...]
|
||||
std::iter::once(true).chain(std::iter::repeat(false))
|
||||
}
|
||||
|
||||
pub fn set_interpolate_gen(runtime: &ControlRuntime) -> impl Fn(Vec<Primitive>) -> Vec<Primitive> {
|
||||
let runtime_settings = runtime.settings_clone();
|
||||
let runtime_state = runtime.state_clone();
|
||||
move |params| {
|
||||
if let Some(Primitive::Bool(enabled)) = params.get(0) {
|
||||
let mut settings = match runtime_settings.write() {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
log::error!("set_enable failed to acquire settings write lock: {}", e);
|
||||
return vec![];
|
||||
}
|
||||
};
|
||||
if settings.interpolate != *enabled {
|
||||
settings.interpolate = *enabled;
|
||||
let mut state = match runtime_state.write() {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
log::error!("set_interpolate failed to acquire state write lock: {}", e);
|
||||
return vec![];
|
||||
}
|
||||
};
|
||||
state.dirty = true;
|
||||
log::debug!("set_interpolate({}) success", enabled);
|
||||
}
|
||||
vec![(*enabled).into()]
|
||||
} else {
|
||||
Vec::new()
|
||||
#[::nrpc::_helpers::async_trait::async_trait]
|
||||
impl<'a> IFan<'a> for FanService {
|
||||
async fn echo(
|
||||
&mut self,
|
||||
input: EchoMessage,
|
||||
) -> Result<EchoMessage, Box<dyn std::error::Error + Send>> {
|
||||
Ok(input)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_interpolate_gen(runtime: &ControlRuntime) -> impl Fn(Vec<Primitive>) -> Vec<Primitive> {
|
||||
let runtime_settings = runtime.settings_clone();
|
||||
move |_| {
|
||||
let lock = match runtime_settings.read() {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
log::error!("get_interpolate failed to acquire settings read lock: {}", e);
|
||||
return vec![];
|
||||
}
|
||||
};
|
||||
log::debug!("get_interpolate() success");
|
||||
vec![lock.interpolate.into()]
|
||||
}
|
||||
}
|
||||
|
||||
fn curve_to_json(curve: &Vec<super::datastructs::GraphPoint>) -> serde_json::Result<String> {
|
||||
let mut curve_points = Vec::<GraphPointJson>::with_capacity(curve.len());
|
||||
for point in curve.iter() {
|
||||
curve_points.push(point.clone().into());
|
||||
}
|
||||
serde_json::to_string(&curve_points)
|
||||
}
|
||||
|
||||
pub fn get_curve_gen(runtime: &ControlRuntime) -> impl Fn(Vec<Primitive>) -> Vec<Primitive> {
|
||||
let runtime_settings = runtime.settings_clone();
|
||||
move |_| {
|
||||
let lock = match runtime_settings.read() {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
log::error!("get_curve failed to acquire settings read lock: {}", e);
|
||||
return vec![];
|
||||
}
|
||||
};
|
||||
let json_str = match curve_to_json(&lock.curve) {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
log::error!("get_curve failed to serialize points: {}", e);
|
||||
return vec![];
|
||||
}
|
||||
};
|
||||
log::debug!("get_curve() success");
|
||||
vec![Primitive::Json(json_str)]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_curve_point_gen(runtime: &ControlRuntime) -> impl Fn(Vec<Primitive>) -> Vec<Primitive> {
|
||||
let runtime_settings = runtime.settings_clone();
|
||||
let runtime_state = runtime.state_clone();
|
||||
move |params| {
|
||||
if let Some(Primitive::Json(json_str)) = params.get(0) {
|
||||
let mut settings = match runtime_settings.write() {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
log::error!("add_curve_point failed to acquire settings write lock: {}", e);
|
||||
return vec![];
|
||||
}
|
||||
};
|
||||
let new_point: GraphPointJson = match serde_json::from_str(&json_str) {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
log::error!("add_curve_point failed deserialize point json: {}", e);
|
||||
return vec![];
|
||||
}
|
||||
};
|
||||
let version = settings.version;
|
||||
settings.curve.push(super::datastructs::GraphPoint::from_json(new_point, version));
|
||||
settings.sort_curve();
|
||||
let mut state = match runtime_state.write() {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
log::error!("add_curve_point failed to acquire state write lock: {}", e);
|
||||
return vec![];
|
||||
}
|
||||
};
|
||||
state.dirty = true;
|
||||
let json_str = match curve_to_json(&settings.curve) {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
log::error!("add_curve_point failed to serialize points: {}", e);
|
||||
return vec![];
|
||||
}
|
||||
};
|
||||
log::debug!("add_curve_point({}) success", json_str);
|
||||
vec![Primitive::Json(json_str)]
|
||||
} else {
|
||||
Vec::new()
|
||||
async fn hello(
|
||||
&mut self,
|
||||
input: NameMessage,
|
||||
) -> Result<HelloResponse, Box<dyn std::error::Error + Send>> {
|
||||
Ok(HelloResponse {
|
||||
phrase: format!("Hello {}", input.name)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_curve_point_gen(runtime: &ControlRuntime) -> impl Fn(Vec<Primitive>) -> Vec<Primitive> {
|
||||
let runtime_settings = runtime.settings_clone();
|
||||
let runtime_state = runtime.state_clone();
|
||||
move |params| {
|
||||
if let Some(Primitive::F64(index)) = params.get(0) {
|
||||
let mut settings = match runtime_settings.write() {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
log::error!("remove_curve_point failed to acquire settings write lock: {}", e);
|
||||
return vec![];
|
||||
async fn version(
|
||||
&mut self,
|
||||
_input: Empty,
|
||||
) -> Result<VersionMessage, Box<dyn std::error::Error + Send>> {
|
||||
Ok(
|
||||
VersionMessage {
|
||||
major: 0,
|
||||
minor: 0,
|
||||
patch: 0,
|
||||
//display: VERSION.to_string(),
|
||||
}
|
||||
};
|
||||
let rounded = index.round();
|
||||
if rounded >= 0.0 && rounded < settings.curve.len() as _ {
|
||||
let index = rounded as usize;
|
||||
settings.curve.swap_remove(index);
|
||||
settings.sort_curve();
|
||||
let mut state = match runtime_state.write() {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
log::error!("remove_curve_point failed to acquire state write lock: {}", e);
|
||||
return vec![];
|
||||
}
|
||||
};
|
||||
state.dirty = true;
|
||||
let json_str = match curve_to_json(&settings.curve) {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
log::error!("remove_curve_point failed to serialize points: {}", e);
|
||||
return vec![];
|
||||
}
|
||||
};
|
||||
log::debug!("remove_curve_point({}) success", json_str);
|
||||
vec![Primitive::Json(json_str)]
|
||||
)
|
||||
}
|
||||
async fn version_str(
|
||||
&mut self,
|
||||
_input: Empty,
|
||||
) -> Result<VersionDisplayMessage, Box<dyn std::error::Error + Send>> {
|
||||
Ok(
|
||||
VersionDisplayMessage {
|
||||
display: VERSION.to_owned(),
|
||||
}
|
||||
)
|
||||
}
|
||||
async fn name(
|
||||
&mut self,
|
||||
_input: Empty,
|
||||
) -> Result<NameMessage, Box<dyn std::error::Error + Send>> {
|
||||
Ok(
|
||||
NameMessage {
|
||||
name: NAME.to_string(),
|
||||
}
|
||||
)
|
||||
}
|
||||
async fn get_fan_rpm<'b: 'a>(
|
||||
&mut self,
|
||||
_input: Empty,
|
||||
) -> Result<
|
||||
usdpl_back::nrpc::ServiceServerStream<'b, RpmMessage>,
|
||||
Box<dyn std::error::Error + Send>,
|
||||
> {
|
||||
let fan_clone = self.ctrl.fan_clone();
|
||||
if fan_clone.sensor().is_some() {
|
||||
let stream = usdpl_back::nrpc::_helpers::futures::stream::iter(once_true()).then(move |is_first| {
|
||||
let fan_clone2 = fan_clone.clone();
|
||||
tokio::task::spawn_blocking(
|
||||
/* tokio::time::sleep(..) is not Unpin (but this is)... *grumble grumble* */
|
||||
move || if !is_first { std::thread::sleep(FAN_READ_PERIOD); })
|
||||
.map(move |_| {
|
||||
if let Some(fan_sensor) = fan_clone2.sensor() {
|
||||
match fan_sensor.read() {
|
||||
Ok(reading) => {
|
||||
log::debug!("get_fan_rpm() success: {}", reading.value);
|
||||
Ok(RpmMessage { rpm: reading.value as u32 })
|
||||
}
|
||||
Err(e) => {
|
||||
Err(usdpl_back::nrpc::ServiceError::Method(Box::<dyn std::error::Error + Send + Sync>::from(format!("Failed to read fan speed: {}", e))))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(usdpl_back::nrpc::ServiceError::Method(Box::<dyn std::error::Error + Send + Sync>::from("Failed to get fan speed sensor")))
|
||||
}
|
||||
})
|
||||
});
|
||||
Ok(Box::new(stream))
|
||||
} else {
|
||||
log::error!("remove_curve_point received index out of bounds: {} indexing array of length {}", index, settings.curve.len());
|
||||
return vec![];
|
||||
Ok(Box::new(usdpl_back::nrpc::_helpers::futures::stream::empty()))
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_temperature<'b: 'a>(
|
||||
&mut self,
|
||||
_input: Empty,
|
||||
) -> Result<
|
||||
usdpl_back::nrpc::ServiceServerStream<'b, TemperatureMessage>,
|
||||
Box<dyn std::error::Error + Send>,
|
||||
> {
|
||||
let sensor_clone = self.ctrl.sensor_clone();
|
||||
let stream = usdpl_back::nrpc::_helpers::futures::stream::iter(once_true()).then(move |is_first| {
|
||||
let sensor_clone2 = sensor_clone.clone();
|
||||
tokio::task::spawn_blocking(
|
||||
/* tokio::time::sleep(..) is not Unpin (but this is)... *grumble grumble* */
|
||||
move || if !is_first { std::thread::sleep(TEMPERATURE_READ_PERIOD); })
|
||||
.map(move |_| {
|
||||
match sensor_clone2.read() {
|
||||
Ok(reading) => {
|
||||
let real_temp = reading.value as f64 / 1000.0;
|
||||
log::debug!("get_temperature() success: {}", real_temp);
|
||||
Ok(TemperatureMessage { temperature: real_temp })
|
||||
},
|
||||
Err(e) => {
|
||||
Err(usdpl_back::nrpc::ServiceError::Method(Box::<dyn std::error::Error + Send + Sync>::from(format!("get_temperature failed to read sensor: {}", e))))
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
Ok(Box::new(stream))
|
||||
}
|
||||
|
||||
async fn set_enable(
|
||||
&mut self,
|
||||
input: EnablementMessage,
|
||||
) -> Result<EnablementMessage, Box<dyn std::error::Error + Send>>{
|
||||
let mut settings = self.ctrl.settings().write().await;
|
||||
if settings.enable != input.is_enabled {
|
||||
let mut state = self.ctrl.state().write().await;
|
||||
settings.enable = input.is_enabled;
|
||||
state.dirty = true;
|
||||
}
|
||||
log::debug!("set_enable({}) success", input.is_enabled);
|
||||
Ok(input)
|
||||
}
|
||||
async fn get_enable(
|
||||
&mut self,
|
||||
_input: Empty,
|
||||
) -> Result<EnablementMessage, Box<dyn std::error::Error + Send>>{
|
||||
let is_enabled = self.ctrl.settings().read().await.enable;
|
||||
log::debug!("get_enable() success");
|
||||
Ok(EnablementMessage { is_enabled })
|
||||
}
|
||||
async fn set_interpolate(
|
||||
&mut self,
|
||||
input: EnablementMessage,
|
||||
) -> Result<EnablementMessage, Box<dyn std::error::Error + Send>>{
|
||||
let mut settings = self.ctrl.settings().write().await;
|
||||
if settings.interpolate != input.is_enabled {
|
||||
let mut state = self.ctrl.state().write().await;
|
||||
settings.interpolate = input.is_enabled;
|
||||
state.dirty = true;
|
||||
}
|
||||
log::debug!("set_interpolate({}) success", input.is_enabled);
|
||||
Ok(input)
|
||||
}
|
||||
async fn get_interpolate(
|
||||
&mut self,
|
||||
_input: Empty,
|
||||
) -> Result<EnablementMessage, Box<dyn std::error::Error + Send>>{
|
||||
let is_enabled = self.ctrl.settings().read().await.interpolate;
|
||||
log::debug!("get_interpolate() success");
|
||||
Ok(EnablementMessage { is_enabled })
|
||||
}
|
||||
async fn get_curve_x(
|
||||
&mut self,
|
||||
_input: Empty,
|
||||
) -> Result<CurveMessageX, Box<dyn std::error::Error + Send>>{
|
||||
let settings = self.ctrl.settings().read().await;
|
||||
let x = settings.curve.iter().map(|p| p.x).collect();
|
||||
log::debug!("get_curve_x() success");
|
||||
Ok(CurveMessageX { x })
|
||||
}
|
||||
async fn get_curve_y(
|
||||
&mut self,
|
||||
_input: Empty,
|
||||
) -> Result<CurveMessageY, Box<dyn std::error::Error + Send>>{
|
||||
let settings = self.ctrl.settings().read().await;
|
||||
let y = settings.curve.iter().map(|p| p.y).collect();
|
||||
log::debug!("get_curve_x() success");
|
||||
Ok(CurveMessageY { y })
|
||||
}
|
||||
async fn add_curve_point(
|
||||
&mut self,
|
||||
point: GraphPoint,
|
||||
) -> Result<Empty, Box<dyn std::error::Error + Send>>{
|
||||
let mut settings = self.ctrl.settings().write().await;
|
||||
settings.curve.push(super::datastructs::GraphPoint {
|
||||
x: point.x,
|
||||
y: point.y
|
||||
});
|
||||
settings.sort_curve();
|
||||
let mut state = self.ctrl.state().write().await;
|
||||
state.dirty = true;
|
||||
log::debug!("add_curve_point(point: {:?}) success", point);
|
||||
Ok(Empty { ok: true })
|
||||
}
|
||||
async fn remove_curve_point(
|
||||
&mut self,
|
||||
input: IndexMessage,
|
||||
) -> Result<Empty, Box<dyn std::error::Error + Send>>{
|
||||
let mut settings = self.ctrl.settings().write().await;
|
||||
let i = input.index as usize;
|
||||
if settings.curve.len() > i {
|
||||
settings.curve.swap_remove(i);
|
||||
settings.sort_curve();
|
||||
let mut state = self.ctrl.state().write().await;
|
||||
state.dirty = true;
|
||||
log::debug!("remove_curve_point(point: {}) success", input.index);
|
||||
Ok(Empty { ok: true })
|
||||
} else {
|
||||
log::debug!("remove_curve_point(index: {}) failed", input.index);
|
||||
Ok(Empty { ok: false })
|
||||
}
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,27 +1,36 @@
|
|||
//! Fan control
|
||||
|
||||
use std::sync::{RwLock, Arc};
|
||||
use std::sync::Arc;
|
||||
//use std::collections::HashMap;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use std::thread;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use super::datastructs::{Settings, State, GraphPoint};
|
||||
use super::datastructs::{Settings, State};
|
||||
use super::json::SettingsJson;
|
||||
|
||||
const VALVE_FAN_SERVICE: &str = "jupiter-fan-control.service";
|
||||
|
||||
pub struct ControlRuntime {
|
||||
settings: Arc<RwLock<Settings>>,
|
||||
state: Arc<RwLock<State>>,
|
||||
fan_adapter: Arc<Box<dyn crate::adapters::FanAdapter + 'static>>,
|
||||
sensor_adapter: Arc<Box<dyn crate::adapters::SensorAdapter + 'static>>,
|
||||
}
|
||||
|
||||
impl ControlRuntime {
|
||||
pub fn new() -> Self {
|
||||
#[allow(dead_code)]
|
||||
pub fn new<F: crate::adapters::FanAdapter + 'static, S: crate::adapters::SensorAdapter + 'static>(fan: F, sensor: S) -> Self {
|
||||
Self::new_boxed(Box::new(fan), Box::new(sensor))
|
||||
}
|
||||
|
||||
pub(crate) fn new_boxed(fan: Box<dyn crate::adapters::FanAdapter + 'static>, sensor: Box<dyn crate::adapters::SensorAdapter + 'static>) -> Self {
|
||||
let new_state = State::new();
|
||||
let settings_p = settings_path(&new_state.home);
|
||||
Self {
|
||||
settings: Arc::new(RwLock::new(super::json::SettingsJson::open(settings_p).unwrap_or_default().into())),
|
||||
state: Arc::new(RwLock::new(new_state)),
|
||||
fan_adapter: Arc::new(fan),
|
||||
sensor_adapter: Arc::new(sensor),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,9 +42,27 @@ impl ControlRuntime {
|
|||
self.state.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn settings(&self) -> &'_ RwLock<Settings> {
|
||||
&self.settings
|
||||
}
|
||||
|
||||
pub(crate) fn state(&self) -> &'_ RwLock<State> {
|
||||
&self.state
|
||||
}
|
||||
|
||||
pub(crate) fn sensor_clone(&self) -> Arc<Box<dyn crate::adapters::SensorAdapter>> {
|
||||
self.sensor_adapter.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn fan_clone(&self) -> Arc<Box<dyn crate::adapters::FanAdapter>> {
|
||||
self.fan_adapter.clone()
|
||||
}
|
||||
|
||||
pub fn run(&self) -> thread::JoinHandle<()> {
|
||||
let runtime_settings = self.settings_clone();
|
||||
let runtime_state = self.state_clone();
|
||||
let runtime_fan = self.fan_adapter.clone();
|
||||
let runtime_sensor = self.sensor_adapter.clone();
|
||||
thread::spawn(move || {
|
||||
let sleep_duration = Duration::from_millis(1000);
|
||||
let mut start_time = Instant::now();
|
||||
|
@ -44,233 +71,43 @@ impl ControlRuntime {
|
|||
// resumed from sleep; do fan re-init
|
||||
log::debug!("Detected resume from sleep, overriding fan again");
|
||||
{
|
||||
let state = match runtime_state.read() {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
log::error!("runtime failed to acquire state read lock: {}", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let settings = match runtime_settings.read() {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
log::error!("runtime failed to acquire settings read lock: {}", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let settings = runtime_settings.blocking_read();
|
||||
if settings.enable {
|
||||
Self::on_set_enable(&settings, &state);
|
||||
runtime_fan.on_enable_toggled(&settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
start_time = Instant::now();
|
||||
{ // save to file
|
||||
let state = match runtime_state.read() {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
log::error!("runtime failed to acquire state read lock: {}", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let state = runtime_state.blocking_read();
|
||||
if state.dirty {
|
||||
// save settings to file
|
||||
let settings = match runtime_settings.read() {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
log::error!("runtime failed to acquire settings read lock: {}", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let settings = runtime_settings.blocking_read();
|
||||
let settings_json: SettingsJson = settings.clone().into();
|
||||
if let Err(e) = settings_json.save(settings_path(&state.home)) {
|
||||
log::error!("SettingsJson.save({}) error: {}", settings_path(&state.home).display(), e);
|
||||
}
|
||||
Self::on_set_enable(&settings, &state);
|
||||
runtime_fan.on_enable_toggled(&settings);
|
||||
drop(state);
|
||||
let mut state = match runtime_state.write() {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
log::error!("runtime failed to acquire state write lock: {}", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let mut state = runtime_state.blocking_write();
|
||||
state.dirty = false;
|
||||
}
|
||||
}
|
||||
{ // fan control
|
||||
let settings = match runtime_settings.read() {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
log::error!("runtime failed to acquire settings read lock: {}", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let settings = runtime_settings.blocking_read();
|
||||
if settings.enable {
|
||||
Self::enforce_jupiter_status(true);
|
||||
Self::do_fan_control(&settings);
|
||||
match runtime_sensor.read() {
|
||||
Err(e) => log::error!("Failed to read sensor for control_fan input: {}", e),
|
||||
Ok(reading) => {
|
||||
runtime_fan.control_fan(&settings, &reading);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
thread::sleep(sleep_duration);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn on_set_enable(settings: &Settings, _state: &State) {
|
||||
// stop/start jupiter fan control (since the client-side way of doing this was removed :( )
|
||||
Self::enforce_jupiter_status(settings.enable);
|
||||
if let Err(e) = crate::sys::write_fan_recalc(settings.enable) {
|
||||
log::error!("runtime failed to write to fan recalculate file: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
fn do_fan_control(settings: &Settings) {
|
||||
/*
|
||||
curve = self.settings["curve"]
|
||||
fan_ratio = 0 # unnecessary in Python, but stupid without
|
||||
if len(curve) == 0:
|
||||
fan_ratio = 1
|
||||
else:
|
||||
index = -1
|
||||
temperature_ratio = (thermal_zone(0) - TEMPERATURE_MINIMUM) / (TEMPERATURE_MAXIMUM - TEMPERATURE_MINIMUM)
|
||||
for i in range(len(curve)-1, -1, -1):
|
||||
if curve[i]["x"] < temperature_ratio:
|
||||
index = i
|
||||
break
|
||||
if self.settings["interpolate"]:
|
||||
fan_ratio = self.interpolate_fan(self, index, temperature_ratio)
|
||||
else:
|
||||
fan_ratio = self.step_fan(self, index, temperature_ratio)
|
||||
set_fan_target(int((fan_ratio * FAN_MAXIMUM) + FAN_MINIMUM))
|
||||
*/
|
||||
let fan_ratio: f64 = if let Some(thermal_zone) = crate::sys::read_thermal_zone(0) {
|
||||
let temperature_ratio = (((thermal_zone as f64)/1000.0) - settings.temperature_bounds.min)
|
||||
/ (settings.temperature_bounds.max - settings.temperature_bounds.min);
|
||||
let mut index = None;
|
||||
for i in (0..settings.curve.len()).rev() {
|
||||
if settings.curve[i].x < temperature_ratio {
|
||||
index = Some(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if settings.interpolate {
|
||||
Self::interpolate_fan(settings, index, temperature_ratio)
|
||||
} else {
|
||||
Self::step_fan(settings, index, temperature_ratio)
|
||||
}
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
let fan_speed: u64 = ((fan_ratio * (settings.fan_bounds.max - settings.fan_bounds.min)) + settings.fan_bounds.min) as _;
|
||||
if let Err(e) = crate::sys::write_fan_target(fan_speed) {
|
||||
log::error!("runtime failed to write to fan target file: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
fn interpolate_fan(settings: &Settings, index: Option<usize>, t_ratio: f64) -> f64 {
|
||||
/*
|
||||
curve = self.settings["curve"]
|
||||
upper_point = {"x": 1.0, "y": 0.0}
|
||||
lower_point = {"x": 0.0, "y": 1.0}
|
||||
if index != -1: # guaranteed to not be empty
|
||||
lower_point = curve[index]
|
||||
if index != len(curve) - 1:
|
||||
upper_point = curve[index+1]
|
||||
#logging.debug(f"lower_point: {lower_point}, upper_point: {upper_point}")
|
||||
upper_y = 1-upper_point["y"]
|
||||
lower_y = 1-lower_point["y"]
|
||||
slope_m = (upper_y - lower_y) / (upper_point["x"] - lower_point["x"])
|
||||
y_intercept_b = lower_y - (slope_m * lower_point["x"])
|
||||
logging.debug(f"interpolation: y = {slope_m}x + {y_intercept_b}")
|
||||
return (slope_m * temperature_ratio) + y_intercept_b
|
||||
*/
|
||||
let (upper, lower) = if let Some(i) = index {
|
||||
(if i != settings.curve.len() - 1 {
|
||||
settings.curve[i+1].clone()
|
||||
} else {
|
||||
GraphPoint{x: 1.0, y: 1.0}
|
||||
},
|
||||
settings.curve[i].clone())
|
||||
} else {
|
||||
(if settings.curve.is_empty() {
|
||||
GraphPoint{x: 1.0, y: 1.0}
|
||||
} else {
|
||||
settings.curve[0].clone()
|
||||
},
|
||||
GraphPoint{x: 0.0, y: 0.0})
|
||||
};
|
||||
let slope_m = (upper.y - lower.y) / (upper.x - lower.x);
|
||||
let y_intercept_b = lower.y - (slope_m * lower.x);
|
||||
log::debug!("interpolation: y = {}x + {} (between {:?} and {:?})", slope_m, y_intercept_b, upper, lower);
|
||||
(slope_m * t_ratio) + y_intercept_b
|
||||
}
|
||||
|
||||
fn step_fan(settings: &Settings, index: Option<usize>, _t_ratio: f64) -> f64 {
|
||||
/*
|
||||
curve = self.settings["curve"]
|
||||
if index != -1:
|
||||
return 1 - curve[index]["y"]
|
||||
else:
|
||||
if len(curve) == 0:
|
||||
return 1
|
||||
else:
|
||||
return 0.5
|
||||
*/
|
||||
// step fan, what are you doing?
|
||||
if let Some(index) = index {
|
||||
settings.curve[index].y
|
||||
} else {
|
||||
if settings.curve.is_empty() {
|
||||
1.0
|
||||
} else {
|
||||
0.5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn enforce_jupiter_status(enabled: bool) {
|
||||
// enabled refers to whether this plugin's functionality is enabled,
|
||||
// not the jupiter fan control service
|
||||
let service_status = Self::detect_jupiter_fan_service();
|
||||
log::debug!("fan control service is enabled? {}", service_status);
|
||||
if enabled == service_status {
|
||||
// do not run Valve's fan service along with Fantastic, since they fight
|
||||
if enabled {
|
||||
Self::stop_fan_service();
|
||||
} else {
|
||||
Self::start_fan_service();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn detect_jupiter_fan_service() -> bool {
|
||||
match std::process::Command::new("systemctl")
|
||||
.args(["is-active", VALVE_FAN_SERVICE])
|
||||
.output() {
|
||||
Ok(cmd) => String::from_utf8_lossy(&cmd.stdout).trim() == "active",
|
||||
Err(e) => {
|
||||
log::error!("`systemctl is-active {}` err: {}", VALVE_FAN_SERVICE, e);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn start_fan_service() {
|
||||
match std::process::Command::new("systemctl")
|
||||
.args(["start", VALVE_FAN_SERVICE])
|
||||
.output() {
|
||||
Err(e) => log::error!("`systemctl start {}` err: {}", VALVE_FAN_SERVICE, e),
|
||||
Ok(out) => log::debug!("started `{}`:\nstdout:{}\nstderr:{}", VALVE_FAN_SERVICE, String::from_utf8_lossy(&out.stdout), String::from_utf8_lossy(&out.stderr)),
|
||||
}
|
||||
}
|
||||
|
||||
fn stop_fan_service() {
|
||||
match std::process::Command::new("systemctl")
|
||||
.args(["stop", VALVE_FAN_SERVICE])
|
||||
.output() {
|
||||
Err(e) => log::error!("`systemctl stop {}` err: {}", VALVE_FAN_SERVICE, e),
|
||||
Ok(out) => log::debug!("stopped `{}`:\nstdout:{}\nstderr:{}", VALVE_FAN_SERVICE, String::from_utf8_lossy(&out.stdout), String::from_utf8_lossy(&out.stderr)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn settings_path<P: AsRef<std::path::Path>>(home: P) -> std::path::PathBuf {
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::default::Default;
|
|||
use std::convert::{Into, From};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use super::json::{SettingsJson, GraphPointJson, BoundsJson};
|
||||
use super::json::{SettingsJson, GraphPointJson};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Settings {
|
||||
|
@ -10,8 +10,6 @@ pub struct Settings {
|
|||
pub enable: bool,
|
||||
pub interpolate: bool,
|
||||
pub curve: Vec<GraphPoint>,
|
||||
pub fan_bounds: Bounds<f64>,
|
||||
pub temperature_bounds: Bounds<f64>,
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
|
@ -28,42 +26,18 @@ impl From<SettingsJson> for Settings {
|
|||
enable: other.enable,
|
||||
interpolate: other.interpolate,
|
||||
curve: other.curve.drain(..).map(|x| GraphPoint::from_json(x, other.version)).collect(),
|
||||
fan_bounds: Bounds {
|
||||
min: 1.0,
|
||||
max: 7000.0,
|
||||
},
|
||||
temperature_bounds: Bounds {
|
||||
min: 0.0,
|
||||
max: 100.0,
|
||||
},
|
||||
},
|
||||
1 => Self {
|
||||
version: 1,
|
||||
enable: other.enable,
|
||||
interpolate: other.interpolate,
|
||||
curve: other.curve.drain(..).map(|x| GraphPoint::from_json(x, other.version)).collect(),
|
||||
fan_bounds: other.fan_bounds.map(|x| Bounds::<f64>::from_json(x, other.version)).unwrap_or(Bounds {
|
||||
min: 1.0,
|
||||
max: 7000.0,
|
||||
}),
|
||||
temperature_bounds: other.temperature_bounds.map(|x| Bounds::<f64>::from_json(x, other.version)).unwrap_or(Bounds {
|
||||
min: 0.0,
|
||||
max: 100.0,
|
||||
}),
|
||||
},
|
||||
_ => Self {
|
||||
version: 1,
|
||||
enable: other.enable,
|
||||
interpolate: other.interpolate,
|
||||
curve: other.curve.drain(..).map(|x| GraphPoint::from_json(x, other.version)).collect(),
|
||||
fan_bounds: Bounds {
|
||||
min: 1.0,
|
||||
max: 7000.0,
|
||||
},
|
||||
temperature_bounds: Bounds {
|
||||
min: 0.0,
|
||||
max: 100.0,
|
||||
},
|
||||
}
|
||||
};
|
||||
result.sort_curve();
|
||||
|
@ -79,8 +53,6 @@ impl Into<SettingsJson> for Settings {
|
|||
enable: self.enable,
|
||||
interpolate: self.interpolate,
|
||||
curve: self.curve.drain(..).map(|x| x.into()).collect(),
|
||||
fan_bounds: Some(self.fan_bounds.into()),
|
||||
temperature_bounds: Some(self.temperature_bounds.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -121,72 +93,6 @@ impl Into<GraphPointJson> for GraphPoint {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Bounds<T: core::fmt::Debug + Clone> {
|
||||
pub min: T,
|
||||
pub max: T,
|
||||
}
|
||||
|
||||
/*impl Bounds<usize> {
|
||||
#[inline]
|
||||
pub fn from_json(other: BoundsJson, version: u64) -> Self {
|
||||
match version {
|
||||
0 => Self {
|
||||
min: other.min as _,
|
||||
max: other.max as _,
|
||||
},
|
||||
1 => Self {
|
||||
min: other.min as _,
|
||||
max: other.max as _,
|
||||
},
|
||||
_ => Self {
|
||||
min: other.min as _,
|
||||
max: other.max as _,
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
impl Bounds<f64> {
|
||||
#[inline]
|
||||
pub fn from_json(other: BoundsJson, version: u64) -> Self {
|
||||
match version {
|
||||
0 => Self {
|
||||
min: other.min,
|
||||
max: other.max,
|
||||
},
|
||||
1 => Self {
|
||||
min: other.min,
|
||||
max: other.max,
|
||||
},
|
||||
_ => Self {
|
||||
min: other.min,
|
||||
max: other.max,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*impl Into<BoundsJson> for Bounds<usize> {
|
||||
#[inline]
|
||||
fn into(self) -> BoundsJson {
|
||||
BoundsJson {
|
||||
min: self.min as _,
|
||||
max: self.max as _,
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
impl Into<BoundsJson> for Bounds<f64> {
|
||||
#[inline]
|
||||
fn into(self) -> BoundsJson {
|
||||
BoundsJson {
|
||||
min: self.min,
|
||||
max: self.max,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct State {
|
||||
pub home: PathBuf,
|
||||
|
|
|
@ -11,8 +11,6 @@ pub struct SettingsJson {
|
|||
pub enable: bool,
|
||||
pub interpolate: bool,
|
||||
pub curve: Vec<GraphPointJson>,
|
||||
pub fan_bounds: Option<BoundsJson>,
|
||||
pub temperature_bounds: Option<BoundsJson>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
|
@ -21,12 +19,6 @@ pub struct GraphPointJson {
|
|||
pub y: f64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct BoundsJson {
|
||||
pub min: f64,
|
||||
pub max: f64,
|
||||
}
|
||||
|
||||
impl Default for SettingsJson {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
|
@ -34,14 +26,6 @@ impl Default for SettingsJson {
|
|||
enable: false,
|
||||
interpolate: true,
|
||||
curve: Vec::new(),
|
||||
fan_bounds: Some(BoundsJson {
|
||||
min: 0.0,
|
||||
max: 7000.0,
|
||||
}),
|
||||
temperature_bounds: Some(BoundsJson {
|
||||
min: 0.0,
|
||||
max: 100.0,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
mod api;
|
||||
mod adapters;
|
||||
mod control;
|
||||
mod datastructs;
|
||||
mod json;
|
||||
|
@ -6,7 +7,13 @@ mod sys;
|
|||
|
||||
use simplelog::{WriteLogger, LevelFilter};
|
||||
|
||||
use usdpl_back::Instance;
|
||||
#[allow(missing_docs)]
|
||||
#[allow(dead_code)]
|
||||
pub mod services {
|
||||
include!(concat!(env!("OUT_DIR"), "/mod.rs"));
|
||||
}
|
||||
|
||||
use services::fantastic::FanServer;
|
||||
|
||||
const PORT: u16 = 44444;
|
||||
|
||||
|
@ -20,9 +27,29 @@ fn main() -> Result<(), ()> {
|
|||
|
||||
log::info!("Starting back-end ({} v{})", api::NAME, api::VERSION);
|
||||
println!("Starting back-end ({} v{})", api::NAME, api::VERSION);
|
||||
let runtime = control::ControlRuntime::new();
|
||||
runtime.run();
|
||||
Instance::new(PORT)
|
||||
usdpl_back::Server::new(PORT)
|
||||
.register(FanServer::new(
|
||||
api::FanService::new(control::ControlRuntime::new_boxed(
|
||||
adapters::fans::SteamDeckFan::maybe_find()
|
||||
.map(|f| Box::new(f) as Box<dyn adapters::FanAdapter>)
|
||||
.or_else(
|
||||
|| adapters::fans::RogAllyFan::maybe_find()
|
||||
.map(|f| Box::new(f) as Box<dyn adapters::FanAdapter>)
|
||||
)
|
||||
.unwrap_or_else(|| Box::new(adapters::fans::DevModeFan)),
|
||||
adapters::fans::SteamDeckFan::maybe_find_thermal_zone()
|
||||
.map(|t| Box::new(adapters::sensors::ThermalZoneSensor::new(t)) as Box<dyn adapters::SensorAdapter>)
|
||||
.or_else(
|
||||
|| adapters::sensors::ThermalZoneSensor::new_if_exists("/sys/thermal/thermal_zone0")
|
||||
.map(|t| Box::new(t) as Box<dyn adapters::SensorAdapter>)
|
||||
)
|
||||
.unwrap_or_else(|| Box::new(adapters::sensors::DevModeSensor))
|
||||
))
|
||||
))
|
||||
.run_blocking()
|
||||
.unwrap();
|
||||
Ok(())
|
||||
/*Instance::new(PORT)
|
||||
.register("echo", api::echo)
|
||||
.register("hello", api::hello)
|
||||
.register("version", api::version)
|
||||
|
@ -36,7 +63,7 @@ fn main() -> Result<(), ()> {
|
|||
.register("get_curve", api::get_curve_gen(&runtime))
|
||||
.register("add_curve_point", api::add_curve_point_gen(&runtime))
|
||||
.register("remove_curve_point", api::remove_curve_point_gen(&runtime))
|
||||
.run_blocking()
|
||||
.run_blocking()*/
|
||||
//Ok(())
|
||||
//println!("Hello, world!");
|
||||
}
|
||||
|
|
|
@ -1,19 +1 @@
|
|||
use usdpl_back::api::files::*;
|
||||
|
||||
const HWMON_INDEX: usize = 5;
|
||||
|
||||
pub fn read_fan() -> Option<u64> {
|
||||
read_single(format!("/sys/class/hwmon/hwmon{}/fan1_input", HWMON_INDEX)).ok()
|
||||
}
|
||||
|
||||
pub fn read_thermal_zone(index: u8) -> Option<u64> {
|
||||
read_single(format!("/sys/class/thermal/thermal_zone{}/temp", index)).ok()
|
||||
}
|
||||
|
||||
pub fn write_fan_recalc(enabled: bool) -> Result<(), std::io::Error> {
|
||||
write_single(format!("/sys/class/hwmon/hwmon{}/recalculate", HWMON_INDEX), enabled as u8)
|
||||
}
|
||||
|
||||
pub fn write_fan_target(rpm: u64) -> Result<(), std::io::Error> {
|
||||
write_single(format!("/sys/class/hwmon/hwmon{}/fan1_target", HWMON_INDEX), rpm)
|
||||
}
|
||||
pub const SYSFS_ROOT: &str = "/";
|
||||
|
|
12
package.json
12
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "Fantastic",
|
||||
"version": "0.4.0",
|
||||
"version": "0.6.0-alpha1",
|
||||
"description": "A template to quickly create decky plugins from scratch, based on TypeScript and webpack",
|
||||
"scripts": {
|
||||
"build": "shx rm -rf dist && rollup -c",
|
||||
|
@ -32,17 +32,17 @@
|
|||
"@rollup/plugin-replace": "^4.0.0",
|
||||
"@rollup/plugin-typescript": "^8.5.0",
|
||||
"@types/react": "16.14.0",
|
||||
"@types/webpack": "^5.28.0",
|
||||
"@types/webpack": "^5.28.5",
|
||||
"rollup": "^2.79.1",
|
||||
"rollup-plugin-import-assets": "^1.1.1",
|
||||
"shx": "^0.3.4",
|
||||
"tslib": "^2.5.0",
|
||||
"tslib": "^2.6.2",
|
||||
"typescript": "^4.9.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"decky-frontend-lib": "~3.19.1",
|
||||
"react-icons": "^4.7.1",
|
||||
"usdpl-front": "file:src/usdpl"
|
||||
"decky-frontend-lib": "^3.25.0",
|
||||
"fantastic-wasm": "file:src/rust/pkg",
|
||||
"react-icons": "^4.12.0"
|
||||
},
|
||||
"pnpm": {
|
||||
"peerDependencyRules": {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
{
|
||||
"name": "Fantastic",
|
||||
"author": "NGnius",
|
||||
"flags": ["root", "debug"],
|
||||
"flags": ["root", "_debug", "global-dfl"],
|
||||
"publish": {
|
||||
"discord_id": "106537989684887552",
|
||||
"description": "Fan controls",
|
||||
"tags": [ "utility", "fan-control" ],
|
||||
"tags": [ "utility", "fan-control", "root" ],
|
||||
"image": "https://raw.githubusercontent.com/NGnius/Fantastic/main/assets/thumbnail.png"
|
||||
}
|
||||
}
|
||||
|
|
669
pnpm-lock.yaml
669
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
@ -24,12 +24,13 @@ export default defineConfig({
|
|||
})
|
||||
],
|
||||
context: 'window',
|
||||
external: ['react', 'react-dom'],
|
||||
external: ['react', 'react-dom', 'decky-frontend-lib'],
|
||||
output: {
|
||||
file: 'dist/index.js',
|
||||
globals: {
|
||||
react: 'SP_REACT',
|
||||
'react-dom': 'SP_REACTDOM',
|
||||
'decky-frontend-lib': 'DFL',
|
||||
},
|
||||
format: 'iife',
|
||||
exports: 'default',
|
||||
|
|
|
@ -1,7 +1,15 @@
|
|||
import {init_usdpl, target_usdpl, init_embedded, call_backend} from "usdpl-front";
|
||||
//import {init_usdpl, target_usdpl, init_embedded, call_backend} from "usdpl-front";
|
||||
|
||||
import { init_embedded, target_usdpl } from "fantastic-wasm";
|
||||
import { Fan } from "fantastic-wasm";
|
||||
|
||||
//@ts-ignore
|
||||
//const Fan = {};
|
||||
|
||||
const USDPL_PORT: number = 44444;
|
||||
|
||||
var FAN_CLIENT: Fan | undefined = undefined;
|
||||
|
||||
// Utility
|
||||
|
||||
export function resolve(promise: Promise<any>, setter: any) {
|
||||
|
@ -26,7 +34,7 @@ export function execute(promise: Promise<any[]>) {
|
|||
export async function initBackend() {
|
||||
// init usdpl
|
||||
await init_embedded();
|
||||
init_usdpl(USDPL_PORT);
|
||||
FAN_CLIENT = new Fan(USDPL_PORT);
|
||||
console.log("FANTASTIC: USDPL started for framework: " + target_usdpl());
|
||||
//setReady(true);
|
||||
}
|
||||
|
@ -34,45 +42,62 @@ export async function initBackend() {
|
|||
// Back-end functions
|
||||
|
||||
export async function setEnabled(value: boolean): Promise<boolean> {
|
||||
return (await call_backend("set_enable", [value]))[0];
|
||||
return (await FAN_CLIENT!.set_enable(value))?? value;
|
||||
//return (await call_backend("set_enable", [value]))[0];
|
||||
}
|
||||
|
||||
export async function getEnabled(): Promise<boolean> {
|
||||
return (await call_backend("get_enable", []))[0];
|
||||
return (await FAN_CLIENT!.get_enable(true)) ?? false;
|
||||
}
|
||||
|
||||
export async function setInterpolate(value: boolean): Promise<boolean> {
|
||||
return (await call_backend("set_interpolate", [value]))[0];
|
||||
return (await FAN_CLIENT!.set_interpolate(value)) ?? value;
|
||||
//return (await call_backend("set_interpolate", [value]))[0];
|
||||
}
|
||||
|
||||
export async function getInterpolate(): Promise<boolean> {
|
||||
return (await call_backend("get_interpolate", []))[0];
|
||||
return (await FAN_CLIENT!.get_interpolate(true)) ?? false;
|
||||
//return (await call_backend("get_interpolate", []))[0];
|
||||
}
|
||||
|
||||
export async function getVersion(): Promise<string> {
|
||||
return (await call_backend("version", []))[0];
|
||||
return (await FAN_CLIENT!.version_str(true)) ?? "version";
|
||||
//return (await call_backend("version", []))[0];
|
||||
}
|
||||
|
||||
export async function getName(): Promise<string> {
|
||||
return (await call_backend("name", []))[0];
|
||||
return (await FAN_CLIENT!.name(true))?? "broken";
|
||||
//return (await call_backend("name", []))[0];
|
||||
}
|
||||
|
||||
export async function getCurve(): Promise<{"x": number, "y": number}[]> {
|
||||
return (await call_backend("get_curve", []))[0];
|
||||
var x_s = (await FAN_CLIENT!.get_curve_x(true))?? [];
|
||||
var y_s = (await FAN_CLIENT!.get_curve_y(true))?? [];
|
||||
let result: {"x": number, "y": number}[] = [];
|
||||
for (let i = 0; i < x_s.length && i < y_s.length; i++) {
|
||||
result.push({
|
||||
x: x_s[i],
|
||||
y: y_s[i],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function addCurvePoint(point: {"x": number, "y": number}): Promise<{"x": number, "y": number}[]> {
|
||||
return (await call_backend("add_curve_point", [point]))[0];
|
||||
await FAN_CLIENT!.add_curve_point(point.x, point.y);
|
||||
return getCurve();
|
||||
}
|
||||
|
||||
export async function removeCurvePoint(index: number): Promise<{"x": number, "y": number}[]> {
|
||||
return (await call_backend("remove_curve_point", [index]))[0];
|
||||
await FAN_CLIENT!.remove_curve_point(index);
|
||||
return getCurve();
|
||||
//return (await call_backend("remove_curve_point", [index]))[0];
|
||||
}
|
||||
|
||||
export async function getFanRpm(): Promise<number> {
|
||||
return (await call_backend("get_fan_rpm", []))[0];
|
||||
export async function getFanRpm(callback: (rpm: number) => void): Promise<void> {
|
||||
return (await FAN_CLIENT!.get_fan_rpm(true, callback));
|
||||
}
|
||||
|
||||
export async function getTemperature(): Promise<number> {
|
||||
return (await call_backend("get_temperature", []))[0];
|
||||
export async function getTemperature(callback: (temp: number) => void): Promise<void> {
|
||||
return (await FAN_CLIENT!.get_temperature(true, callback));
|
||||
}
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
import {
|
||||
definePlugin,
|
||||
DialogButton,
|
||||
PanelSection,
|
||||
PanelSectionRow,
|
||||
Field,
|
||||
ServerAPI,
|
||||
ToggleField,
|
||||
staticClasses,
|
||||
Navigation,
|
||||
} from "decky-frontend-lib";
|
||||
import { VFC, useState } from "react";
|
||||
import { FaFan } from "react-icons/fa";
|
||||
import { SiOnlyfans } from "react-icons/si";
|
||||
|
||||
import { version_usdpl } from "fantastic-wasm";
|
||||
|
||||
import * as backend from "./backend";
|
||||
import {Canvas} from "./canvas";
|
||||
|
||||
|
@ -22,10 +24,15 @@ var usdplReady: boolean = false;
|
|||
|
||||
var name: string = "";
|
||||
var version: string = "";
|
||||
var egg = 0;
|
||||
|
||||
var curve_backup: {x: number, y: number}[] = [];
|
||||
|
||||
var tempCache: number = -1337;
|
||||
var setTemperature_display = (_: number) => {};
|
||||
|
||||
var fanRpmCache: number = -273;
|
||||
var setFanRpm_display = (_: number) => {};
|
||||
|
||||
const Content: VFC<{ serverAPI: ServerAPI }> = ({serverAPI}) => {
|
||||
// const [result, setResult] = useState<number | undefined>();
|
||||
|
||||
|
@ -53,21 +60,30 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({serverAPI}) => {
|
|||
curve_backup = value;
|
||||
}
|
||||
|
||||
const [temperatureGlobal, setTemperature] = useState<number>(-273.15);
|
||||
const [fanRpmGlobal, setFanRpm] = useState<number>(-1337);
|
||||
const [temperatureGlobal, setTemperature] = useState<number>(tempCache);
|
||||
const [fanRpmGlobal, setFanRpm] = useState<number>(fanRpmCache);
|
||||
setTemperature_display = (x) => {
|
||||
setTemperature(x);
|
||||
tempCache = x;
|
||||
};
|
||||
setFanRpm_display = (x) => {
|
||||
setFanRpm(x);
|
||||
fanRpmCache = x;
|
||||
};
|
||||
|
||||
function setEnable(enable: boolean) {
|
||||
setEnableInternal(enable);
|
||||
}
|
||||
|
||||
function onClickCanvas(e: any) {
|
||||
//console.log("canvas click", e);
|
||||
//console.log("[FANTASTIC] canvas click", e);
|
||||
const realEvent: any = e.nativeEvent;
|
||||
//console.log("Canvas click @ (" + realEvent.layerX.toString() + ", " + realEvent.layerY.toString() + ")");
|
||||
const target: any = e.currentTarget;
|
||||
//console.log("Target dimensions " + target.width.toString() + "x" + target.height.toString());
|
||||
const clickX = realEvent.layerX;
|
||||
const clickY = realEvent.layerY;
|
||||
//console.log("[FANTASTIC] Target dimensions " + target.width.toString() + "x" + target.height.toString());
|
||||
var clickX = realEvent.offsetX;
|
||||
var clickY = realEvent.offsetY;
|
||||
//console.debug("[FANTASTIC] curve click:", clickX, clickY);
|
||||
for (let i = 0; i < curveGlobal.length; i++) {
|
||||
const curvePoint = curveGlobal[i];
|
||||
const pointX = curvePoint.x * target.width;
|
||||
|
@ -84,7 +100,8 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({serverAPI}) => {
|
|||
}
|
||||
}
|
||||
//console.log("Adding new point");
|
||||
backend.resolve(backend.addCurvePoint({x: clickX / target.width, y: 1 - (clickY / target.height)}), setCurve);
|
||||
const curvePoint = {x: clickX / target.width, y: 1 - (clickY / target.height)};
|
||||
backend.resolve(backend.addCurvePoint(curvePoint), setCurve);
|
||||
}
|
||||
|
||||
function drawCanvas(ctx: any, frameCount: number): void {
|
||||
|
@ -205,23 +222,27 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({serverAPI}) => {
|
|||
backend.resolve(backend.getEnabled(), setEnable);
|
||||
backend.resolve(backend.getInterpolate(), setInterpol);
|
||||
backend.resolve(backend.getCurve(), setCurve);
|
||||
backend.resolve(backend.getTemperature(), setTemperature);
|
||||
backend.resolve(backend.getFanRpm(), setFanRpm);
|
||||
backend.resolve(backend.getTemperature(setTemperature_display), (_: any) => {});
|
||||
backend.resolve(backend.getFanRpm(setFanRpm_display), (_: any) => {});
|
||||
|
||||
if (periodicHook != null) {
|
||||
clearInterval(periodicHook);
|
||||
}
|
||||
|
||||
periodicHook = setInterval(function() {
|
||||
/*periodicHook = setInterval(function() {
|
||||
backend.resolve(backend.getTemperature(), setTemperature);
|
||||
backend.resolve(backend.getFanRpm(), setFanRpm);
|
||||
}, 1000);
|
||||
}, 1000);*/
|
||||
}
|
||||
|
||||
if (!usdplReady) {
|
||||
return (
|
||||
<PanelSection>
|
||||
</PanelSection>
|
||||
<PanelSectionRow>
|
||||
<Field
|
||||
label="Loading...">
|
||||
If you can read this, something probably went wrong :(
|
||||
</Field>
|
||||
</PanelSectionRow>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -285,37 +306,32 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({serverAPI}) => {
|
|||
<PanelSectionRow>
|
||||
<Field
|
||||
label={name}
|
||||
onClick={()=> { egg++; }}>
|
||||
{egg % 10 == 9 ? "by NGnius" : "v" + version}
|
||||
onClick={()=> { Navigation.NavigateToExternalWeb("https://git.ngni.us/NG-SD-Plugins/Fantastic/releases"); }}>
|
||||
{"v" + version}
|
||||
</Field>
|
||||
</PanelSectionRow>
|
||||
{ (version?.includes("alpha") || version?.includes("beta")) && <PanelSectionRow>
|
||||
<Field
|
||||
label="USDPL"
|
||||
onClick={()=> { Navigation.NavigateToExternalWeb("https://git.ngni.us/NG-SD-Plugins/usdpl-rs"); }}>
|
||||
v{version_usdpl()}
|
||||
</Field>
|
||||
</PanelSectionRow>}
|
||||
</PanelSection>
|
||||
);
|
||||
};
|
||||
|
||||
const DeckyPluginRouterTest: VFC = () => {
|
||||
return (
|
||||
<div style={{ marginTop: "50px", color: "white" }}>
|
||||
Hello World!
|
||||
<DialogButton onClick={() => {}}>
|
||||
Go to Store
|
||||
</DialogButton>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
(async function(){
|
||||
if (!usdplReady) {
|
||||
await backend.initBackend();
|
||||
usdplReady = true;
|
||||
backend.getEnabled();
|
||||
name = await backend.getName();
|
||||
version = await backend.getVersion();
|
||||
}
|
||||
})();
|
||||
|
||||
export default definePlugin((serverApi: ServerAPI) => {
|
||||
serverApi.routerHook.addRoute("/decky-plugin-test", DeckyPluginRouterTest, {
|
||||
exact: true,
|
||||
});
|
||||
|
||||
(async function(){
|
||||
await backend.initBackend();
|
||||
usdplReady = true;
|
||||
backend.getEnabled();
|
||||
name = await backend.getName();
|
||||
version = await backend.getVersion();
|
||||
})();
|
||||
|
||||
let ico = <FaFan />;
|
||||
let now = new Date();
|
||||
|
@ -329,7 +345,6 @@ export default definePlugin((serverApi: ServerAPI) => {
|
|||
icon: ico,
|
||||
onDismount() {
|
||||
clearInterval(periodicHook!);
|
||||
serverApi.routerHook.removeRoute("/decky-plugin-test");
|
||||
},
|
||||
};
|
||||
});
|
||||
|
|
981
src/rust/Cargo.lock
generated
Normal file
981
src/rust/Cargo.lock
generated
Normal file
|
@ -0,0 +1,981 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c378d78423fdad8089616f827526ee33c19f2fddbd5de1629152c9593ba4783"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.75"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||
|
||||
[[package]]
|
||||
name = "beef"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "console_error_panic_hook"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd"
|
||||
dependencies = [
|
||||
"errno-dragonfly",
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno-dragonfly"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fantastic-wasm"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"nrpc",
|
||||
"prost",
|
||||
"usdpl-build",
|
||||
"usdpl-front",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764"
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gloo-net"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ac9e8288ae2c632fa9f8657ac70bfe38a1530f345282d7ba66a1f70b72b7dc4"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"gloo-utils",
|
||||
"http",
|
||||
"js-sys",
|
||||
"pin-project",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gloo-utils"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.147"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||
|
||||
[[package]]
|
||||
name = "logos"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c000ca4d908ff18ac99b93a062cb8958d331c3220719c52e77cb19cc6ac5d2c1"
|
||||
dependencies = [
|
||||
"logos-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "logos-codegen"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc487311295e0002e452025d6b580b77bb17286de87b57138f3b5db711cded68"
|
||||
dependencies = [
|
||||
"beef",
|
||||
"fnv",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex-syntax 0.6.29",
|
||||
"syn 2.0.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "logos-derive"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbfc0d229f1f42d790440136d941afd806bc9e949e2bcb8faa813b0f00d1267e"
|
||||
dependencies = [
|
||||
"logos-codegen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5486aed0026218e61b8a01d5fbd5a0a134649abb71a0e53b7bc088529dced86e"
|
||||
|
||||
[[package]]
|
||||
name = "miette"
|
||||
version = "5.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59bb584eaeeab6bd0226ccf3509a69d7936d148cf3d036ad350abe35e8c6856e"
|
||||
dependencies = [
|
||||
"miette-derive",
|
||||
"once_cell",
|
||||
"thiserror",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miette-derive"
|
||||
version = "5.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "multimap"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a"
|
||||
|
||||
[[package]]
|
||||
name = "nrpc"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures",
|
||||
"prost",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nrpc-build"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"nrpc",
|
||||
"prettyplease 0.2.12",
|
||||
"proc-macro2",
|
||||
"prost-build",
|
||||
"prost-types",
|
||||
"protox",
|
||||
"quote",
|
||||
"syn 2.0.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9"
|
||||
dependencies = [
|
||||
"fixedbitset",
|
||||
"indexmap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422"
|
||||
dependencies = [
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.1.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn 2.0.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost"
|
||||
version = "0.11.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"prost-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost-build"
|
||||
version = "0.11.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"heck",
|
||||
"itertools",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"multimap",
|
||||
"petgraph",
|
||||
"prettyplease 0.1.25",
|
||||
"prost",
|
||||
"prost-types",
|
||||
"regex",
|
||||
"syn 1.0.109",
|
||||
"tempfile",
|
||||
"which",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost-derive"
|
||||
version = "0.11.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost-reflect"
|
||||
version = "0.11.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b823de344848e011658ac981009100818b322421676740546f8b52ed5249428"
|
||||
dependencies = [
|
||||
"logos",
|
||||
"miette",
|
||||
"once_cell",
|
||||
"prost",
|
||||
"prost-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost-types"
|
||||
version = "0.11.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13"
|
||||
dependencies = [
|
||||
"prost",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "protox"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24022a7eb88547eaba87a1db7954c9c4cb4a143565c4e8f2b7f3e76eed63db2d"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"miette",
|
||||
"prost",
|
||||
"prost-reflect",
|
||||
"prost-types",
|
||||
"protox-parse",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "protox-parse"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712a2a651fa4466e67df6c967df5d7fc6cbffac89afc7b834f97ec49846c9c11"
|
||||
dependencies = [
|
||||
"logos",
|
||||
"miette",
|
||||
"prost-types",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12de2eff854e5fa4b1295edd650e227e9d8fb0c9e90b12e7f36d6a6811791a29"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax 0.7.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49530408a136e16e5b486e883fbb6ba058e8e4e8ae6621a77b048b314336e629"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax 0.7.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0c3dde1fc030af041adc40e79c0e7fbcf431dd24870053d187d7c66e4b87453"
|
||||
dependencies = [
|
||||
"bitflags 2.4.0",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.188"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.188"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.105"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"redox_syscall",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||
|
||||
[[package]]
|
||||
name = "usdpl-build"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"nrpc-build",
|
||||
"prettyplease 0.2.12",
|
||||
"proc-macro2",
|
||||
"prost-build",
|
||||
"prost-types",
|
||||
"quote",
|
||||
"syn 2.0.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "usdpl-core"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"base64",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "usdpl-front"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"futures",
|
||||
"futures-channel",
|
||||
"gloo-net",
|
||||
"js-sys",
|
||||
"log",
|
||||
"nrpc",
|
||||
"prost",
|
||||
"usdpl-core",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.87"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.87"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.29",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.87"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.87"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.29",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.87"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "4.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269"
|
||||
dependencies = [
|
||||
"either",
|
||||
"libc",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
33
src/rust/Cargo.toml
Normal file
33
src/rust/Cargo.toml
Normal file
|
@ -0,0 +1,33 @@
|
|||
[package]
|
||||
name = "fantastic-wasm"
|
||||
version = "0.5.0"
|
||||
edition = "2021"
|
||||
authors = ["NGnius (Graham) <ngniusness@gmail.com>"]
|
||||
description = "Frontend bindings for fan control functionality"
|
||||
license = "GPL-3.0-only"
|
||||
repository = "https://git.ngni.us/NG-SD-Plugins/Fantastic"
|
||||
keywords = ["utility", "fan-control", "root", "decky"]
|
||||
readme = "../../README.md"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
usdpl-front = { version = "1.0", path = "../../../usdpl-rs/usdpl-front" }
|
||||
nrpc = { version = "1.0", path = "../../../nRPC/nrpc", default-features = false }
|
||||
prost = "0.11"
|
||||
|
||||
[build-dependencies]
|
||||
usdpl-build = { version = "1.0", path = "../../../usdpl-rs/usdpl-build" }
|
||||
|
||||
[features]
|
||||
debug = ["usdpl-front/debug"]
|
||||
decky = ["usdpl-front/decky"]
|
||||
|
||||
[profile.release]
|
||||
# Tell `rustc` to optimize for small code size.
|
||||
opt-level = "s"
|
||||
debug = false
|
||||
strip = true
|
||||
lto = true
|
||||
codegen-units = 4
|
15
src/rust/build.rs
Normal file
15
src/rust/build.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
fn main() {
|
||||
println!("CWD: {}", std::env::current_dir().unwrap().display());
|
||||
usdpl_build::front::build(
|
||||
[format!(
|
||||
"{}/../../backend-rs/protos/fantastic.proto",
|
||||
std::env::current_dir().unwrap().display()
|
||||
)]
|
||||
.into_iter(),
|
||||
[format!(
|
||||
"{}/../../backend-rs/protos/",
|
||||
std::env::current_dir().unwrap().display()
|
||||
)]
|
||||
.into_iter(),
|
||||
)
|
||||
}
|
19
src/rust/build.sh
Executable file
19
src/rust/build.sh
Executable file
|
@ -0,0 +1,19 @@
|
|||
#!/bin/bash
|
||||
if [ -n "$1" ]; then
|
||||
if [ "$1" == "--help" ]; then
|
||||
echo "Usage:
|
||||
$0 [decky|crankshaft|<nothing>]"
|
||||
exit 0
|
||||
elif [ "$1" == "decky" ]; then
|
||||
echo "Building WASM module for decky framework"
|
||||
RUSTFLAGS="--cfg aes_compact" wasm-pack build --target web --features decky,$2
|
||||
else
|
||||
echo "Unsupported plugin framework \`$1\`"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "WARNING: Building for any plugin framework, which may not work for every framework"
|
||||
RUSTFLAGS="--cfg aes_compact" wasm-pack build --target web --features debug,$2
|
||||
fi
|
||||
|
||||
python3 ./scripts/generate_embedded_wasm.py
|
45
src/rust/scripts/generate_embedded_wasm.py
Normal file
45
src/rust/scripts/generate_embedded_wasm.py
Normal file
|
@ -0,0 +1,45 @@
|
|||
import base64
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Embedding WASM into js")
|
||||
# assumption: current working directory (relative to this script) is ../
|
||||
# assumption: release wasm binary at ./pkg/usdpl_bg.wasm
|
||||
with open("./pkg/fantastic_wasm_bg.wasm", mode="rb") as infile:
|
||||
with open("./pkg/fantastic_wasm.js", mode="ab") as outfile:
|
||||
outfile.write("\n\n// USDPL customization\nconst encoded = \"".encode())
|
||||
encoded = base64.b64encode(infile.read())
|
||||
outfile.write(encoded)
|
||||
outfile.write("\";\n\n".encode())
|
||||
outfile.write(
|
||||
"""function asciiToBinary(str) {
|
||||
if (typeof atob === 'function') {
|
||||
return atob(str)
|
||||
} else {
|
||||
return new Buffer(str, 'base64').toString('binary');
|
||||
}
|
||||
}
|
||||
|
||||
function decode() {
|
||||
var binaryString = asciiToBinary(encoded);
|
||||
var bytes = new Uint8Array(binaryString.length);
|
||||
for (var i = 0; i < binaryString.length; i++) {
|
||||
bytes[i] = binaryString.charCodeAt(i);
|
||||
}
|
||||
return (async function() {
|
||||
return new Response(bytes.buffer, {
|
||||
status: 200,
|
||||
statusText: 'OK',
|
||||
headers: {
|
||||
'Content-Type': 'application/wasm'
|
||||
}
|
||||
});
|
||||
})();
|
||||
}
|
||||
|
||||
export function init_embedded() {
|
||||
return __wbg_init(decode())
|
||||
}
|
||||
""".encode())
|
||||
with open("./pkg/fantastic_wasm.d.ts", "a") as outfile:
|
||||
outfile.write("\n\n// USDPL customization\nexport function init_embedded();\n")
|
||||
print("Done: Embedded WASM into js")
|
7
src/rust/src/lib.rs
Normal file
7
src/rust/src/lib.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
#[allow(missing_docs)]
|
||||
#[allow(dead_code)]
|
||||
pub mod services {
|
||||
include!(concat!(env!("OUT_DIR"), "/mod.rs"));
|
||||
}
|
||||
|
||||
pub use usdpl_front;
|
|
@ -1,9 +0,0 @@
|
|||
[![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.
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
{
|
||||
"name": "usdpl-front",
|
||||
"collaborators": [
|
||||
"NGnius (Graham) <ngniusness@gmail.com>"
|
||||
],
|
||||
"description": "Universal Steam Deck Plugin Library front-end designed for WASM",
|
||||
"version": "0.10.0",
|
||||
"license": "GPL-3.0-only",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/NGnius/usdpl-rs"
|
||||
},
|
||||
"files": [
|
||||
"usdpl_front_bg.wasm",
|
||||
"usdpl_front.js",
|
||||
"usdpl_front.d.ts"
|
||||
],
|
||||
"module": "usdpl_front.js",
|
||||
"types": "usdpl_front.d.ts",
|
||||
"sideEffects": false
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
git clone https://github.com/NGnius/usdpl-rs usdpl-rs
|
||||
cd usdpl-rs/usdpl-front/
|
||||
|
||||
./build.sh $1 $2
|
||||
|
||||
cd ../..
|
||||
|
||||
cp -f ./usdpl-rs/usdpl-front/pkg/* ./
|
||||
#rm ./.gitignore
|
||||
|
||||
rm -rf ./usdpl-rs
|
105
src/usdpl/usdpl_front.d.ts
vendored
105
src/usdpl/usdpl_front.d.ts
vendored
|
@ -1,105 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Initialize the front-end library
|
||||
* @param {number} port
|
||||
*/
|
||||
export function init_usdpl(port: number): void;
|
||||
/**
|
||||
* Get the targeted plugin framework, or "any" if unknown
|
||||
* @returns {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.
|
||||
* @param {string} name
|
||||
* @param {any[]} parameters
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
export function call_backend(name: string, parameters: any[]): Promise<any>;
|
||||
/**
|
||||
* Initialize translation strings for the front-end
|
||||
* @param {string} locale
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export function init_tr(locale: string): Promise<void>;
|
||||
/**
|
||||
* Translate a phrase, equivalent to tr_n(msg_id, 0)
|
||||
* @param {string} msg_id
|
||||
* @returns {string}
|
||||
*/
|
||||
export function tr(msg_id: string): string;
|
||||
/**
|
||||
* Translate a phrase, retrieving the plural form for `n` items
|
||||
* @param {string} msg_id
|
||||
* @param {number} n
|
||||
* @returns {string}
|
||||
*/
|
||||
export function tr_n(msg_id: string, n: number): string;
|
||||
|
||||
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
|
||||
|
||||
export interface InitOutput {
|
||||
readonly memory: WebAssembly.Memory;
|
||||
readonly init_usdpl: (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 init_tr: (a: number, b: number) => number;
|
||||
readonly tr: (a: number, b: number, c: number) => void;
|
||||
readonly tr_n: (a: number, b: number, c: number, d: number) => void;
|
||||
readonly __wbindgen_export_0: (a: number) => number;
|
||||
readonly __wbindgen_export_1: (a: number, b: number, c: number) => number;
|
||||
readonly __wbindgen_export_2: WebAssembly.Table;
|
||||
readonly __wbindgen_export_3: (a: number, b: number, c: number) => void;
|
||||
readonly __wbindgen_add_to_stack_pointer: (a: number) => number;
|
||||
readonly __wbindgen_export_4: (a: number, b: number) => void;
|
||||
readonly __wbindgen_export_5: (a: number) => void;
|
||||
readonly __wbindgen_export_6: (a: number, b: number, c: number, d: number) => void;
|
||||
}
|
||||
|
||||
export type SyncInitInput = BufferSource | WebAssembly.Module;
|
||||
/**
|
||||
* Instantiates the given `module`, which can either be bytes or
|
||||
* a precompiled `WebAssembly.Module`.
|
||||
*
|
||||
* @param {SyncInitInput} module
|
||||
*
|
||||
* @returns {InitOutput}
|
||||
*/
|
||||
export function initSync(module: SyncInitInput): InitOutput;
|
||||
|
||||
/**
|
||||
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
|
||||
* for everything else, calls `WebAssembly.instantiate` directly.
|
||||
*
|
||||
* @param {InitInput | Promise<InitInput>} module_or_path
|
||||
*
|
||||
* @returns {Promise<InitOutput>}
|
||||
*/
|
||||
export default function init (module_or_path?: InitInput | Promise<InitInput>): Promise<InitOutput>;
|
||||
|
||||
|
||||
// USDPL customization
|
||||
export function init_embedded();
|
File diff suppressed because one or more lines are too long
Binary file not shown.
20
src/usdpl/usdpl_front_bg.wasm.d.ts
vendored
20
src/usdpl/usdpl_front_bg.wasm.d.ts
vendored
|
@ -1,20 +0,0 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const memory: WebAssembly.Memory;
|
||||
export function init_usdpl(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 init_tr(a: number, b: number): number;
|
||||
export function tr(a: number, b: number, c: number): void;
|
||||
export function tr_n(a: number, b: number, c: number, d: number): void;
|
||||
export function __wbindgen_export_0(a: number): number;
|
||||
export function __wbindgen_export_1(a: number, b: number, c: number): number;
|
||||
export const __wbindgen_export_2: WebAssembly.Table;
|
||||
export function __wbindgen_export_3(a: number, b: number, c: number): void;
|
||||
export function __wbindgen_add_to_stack_pointer(a: number): number;
|
||||
export function __wbindgen_export_4(a: number, b: number): void;
|
||||
export function __wbindgen_export_5(a: number): void;
|
||||
export function __wbindgen_export_6(a: number, b: number, c: number, d: number): void;
|
Loading…
Reference in a new issue