use super::*;
use std::collections::HashMap;
use std::collections::HashSet;
use crate::backend::tty::drm::collect_outputs_for_ipc;
use crate::backend::tty::drm::{TtyDrmDevice, TtyDrmOutput};
pub(crate) fn sync_tty_dpms_state(
outputs: &[TtyDrmOutput],
dpms_enabled: &Rc<RefCell<HashMap<String, bool>>>,
) {
let mut next = HashMap::new();
let current = dpms_enabled.borrow();
for output in outputs {
next.insert(
output.connector_name.clone(),
current
.get(output.connector_name.as_str())
.copied()
.unwrap_or(true),
);
}
drop(current);
*dpms_enabled.borrow_mut() = next;
}
pub(crate) fn tty_output_dpms_enabled(
dpms_enabled: &HashMap<String, bool>,
output_name: &str,
) -> bool {
dpms_enabled.get(output_name).copied().unwrap_or(true)
}
pub(crate) fn any_tty_output_dpms_enabled(dpms_enabled: &HashMap<String, bool>) -> bool {
dpms_enabled.values().copied().any(|enabled| enabled)
}
pub(crate) fn publish_tty_outputs_snapshot_for_devices(
devices: &[TtyDrmDevice],
active_modes: &HashMap<String, drm_control::Mode>,
dpms_enabled: &HashMap<String, bool>,
tuning: &RuntimeTuning,
st: &Halley,
) {
let vrr_support: HashMap<String, String> = HashMap::new();
let mut outputs = Vec::new();
for device in devices {
outputs.extend(collect_outputs_for_ipc(
&device.dev.borrow(),
active_modes,
tuning,
&vrr_support,
&st.model.fullscreen_state.direct_scanout,
));
}
for output in &mut outputs {
if active_modes.contains_key(&output.name)
&& !tty_output_dpms_enabled(dpms_enabled, output.name.as_str())
{
output.enabled = false;
output.current_mode = None;
for mode in &mut output.modes {
mode.current = false;
}
}
}
publish_outputs(outputs);
}
pub(crate) fn apply_tty_dpms_command(
drm_devices: &Rc<RefCell<Vec<TtyDrmDevice>>>,
active_modes: &Rc<RefCell<HashMap<String, drm_control::Mode>>>,
dpms_enabled: &Rc<RefCell<HashMap<String, bool>>>,
command: halley_ipc::DpmsCommand,
output: Option<&str>,
outputs: &Rc<RefCell<Vec<TtyDrmOutput>>>,
tuning: &RuntimeTuning,
output_frame_pending: &Rc<RefCell<HashMap<String, bool>>>,
dpms_just_woke_outputs: &Rc<RefCell<HashSet<String>>>,
st: &mut Halley,
) -> bool {
let target_outputs: Vec<String> = {
let outputs = outputs.borrow();
match output {
Some(output) => {
let Some(found) = outputs
.iter()
.find(|candidate| candidate.connector_name == output)
.map(|candidate| candidate.connector_name.clone())
else {
return false;
};
vec![found]
}
None => outputs
.iter()
.map(|output| output.connector_name.clone())
.collect(),
}
};
let target_enabled = match command {
halley_ipc::DpmsCommand::On => true,
halley_ipc::DpmsCommand::Off => false,
halley_ipc::DpmsCommand::Toggle => {
let current = dpms_enabled.borrow();
!target_outputs
.iter()
.all(|name| tty_output_dpms_enabled(¤t, name.as_str()))
}
};
let already_matches = {
let current = dpms_enabled.borrow();
target_outputs
.iter()
.all(|name| tty_output_dpms_enabled(¤t, name.as_str()) == target_enabled)
};
if already_matches {
return false;
}
if !target_enabled {
for output in outputs.borrow().iter() {
if !target_outputs.contains(&output.connector_name) {
continue;
}
if let Err(err) = output.compositor.borrow_mut().clear() {
warn!(
"tty dpms off: clear failed for {}: {}",
output.connector_name, err
);
}
output_frame_pending
.borrow_mut()
.insert(output.connector_name.clone(), false);
dpms_just_woke_outputs
.borrow_mut()
.remove(output.connector_name.as_str());
}
{
let mut current = dpms_enabled.borrow_mut();
for output in &target_outputs {
current.insert(output.clone(), false);
}
}
debug!(
"tty dpms: powered off outputs {}",
target_outputs.join(", ")
);
} else {
{
let mut current = dpms_enabled.borrow_mut();
for output in &target_outputs {
current.insert(output.clone(), true);
}
}
{
let mut woke = dpms_just_woke_outputs.borrow_mut();
for output in &target_outputs {
woke.insert(output.clone());
}
}
st.input.interaction_state.dpms_just_woke = true;
debug!(
"tty dpms: powering on outputs {}",
target_outputs.join(", ")
);
}
publish_tty_outputs_snapshot_for_devices(
&drm_devices.borrow(),
&active_modes.borrow(),
&dpms_enabled.borrow(),
tuning,
st,
);
true
}
pub(crate) fn wake_tty_dpms_on_input(
drm_devices: &Rc<RefCell<Vec<TtyDrmDevice>>>,
active_modes: &Rc<RefCell<HashMap<String, drm_control::Mode>>>,
dpms_enabled: &Rc<RefCell<HashMap<String, bool>>>,
outputs: &Rc<RefCell<Vec<TtyDrmOutput>>>,
tuning: &RuntimeTuning,
output_frame_pending: &Rc<RefCell<HashMap<String, bool>>>,
dpms_just_woke_outputs: &Rc<RefCell<HashSet<String>>>,
target_output: Option<&str>,
st: &mut Halley,
) -> bool {
let focused_monitor = st.focused_monitor().to_string();
let output = target_output.unwrap_or(focused_monitor.as_str());
let current = dpms_enabled.borrow();
let wake_all = !any_tty_output_dpms_enabled(¤t);
if !wake_all && tty_output_dpms_enabled(¤t, output) {
return false;
}
drop(current);
apply_tty_dpms_command(
drm_devices,
active_modes,
dpms_enabled,
halley_ipc::DpmsCommand::On,
if wake_all { None } else { Some(output) },
outputs,
tuning,
output_frame_pending,
dpms_just_woke_outputs,
st,
)
}