Separate requested and applied output runtime power

This commit is contained in:
Samuel Onoja committed 2026-10-02 08:05:28 +01:00
1 parent 2f63cae54c
commit bb78801720
3 files changed
+134 -14

No files matched your search

@@ -0,0 +1,93 @@
//! Runtime power is independent of connector presence and configured enablement.
//! Only successful hardware transitions change the applied state.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) enum Power {
On,
Off,
}
#[derive(Debug)]
pub(super) struct OutputPower {
requested: Power,
applied: Option<Power>,
}
impl Default for OutputPower {
fn default() -> Self {
Self {
requested: Power::On,
applied: None,
}
}
}
impl OutputPower {
pub fn request(&mut self, power: Power) {
self.requested = power;
}
pub fn can_render(&self) -> bool {
self.requested == Power::On
}
pub fn needs_power_off(&self) -> bool {
self.requested == Power::Off && self.applied != Some(Power::Off)
}
pub fn needs_wake(&self) -> bool {
self.requested == Power::Off || self.applied != Some(Power::On)
}
pub fn powered_off(&mut self) {
self.applied = Some(Power::Off);
}
pub fn presented(&mut self) {
self.applied = Some(Power::On);
}
pub fn suspended(&mut self) {
self.applied = None;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn failed_power_off_keeps_the_applied_state_and_remains_retryable() {
let mut power = OutputPower::default();
power.presented();
power.request(Power::Off);
assert!(!power.can_render());
assert_eq!(power.applied, Some(Power::On));
assert!(power.needs_power_off());
power.powered_off();
assert!(!power.needs_power_off());
}
#[test]
fn waking_does_not_claim_success_before_presentation() {
let mut power = OutputPower::default();
power.request(Power::Off);
power.powered_off();
power.request(Power::On);
assert!(power.can_render());
assert_eq!(power.applied, Some(Power::Off));
power.presented();
assert!(!power.needs_wake());
}
#[test]
fn pending_frame_retirement_and_suspend_do_not_lose_requested_power() {
let mut power = OutputPower::default();
power.request(Power::Off);
power.presented();
assert!(power.needs_power_off());
power.suspended();
assert_eq!(power.applied, None);
assert!(power.needs_power_off());
assert!(!power.can_render());
}
}
+7 -3
View File
@@ -39,7 +39,7 @@ pub(super) fn request_frame(state: &mut Ferese, node: DrmNode, crtc: crtc::Handl
return;
};
if output.power_off {
if !output.power.can_render() {
return;
}
@@ -63,7 +63,11 @@ pub(super) fn arm_frame(state: &mut Ferese, node: DrmNode, crtc: crtc::Handle) {
return;
};
if output.render_timer.is_some() || output.frame_pending || output.power_off || state.session_lock.sleeping {
if output.render_timer.is_some()
|| output.frame_pending
|| !output.power.can_render()
|| state.session_lock.sleeping
{
return;
}
@@ -110,7 +114,7 @@ fn dispatch_frame(state: &mut Ferese, node: DrmNode, crtc: crtc::Handle, identit
};
output.render_timer = None;
if output.frame_pending || output.power_off || state.session_lock.sleeping {
if output.frame_pending || !output.power.can_render() || state.session_lock.sleeping {
return;
}