From 13bf31611d88b25c1e82239f579eb373abb5cfd1 Mon Sep 17 00:00:00 2001 From: "NGnius (Graham)" Date: Wed, 4 Jan 2023 19:42:59 -0500 Subject: [PATCH] Send front-end logs to back and improve Steam Deck driver display --- backend/src/api/general.rs | 33 ++++++++++++ backend/src/main.rs | 1 + backend/src/settings/steam_deck/battery.rs | 14 +++-- backend/src/settings/steam_deck/cpu.rs | 14 +++-- backend/src/settings/steam_deck/gpu.rs | 17 +++--- backend/src/settings/steam_deck/oc_limits.rs | 11 ++-- src/backend.ts | 13 +++++ src/index.tsx | 54 ++++++++++---------- 8 files changed, 113 insertions(+), 44 deletions(-) diff --git a/backend/src/api/general.rs b/backend/src/api/general.rs index 309203d..aae871b 100644 --- a/backend/src/api/general.rs +++ b/backend/src/api/general.rs @@ -215,3 +215,36 @@ pub fn gunter(_: super::ApiParameterType) -> super::ApiParameterType { }); vec![true.into()] } + +/// API web method to send log messages to the back-end log, callable from the front-end +pub fn log_it() -> impl Fn(super::ApiParameterType) -> super::ApiParameterType { + move |params| { + if let Some(Primitive::F64(level)) = params.get(0) { + if let Some(Primitive::String(msg)) = params.get(1) { + log_msg_by_level(*level as u8, msg); + vec![true.into()] + } else if let Some(Primitive::Json(msg)) = params.get(1) { + log_msg_by_level(*level as u8, msg); + vec![true.into()] + } else { + log::warn!("Got log_it call with wrong/missing 2nd parameter"); + vec![false.into()] + } + } else { + log::warn!("Got log_it call with wrong/missing 1st parameter"); + vec![false.into()] + } + } +} + +fn log_msg_by_level(level: u8, msg: &str) { + match level { + 1 => log::trace!("FRONT-END: {}", msg), + 2 => log::debug!("FRONT-END: {}", msg), + 3 => log::info!("FRONT-END: {}", msg), + 4 => log::warn!("FRONT-END: {}", msg), + 5 => log::error!("FRONT-END: {}", msg), + _ => log::trace!("FRONT-END: {}", msg), + } +} + diff --git a/backend/src/main.rs b/backend/src/main.rs index 8390b97..d8b4397 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -75,6 +75,7 @@ fn main() -> Result<(), ()> { .register("V_INFO", |_: Vec| { vec![format!("{} v{}", PACKAGE_NAME, PACKAGE_VERSION).into()] }) + .register("LOG", api::general::log_it()) // battery API functions .register_async("BATTERY_current_now", api::battery::current_now(api_sender.clone())) .register_async("BATTERY_charge_now", api::battery::charge_now(api_sender.clone())) diff --git a/backend/src/settings/steam_deck/battery.rs b/backend/src/settings/steam_deck/battery.rs index 3732bee..c2fdb60 100644 --- a/backend/src/settings/steam_deck/battery.rs +++ b/backend/src/settings/steam_deck/battery.rs @@ -13,6 +13,7 @@ pub struct Battery { pub charge_mode: Option, limits: BatteryLimits, state: crate::state::steam_deck::Battery, + driver_mode: crate::persist::DriverJson, } const BATTERY_VOLTAGE: f64 = 7.7; @@ -26,19 +27,23 @@ const BATTERY_CHARGE_DESIGN_PATH: &str = "/sys/class/hwmon/hwmon2/device/charge_ impl Battery { #[inline] pub fn from_json(other: BatteryJson, version: u64) -> Self { - let oc_limits = OverclockLimits::load_or_default().battery; + let (oc_limits, is_default) = OverclockLimits::load_or_default(); + let oc_limits = oc_limits.battery; + let driver = if is_default { crate::persist::DriverJson::SteamDeck } else { crate::persist::DriverJson::SteamDeckAdvance }; match version { 0 => Self { charge_rate: other.charge_rate, charge_mode: other.charge_mode.map(|x| Self::str_to_charge_mode(&x)).flatten(), limits: oc_limits, state: crate::state::steam_deck::Battery::default(), + driver_mode: driver, }, _ => Self { charge_rate: other.charge_rate, charge_mode: other.charge_mode.map(|x| Self::str_to_charge_mode(&x)).flatten(), limits: oc_limits, state: crate::state::steam_deck::Battery::default(), + driver_mode: driver, }, } } @@ -184,12 +189,15 @@ impl Battery { } pub fn system_default() -> Self { - let oc_limits = OverclockLimits::load_or_default().battery; + let (oc_limits, is_default) = OverclockLimits::load_or_default(); + let oc_limits = oc_limits.battery; + let driver = if is_default { crate::persist::DriverJson::SteamDeck } else { crate::persist::DriverJson::SteamDeckAdvance }; Self { charge_rate: None, charge_mode: None, limits: oc_limits, state: crate::state::steam_deck::Battery::default(), + driver_mode: driver, } } } @@ -290,6 +298,6 @@ impl TBattery for Battery { } fn provider(&self) -> crate::persist::DriverJson { - crate::persist::DriverJson::SteamDeck + self.driver_mode.clone() } } diff --git a/backend/src/settings/steam_deck/cpu.rs b/backend/src/settings/steam_deck/cpu.rs index f1b33b8..7ae9630 100644 --- a/backend/src/settings/steam_deck/cpu.rs +++ b/backend/src/settings/steam_deck/cpu.rs @@ -17,6 +17,7 @@ pub struct Cpus { pub smt_capable: bool, #[allow(dead_code)] // in case this may be useful in the future pub(super) limits: CpusLimits, + driver_mode: crate::persist::DriverJson, } impl OnSet for Cpus { @@ -84,7 +85,9 @@ impl Cpus { } pub fn system_default() -> Self { - let oc_limits = OverclockLimits::load_or_default().cpus; + let (oc_limits, is_default) = OverclockLimits::load_or_default(); + let oc_limits = oc_limits.cpus; + let driver = if is_default { crate::persist::DriverJson::SteamDeck } else { crate::persist::DriverJson::SteamDeckAdvance }; if let Some(max_cpu) = Self::cpu_count() { let mut sys_cpus = Vec::with_capacity(max_cpu); for i in 0..max_cpu { @@ -96,6 +99,7 @@ impl Cpus { smt: true, smt_capable: can_smt, limits: oc_limits, + driver_mode: driver, } } else { Self { @@ -103,13 +107,16 @@ impl Cpus { smt: false, smt_capable: false, limits: oc_limits, + driver_mode: driver, } } } #[inline] pub fn from_json(mut other: Vec, version: u64) -> Self { - let oc_limits = OverclockLimits::load_or_default().cpus; + let (oc_limits, is_default) = OverclockLimits::load_or_default(); + let oc_limits = oc_limits.cpus; + let driver = if is_default { crate::persist::DriverJson::SteamDeck } else { crate::persist::DriverJson::SteamDeckAdvance }; let (_, can_smt) = Self::system_smt_capabilities(); let mut result = Vec::with_capacity(other.len()); let max_cpus = Self::cpu_count(); @@ -138,6 +145,7 @@ impl Cpus { smt: !smt_disabled, smt_capable: can_smt, limits: oc_limits, + driver_mode: driver, } } } @@ -168,7 +176,7 @@ impl TCpus for Cpus { } fn provider(&self) -> crate::persist::DriverJson { - crate::persist::DriverJson::SteamDeck + self.driver_mode.clone() } } diff --git a/backend/src/settings/steam_deck/gpu.rs b/backend/src/settings/steam_deck/gpu.rs index 9e77ba8..8f3f826 100644 --- a/backend/src/settings/steam_deck/gpu.rs +++ b/backend/src/settings/steam_deck/gpu.rs @@ -18,6 +18,7 @@ pub struct Gpu { pub slow_memory: bool, limits: GpuLimits, state: crate::state::steam_deck::Gpu, + driver_mode: crate::persist::DriverJson, } // same as CPU @@ -28,23 +29,26 @@ const GPU_MEMORY_DOWNCLOCK_PATH: &str = "/sys/class/drm/card0/device/pp_dpm_fclk impl Gpu { #[inline] pub fn from_json(other: GpuJson, version: u64) -> Self { - let oc_limits = OverclockLimits::load_or_default().gpu; + let (oc_limits, is_default) = OverclockLimits::load_or_default(); + let driver = if is_default { crate::persist::DriverJson::SteamDeck } else { crate::persist::DriverJson::SteamDeckAdvance }; match version { 0 => Self { fast_ppt: other.fast_ppt, slow_ppt: other.slow_ppt, clock_limits: other.clock_limits.map(|x| min_max_from_json(x, version)), slow_memory: other.slow_memory, - limits: oc_limits, + limits: oc_limits.gpu, state: crate::state::steam_deck::Gpu::default(), + driver_mode: driver, }, _ => Self { fast_ppt: other.fast_ppt, slow_ppt: other.slow_ppt, clock_limits: other.clock_limits.map(|x| min_max_from_json(x, version)), slow_memory: other.slow_memory, - limits: oc_limits, + limits: oc_limits.gpu, state: crate::state::steam_deck::Gpu::default(), + driver_mode: driver, }, } } @@ -178,14 +182,15 @@ impl Gpu { } pub fn system_default() -> Self { - let oc_limits = OverclockLimits::load_or_default().gpu; + let (oc_limits, is_default) = OverclockLimits::load_or_default(); Self { fast_ppt: None, slow_ppt: None, clock_limits: None, slow_memory: false, - limits: oc_limits, + limits: oc_limits.gpu, state: crate::state::steam_deck::Gpu::default(), + driver_mode: if is_default { crate::persist::DriverJson::SteamDeck } else { crate::persist::DriverJson::SteamDeckAdvance }, } } } @@ -273,7 +278,7 @@ impl TGpu for Gpu { } fn provider(&self) -> crate::persist::DriverJson { - crate::persist::DriverJson::SteamDeck + self.driver_mode.clone() } } diff --git a/backend/src/settings/steam_deck/oc_limits.rs b/backend/src/settings/steam_deck/oc_limits.rs index 9712831..fe45826 100644 --- a/backend/src/settings/steam_deck/oc_limits.rs +++ b/backend/src/settings/steam_deck/oc_limits.rs @@ -21,7 +21,8 @@ impl Default for OverclockLimits { } impl OverclockLimits { - pub fn load_or_default() -> Self { + /// (Self, is_default) + pub fn load_or_default() -> (Self, bool) { let path = std::path::Path::new(OC_LIMITS_FILEPATH); if path.exists() { log::info!("Steam Deck limits file {} found", path.display()); @@ -29,22 +30,22 @@ impl OverclockLimits { Ok(f) => f, Err(e) => { log::warn!("Steam Deck limits file {} err: {} (using default fallback)", path.display(), e); - return Self::default(); + return (Self::default(), true); }, }; match serde_json::from_reader(&mut file) { Ok(result) => { log::debug!("Steam Deck limits file {} successfully loaded", path.display()); - result + (result, false) }, Err(e) => { log::warn!("Steam Deck limits file {} json err: {} (using default fallback)", path.display(), e); - Self::default() + (Self::default(), true) } } } else { log::info!("Steam Deck limits file {} not found (using default fallback)", path.display()); - Self::default() + (Self::default(), true) } } } diff --git a/src/backend.ts b/src/backend.ts index 53a1c9d..59a4f4d 100644 --- a/src/backend.ts +++ b/src/backend.ts @@ -12,6 +12,7 @@ export function resolve(promise: Promise, setter: (t: T) => void) { setter(data); } else { console.warn("Resolve failed:", data); + log(LogLevel.Warn, ""); } })(); } @@ -231,6 +232,18 @@ export async function getDriverProviderName(name: string): Promise { return (await call_backend("GENERAL_get_provider", [name]))[0]; } +export enum LogLevel { + Trace = 1, + Debug = 2, + Info = 3, + Warn = 4, + Error = 5, +} + +export async function log(level: LogLevel, msg: string): Promise { + return (await call_backend("LOG", [level, msg]))[0]; +} + export async function idk(): Promise { return (await call_backend("GENERAL_idk", []))[0]; } diff --git a/src/index.tsx b/src/index.tsx index b804bcd..ef25c48 100755 --- a/src/index.tsx +++ b/src/index.tsx @@ -104,7 +104,7 @@ const reload = function() { backend.resolve(backend.getLimits(), (limits) => { set_value(LIMITS_INFO, limits); - console.debug("POWERTOOLS: got limits", limits); + console.debug("POWERTOOLS: got limits ", limits); }); backend.resolve(backend.getBatteryCurrent(), (rate: number) => { set_value(CURRENT_BATT, rate) }); @@ -128,7 +128,7 @@ const reload = function() { }); backend.resolve(backend.getCpusGovernor(), (governors: string[]) => { set_value(GOVERNOR_CPU, governors); - console.log("POWERTOOLS: Governors from backend", governors); + backend.log(backend.LogLevel.Info, "POWERTOOLS: Governors from backend " + governors.toString()); }); backend.resolve(backend.getGpuPpt(), (ppts: number[]) => { @@ -159,12 +159,12 @@ const reload = function() { //@ts-ignore lifetimeHook = SteamClient.GameSessions.RegisterForAppLifetimeNotifications((update) => { if (update.bRunning) { - //console.debug("AppID " + update.unAppID.toString() + " is now running"); + //backend.log(backend.LogLevel.Debug, "AppID " + update.unAppID.toString() + " is now running"); } else { - //console.debug("AppID " + update.unAppID.toString() + " is no longer running"); + //backend.log(backend.LogLevel.Debug, "AppID " + update.unAppID.toString() + " is no longer running"); backend.resolve( backend.loadGeneralDefaultSettings(), - (ok: boolean) => {console.debug("Loading default settings ok? " + ok)} + (ok: boolean) => {backend.log(backend.LogLevel.Debug, "Loading default settings ok? " + ok)} ); } }); @@ -175,11 +175,11 @@ const reload = function() { // don't use gameInfo.appid, haha backend.resolve( backend.loadGeneralSettings(id.toString() + ".json", gameInfo.display_name), - (ok: boolean) => {console.debug("Loading settings ok? " + ok)} + (ok: boolean) => {backend.log(backend.LogLevel.Debug, "Loading settings ok? " + ok)} ); }); - console.debug("Registered PowerTools callbacks, hello!"); + backend.log(backend.LogLevel.Debug, "Registered PowerTools callbacks, hello!"); })(); const periodicals = function() { @@ -250,7 +250,7 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => { label="SMT" description="Enables odd-numbered CPUs" onChange={(smt: boolean) => { - console.debug("SMT is now " + smt.toString()); + backend.log(backend.LogLevel.Debug, "SMT is now " + smt.toString()); const cpus = get_value(ONLINE_CPUS); const smtNow = smt && smtAllowed; backend.resolve(backend.setCpuSmt(smtNow), (newVal: boolean) => { @@ -280,7 +280,7 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => { min={1} showValue={true} onChange={(cpus: number) => { - console.debug("CPU slider is now " + cpus.toString()); + backend.log(backend.LogLevel.Debug, "CPU slider is now " + cpus.toString()); const onlines = get_value(ONLINE_CPUS); if (cpus != onlines) { set_value(ONLINE_CPUS, cpus); @@ -340,7 +340,7 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => { showValue={true} disabled={get_value(CLOCK_MIN_CPU) == null} onChange={(freq: number) => { - console.debug("Min freq slider is now " + freq.toString()); + backend.log(backend.LogLevel.Debug, "Min freq slider is now " + freq.toString()); const freqNow = get_value(CLOCK_MIN_CPU); if (freq != freqNow) { set_value(CLOCK_MIN_CPU, freq); @@ -370,7 +370,7 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => { showValue={true} disabled={get_value(CLOCK_MAX_CPU) == null} onChange={(freq: number) => { - console.debug("Max freq slider is now " + freq.toString()); + backend.log(backend.LogLevel.Debug, "Max freq slider is now " + freq.toString()); const freqNow = get_value(CLOCK_MAX_CPU); if (freq != freqNow) { set_value(CLOCK_MAX_CPU, freq); @@ -410,7 +410,7 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => { label="Online" description="Allow the CPU thread to do work" onChange={(status: boolean) => { - console.debug("CPU " + advancedCpu.toString() + " is now " + status.toString()); + backend.log(backend.LogLevel.Debug, "CPU " + advancedCpu.toString() + " is now " + status.toString()); if (get_value(SMT_CPU)) { backend.resolve(backend.setCpuSmt(false), (newVal: boolean) => { set_value(SMT_CPU, newVal); @@ -463,7 +463,7 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => { showValue={true} disabled={get_value(CLOCK_MIN_MAX_CPU)[advancedCpuIndex].min == null} onChange={(freq: number) => { - console.debug("Min freq slider for " + advancedCpu.toString() + " is now " + freq.toString()); + backend.log(backend.LogLevel.Debug, "Min freq slider for " + advancedCpu.toString() + " is now " + freq.toString()); const freqNow = get_value(CLOCK_MIN_MAX_CPU)[advancedCpuIndex] as MinMax; if (freq != freqNow.min) { backend.resolve(backend.setCpuClockLimits(advancedCpuIndex, freq, freqNow.max!), @@ -488,7 +488,7 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => { showValue={true} disabled={get_value(CLOCK_MIN_MAX_CPU)[advancedCpuIndex].max == null} onChange={(freq: number) => { - console.debug("Max freq slider for " + advancedCpu.toString() + " is now " + freq.toString()); + backend.log(backend.LogLevel.Debug, "Max freq slider for " + advancedCpu.toString() + " is now " + freq.toString()); const freqNow = get_value(CLOCK_MIN_MAX_CPU)[advancedCpuIndex] as MinMax; if (freq != freqNow.max) { backend.resolve(backend.setCpuClockLimits(advancedCpuIndex, freqNow.min!, freq), @@ -511,13 +511,13 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => { menuLabel="Governor" rgOptions={governorOptions} selectedOption={governorOptions.find((val: SingleDropdownOption, _index, _arr) => { - console.debug("POWERTOOLS: array item", val); - console.debug("POWERTOOLS: looking for data", get_value(GOVERNOR_CPU)[advancedCpuIndex]); + backend.log(backend.LogLevel.Debug, "POWERTOOLS: array item " + val.toString()); + backend.log(backend.LogLevel.Debug, "POWERTOOLS: looking for data " + get_value(GOVERNOR_CPU)[advancedCpuIndex].toString()); return val.data == get_value(GOVERNOR_CPU)[advancedCpuIndex]; })} strDefaultLabel={get_value(GOVERNOR_CPU)[advancedCpuIndex]} onChange={(elem: SingleDropdownOption) => { - console.debug("Governor dropdown selected", elem); + backend.log(backend.LogLevel.Debug, "Governor dropdown selected " + elem.data.toString()); backend.resolve(backend.setCpuGovernor(advancedCpuIndex, elem.data as string), (gov: string) => { const governors = get_value(GOVERNOR_CPU); governors[advancedCpuIndex] = gov; @@ -567,7 +567,7 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => { showValue={true} disabled={get_value(SLOW_PPT_GPU) == null} onChange={(ppt: number) => { - console.debug("SlowPPT is now " + ppt.toString()); + backend.log(backend.LogLevel.Debug, "SlowPPT is now " + ppt.toString()); const pptNow = get_value(SLOW_PPT_GPU); const realPpt = ppt; if (realPpt != pptNow) { @@ -591,7 +591,7 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => { showValue={true} disabled={get_value(FAST_PPT_GPU) == null} onChange={(ppt: number) => { - console.debug("FastPPT is now " + ppt.toString()); + backend.log(backend.LogLevel.Debug, "FastPPT is now " + ppt.toString()); const pptNow = get_value(FAST_PPT_GPU); const realPpt = ppt; if (realPpt != pptNow) { @@ -641,7 +641,7 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => { showValue={true} disabled={get_value(CLOCK_MIN_GPU) == null} onChange={(val: number) => { - console.debug("GPU Clock Min is now " + val.toString()); + backend.log(backend.LogLevel.Debug, "GPU Clock Min is now " + val.toString()); const valNow = get_value(CLOCK_MIN_GPU); if (val != valNow) { backend.resolve(backend.setGpuClockLimits(val, get_value(CLOCK_MAX_GPU)), @@ -664,7 +664,7 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => { showValue={true} disabled={get_value(CLOCK_MAX_GPU) == null} onChange={(val: number) => { - console.debug("GPU Clock Max is now " + val.toString()); + backend.log(backend.LogLevel.Debug, "GPU Clock Max is now " + val.toString()); const valNow = get_value(CLOCK_MAX_GPU); if (val != valNow) { backend.resolve(backend.setGpuClockLimits(get_value(CLOCK_MIN_GPU), val), @@ -736,7 +736,7 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => { showValue={true} disabled={get_value(CHARGE_RATE_BATT) == null} onChange={(val: number) => { - console.debug("Charge rate is now " + val.toString()); + backend.log(backend.LogLevel.Debug, "Charge rate is now " + val.toString()); const rateNow = get_value(CHARGE_RATE_BATT); if (val != rateNow) { backend.resolve(backend.setBatteryChargeRate(val), @@ -776,7 +776,7 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => { })} strDefaultLabel={get_value(CHARGE_MODE_BATT)} onChange={(elem: SingleDropdownOption) => { - console.debug("Charge mode dropdown selected", elem); + backend.log(backend.LogLevel.Debug, "Charge mode dropdown selected " + elem.data.toString()); backend.resolve(backend.setBatteryChargeMode(elem.data as string), (mode: string) => { set_value(CHARGE_MODE_BATT, mode); reloadGUI("BATTChargeMode"); @@ -803,7 +803,7 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => { label="Persistent" description="Save profile and load it next time" onChange={(persist: boolean) => { - console.debug("Persist is now " + persist.toString()); + backend.log(backend.LogLevel.Debug, "Persist is now " + persist.toString()); backend.resolve( backend.setGeneralPersistent(persist), (val: boolean) => {set_value(PERSISTENT_GEN, val)} @@ -879,7 +879,7 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => { { - console.debug("Loading default PowerTools settings"); + backend.log(backend.LogLevel.Debug, "Loading default PowerTools settings"); backend.resolve( backend.setGeneralPersistent(false), (val: boolean) => { @@ -905,13 +905,13 @@ export default definePlugin((serverApi: ServerAPI) => { content: , icon: , onDismount() { - console.debug("PowerTools shutting down"); + backend.log(backend.LogLevel.Debug, "PowerTools shutting down"); clearInterval(periodicHook!); periodicHook = null; lifetimeHook!.unregister(); startHook!.unregister(); serverApi.routerHook.removeRoute("/decky-plugin-test"); - console.debug("Unregistered PowerTools callbacks, goodbye."); + backend.log(backend.LogLevel.Debug, "Unregistered PowerTools callbacks, goodbye."); }, }; });