use bevy::prelude::*;
#[cfg(target_os = "ios")]
mod backend {
unsafe extern "C" {
pub fn platform_thermal_state() -> i32;
pub fn platform_low_power_mode() -> i32;
}
}
#[cfg(not(target_os = "ios"))]
mod backend {
pub unsafe fn platform_thermal_state() -> i32 {
match std::env::var("BEVY_IOS_FAKE_THERMAL")
.unwrap_or_default()
.to_ascii_lowercase()
.as_str()
{
"fair" => 1,
"serious" => 2,
"critical" => 3,
_ => 0,
}
}
pub unsafe fn platform_low_power_mode() -> i32 {
match std::env::var("BEVY_IOS_FAKE_LOW_POWER")
.unwrap_or_default()
.as_str()
{
"1" | "true" => 1,
_ => 0,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub enum ThermalState {
#[default]
Nominal,
Fair,
Serious,
Critical,
}
impl ThermalState {
fn from_i32(v: i32) -> ThermalState {
match v {
1 => ThermalState::Fair,
2 => ThermalState::Serious,
3 => ThermalState::Critical,
_ => ThermalState::Nominal,
}
}
}
#[derive(Resource, Clone, Copy, PartialEq, Eq, Debug, Default)]
pub struct PowerState {
pub thermal: ThermalState,
pub low_power_mode: bool,
}
impl PowerState {
pub fn is_constrained(self) -> bool {
self.low_power_mode
|| matches!(self.thermal, ThermalState::Serious | ThermalState::Critical)
}
}
#[derive(Message, Clone, Copy, Debug)]
pub struct PowerStateChanged(pub PowerState);
pub(super) fn poll_power(
mut power: ResMut<PowerState>,
mut changed: MessageWriter<PowerStateChanged>,
) {
let current = PowerState {
thermal: ThermalState::from_i32(unsafe { backend::platform_thermal_state() }),
low_power_mode: unsafe { backend::platform_low_power_mode() } != 0,
};
if current != *power {
*power = current;
changed.write(PowerStateChanged(current));
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::platform::PlatformPlugin;
#[test]
fn thermal_change_updates_resource_and_emits_once() {
unsafe { std::env::remove_var("BEVY_IOS_FAKE_THERMAL") };
let mut app = App::new();
app.add_plugins(MinimalPlugins).add_plugins(PlatformPlugin);
app.update();
assert_eq!(
app.world().resource::<PowerState>().thermal,
ThermalState::Nominal
);
assert!(!app.world().resource::<PowerState>().is_constrained());
unsafe { std::env::set_var("BEVY_IOS_FAKE_THERMAL", "serious") };
app.update();
let power = *app.world().resource::<PowerState>();
assert_eq!(power.thermal, ThermalState::Serious);
assert!(power.is_constrained());
let written: Vec<_> = app
.world_mut()
.resource_mut::<Messages<PowerStateChanged>>()
.drain()
.collect();
assert_eq!(written.len(), 1);
assert_eq!(written[0].0.thermal, ThermalState::Serious);
app.update();
assert!(
app.world_mut()
.resource_mut::<Messages<PowerStateChanged>>()
.drain()
.next()
.is_none()
);
unsafe { std::env::remove_var("BEVY_IOS_FAKE_THERMAL") };
}
}