use std::sync::atomic::{AtomicU32, Ordering};
use kanade_shared::manifest::Require;
static LATEST_SYSTEM_CPU: AtomicU32 = AtomicU32::new(f32::NAN.to_bits());
pub fn set_system_cpu(pct: Option<f32>) {
let bits = pct.unwrap_or(f32::NAN).to_bits();
LATEST_SYSTEM_CPU.store(bits, Ordering::Relaxed);
}
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
fn system_cpu() -> Option<f64> {
let v = f32::from_bits(LATEST_SYSTEM_CPU.load(Ordering::Relaxed));
if v.is_nan() { None } else { Some(v as f64) }
}
pub fn require_satisfied(req: &Require) -> bool {
if req.is_empty() {
return true; }
#[cfg(target_os = "windows")]
{
use kanade_shared::manifest::{EnvState, require_met};
let (ac_online, idle, network_up) = sense_windows();
require_met(
req,
&EnvState {
ac_online,
idle,
cpu_pct: system_cpu(),
network_up,
},
)
}
#[cfg(not(target_os = "windows"))]
{
let _ = req;
true
}
}
#[cfg(target_os = "windows")]
fn sense_windows() -> (bool, Option<std::time::Duration>, bool) {
use std::time::Duration;
use windows::Win32::NetworkManagement::IpHelper::GetNetworkConnectivityHint;
use windows::Win32::Networking::WinSock::{
NL_NETWORK_CONNECTIVITY_HINT, NetworkConnectivityLevelHintInternetAccess,
};
use windows::Win32::System::Power::{GetSystemPowerStatus, SYSTEM_POWER_STATUS};
use windows::Win32::System::RemoteDesktop::{
WTS_CURRENT_SERVER_HANDLE, WTS_INFO_CLASS, WTSFreeMemory, WTSGetActiveConsoleSessionId,
WTSINFOW, WTSQuerySessionInformationW, WTSSessionInfo,
};
use windows::core::PWSTR;
let ac_online = {
let mut st = SYSTEM_POWER_STATUS::default();
match unsafe { GetSystemPowerStatus(&mut st) } {
Ok(()) => st.ACLineStatus == 1,
Err(_) => false,
}
};
let idle = {
let session = unsafe { WTSGetActiveConsoleSessionId() };
if session == 0xFFFF_FFFF {
Some(Duration::MAX)
} else {
let mut buf = PWSTR::null();
let mut bytes: u32 = 0;
unsafe {
match WTSQuerySessionInformationW(
Some(WTS_CURRENT_SERVER_HANDLE),
session,
WTS_INFO_CLASS(WTSSessionInfo.0),
&mut buf,
&mut bytes,
) {
Ok(())
if (bytes as usize) >= std::mem::size_of::<WTSINFOW>()
&& !buf.is_null() =>
{
let info = &*(buf.0 as *const WTSINFOW);
let delta_100ns = info.CurrentTime.saturating_sub(info.LastInputTime);
let d = if delta_100ns > 0 {
Duration::from_nanos((delta_100ns as u64).saturating_mul(100))
} else {
Duration::ZERO
};
WTSFreeMemory(buf.0 as *mut core::ffi::c_void);
Some(d)
}
Ok(()) => {
if !buf.is_null() {
WTSFreeMemory(buf.0 as *mut core::ffi::c_void);
}
None
}
Err(_) => None,
}
}
}
};
let network_up = {
let mut hint = NL_NETWORK_CONNECTIVITY_HINT::default();
match unsafe { GetNetworkConnectivityHint(&mut hint) } {
ret if ret.0 == 0 => {
hint.ConnectivityLevel == NetworkConnectivityLevelHintInternetAccess
}
_ => false,
}
};
(ac_online, idle, network_up)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_require_is_satisfied_without_syscalls() {
assert!(require_satisfied(&Require::default()));
assert!(require_satisfied(&Require {
ac_power: false,
idle: None,
cpu_below: None,
network: false,
}));
#[cfg(not(target_os = "windows"))]
assert!(require_satisfied(&Require {
ac_power: true,
idle: Some("10m".into()),
cpu_below: Some(20.0),
network: true,
}));
}
#[test]
fn system_cpu_roundtrips_and_unknown_is_none() {
set_system_cpu(Some(42.5));
assert_eq!(system_cpu(), Some(42.5));
set_system_cpu(None);
assert!(system_cpu().is_none());
}
}