Explain runtime invariants and avoid redundant unwraps
This commit is contained in:
1 parent
34f2f07301
commit
1b51ba83b4
116 files changed
+1643
-515
No files matched your search
@@ -387,7 +387,7 @@ pub fn init(event_loop: &mut EventLoop<Ferese>, state: &mut Ferese) -> Result<()
|
||||
state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.topology
|
||||
.observe_devices(&[(
|
||||
primary_path.clone(),
|
||||
@@ -640,25 +640,43 @@ pub(crate) fn set_output_profile(state: &mut Ferese, name: &str) -> Result<(), S
|
||||
return Err("profile does not match the connected monitors/lid".into());
|
||||
}
|
||||
if let Err(error) = validate_live_outputs(state, &state.output_profiles) {
|
||||
state.direct_backend.as_mut().unwrap().manual_outputs = previous;
|
||||
state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.manual_outputs = previous;
|
||||
return Err(error);
|
||||
}
|
||||
reconcile_outputs(state, false);
|
||||
if let Some(error) = state.direct_backend.as_ref().unwrap().output_error.clone() {
|
||||
state.direct_backend.as_mut().unwrap().manual_outputs = previous;
|
||||
if let Some(error) = state
|
||||
.direct_backend
|
||||
.as_ref()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.output_error
|
||||
.clone()
|
||||
{
|
||||
state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.manual_outputs = previous;
|
||||
return Err(error);
|
||||
}
|
||||
if name != "auto"
|
||||
&& state
|
||||
.direct_backend
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.desired_outputs
|
||||
.profile
|
||||
.as_deref()
|
||||
!= Some(name)
|
||||
{
|
||||
state.direct_backend.as_mut().unwrap().manual_outputs = previous;
|
||||
state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.manual_outputs = previous;
|
||||
return Err("profile does not match the connected monitors/lid".into());
|
||||
}
|
||||
arm_output_confirmation(state, known_good, previous);
|
||||
@@ -698,12 +716,26 @@ pub(crate) fn set_output_layout(
|
||||
backend.manual_outputs.internal = None;
|
||||
backend.manual_outputs.layout = Some(layout);
|
||||
if let Err(error) = validate_live_outputs(state, &state.output_profiles) {
|
||||
state.direct_backend.as_mut().unwrap().manual_outputs = previous;
|
||||
state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.manual_outputs = previous;
|
||||
return Err(error);
|
||||
}
|
||||
reconcile_outputs(state, false);
|
||||
if let Some(error) = state.direct_backend.as_ref().unwrap().output_error.clone() {
|
||||
state.direct_backend.as_mut().unwrap().manual_outputs = previous;
|
||||
if let Some(error) = state
|
||||
.direct_backend
|
||||
.as_ref()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.output_error
|
||||
.clone()
|
||||
{
|
||||
state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.manual_outputs = previous;
|
||||
return Err(error);
|
||||
}
|
||||
arm_output_confirmation(state, known_good, previous);
|
||||
@@ -732,8 +764,18 @@ pub(crate) fn set_internal_output(state: &mut Ferese, enabled: bool) -> Result<(
|
||||
backend.manual_outputs.internal = Some(enabled);
|
||||
backend.manual_outputs.layout = None;
|
||||
reconcile_outputs(state, false);
|
||||
if let Some(error) = state.direct_backend.as_ref().unwrap().output_error.clone() {
|
||||
state.direct_backend.as_mut().unwrap().manual_outputs = previous;
|
||||
if let Some(error) = state
|
||||
.direct_backend
|
||||
.as_ref()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.output_error
|
||||
.clone()
|
||||
{
|
||||
state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.manual_outputs = previous;
|
||||
return Err(error);
|
||||
}
|
||||
arm_output_confirmation(state, known_good, previous);
|
||||
@@ -750,7 +792,10 @@ fn arm_output_confirmation(
|
||||
previous: DesiredOutputConfiguration,
|
||||
manual: ManualOverride,
|
||||
) {
|
||||
let backend = state.direct_backend.as_mut().unwrap();
|
||||
let backend = state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.expect("DRM callbacks require an installed direct backend");
|
||||
if let Some(token) = backend.confirmation_timer.take() {
|
||||
state.loop_handle.remove(token);
|
||||
}
|
||||
@@ -781,7 +826,13 @@ fn arm_output_confirmation(
|
||||
TimeoutAction::Drop
|
||||
},
|
||||
) {
|
||||
Ok(token) => state.direct_backend.as_mut().unwrap().confirmation_timer = Some(token),
|
||||
Ok(token) => {
|
||||
state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.confirmation_timer = Some(token)
|
||||
}
|
||||
Err(error) => {
|
||||
tracing::warn!(%error, "could not arm monitor confirmation; reverting");
|
||||
let _ = confirm_output_configuration(state, false);
|
||||
@@ -809,7 +860,13 @@ pub(crate) fn confirm_output_configuration(
|
||||
));
|
||||
backend.manual_outputs = manual;
|
||||
reconcile_outputs(state, false);
|
||||
if let Some(error) = state.direct_backend.as_ref().unwrap().output_error.clone() {
|
||||
if let Some(error) = state
|
||||
.direct_backend
|
||||
.as_ref()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.output_error
|
||||
.clone()
|
||||
{
|
||||
return Err(error);
|
||||
}
|
||||
}
|
||||
@@ -1256,7 +1313,7 @@ fn redraw(
|
||||
let outputs = state
|
||||
.direct_backend
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.devices
|
||||
.iter()
|
||||
.flat_map(|(node, device)| {
|
||||
@@ -1274,7 +1331,7 @@ fn redraw(
|
||||
let scene = state
|
||||
.direct_backend
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.scene_output(state, output);
|
||||
selected.is_none_or(|selected| selected.contains(output) || selected.contains(&scene))
|
||||
|| state.output_has_animations(&scene)
|
||||
@@ -1293,7 +1350,7 @@ fn redraw(
|
||||
state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.scheduling_metrics
|
||||
.redraw(source, selected.is_none(), requested, outputs.len());
|
||||
arm_animation_timer(state);
|
||||
@@ -1517,7 +1574,11 @@ fn render_output(
|
||||
let damage = damage.map_or_else(
|
||||
|| {
|
||||
vec![smithay::utils::Rectangle::from_size(
|
||||
output.output.current_mode().unwrap().size,
|
||||
output
|
||||
.output
|
||||
.current_mode()
|
||||
.expect("a rendered output must have a current mode")
|
||||
.size,
|
||||
)]
|
||||
},
|
||||
|damage| {
|
||||
@@ -1641,7 +1702,13 @@ fn render_output(
|
||||
restore_device(state, node, device);
|
||||
if !submitted
|
||||
&& scene_output
|
||||
== state.direct_backend.as_ref().unwrap().devices[&node].outputs[&crtc].output
|
||||
== state
|
||||
.direct_backend
|
||||
.as_ref()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.devices[&node]
|
||||
.outputs[&crtc]
|
||||
.output
|
||||
{
|
||||
schedule_frame_callbacks(state, node, crtc, plan.present_at);
|
||||
}
|
||||
@@ -1687,7 +1754,11 @@ fn rescan_device(state: &mut Ferese, node: DrmNode) -> Result<bool, ()> {
|
||||
.and_then(|scan| transaction::apply_device(state, node, &mut device, scan));
|
||||
if let Err(error) = &result {
|
||||
tracing::warn!(?node, %error, "output transaction failed; restored known-good outputs where possible");
|
||||
state.direct_backend.as_mut().unwrap().output_error = Some(error.clone());
|
||||
state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.output_error = Some(error.clone());
|
||||
transaction::update_applied_info(&mut device);
|
||||
}
|
||||
restore_device(state, node, device);
|
||||
@@ -1695,7 +1766,10 @@ fn rescan_device(state: &mut Ferese, node: DrmNode) -> Result<bool, ()> {
|
||||
}
|
||||
|
||||
fn refresh_connected_info(state: &mut Ferese) {
|
||||
let backend = state.direct_backend.as_mut().unwrap();
|
||||
let backend = state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.expect("DRM callbacks require an installed direct backend");
|
||||
for device in backend.devices.values_mut() {
|
||||
if let Ok(scan) = select_outputs(
|
||||
device,
|
||||
@@ -1739,7 +1813,10 @@ fn refresh_connected_info(state: &mut Ferese) {
|
||||
}
|
||||
|
||||
fn remember_applied_configuration(state: &mut Ferese) {
|
||||
let backend = state.direct_backend.as_mut().unwrap();
|
||||
let backend = state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.expect("DRM callbacks require an installed direct backend");
|
||||
if backend.topology.reconciling {
|
||||
// Applied positions are read only after the coherent logical publish.
|
||||
backend.topology.remember_applied = true;
|
||||
@@ -1913,17 +1990,20 @@ fn animation_fallbacks(state: &mut Ferese) -> Vec<(DrmNode, crtc::Handle, Instan
|
||||
let scene = state
|
||||
.direct_backend
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.scene_output(state, &identity);
|
||||
let animating = enabled && state.output_has_animations(&scene);
|
||||
let backend = state.direct_backend.as_mut().unwrap();
|
||||
let backend = state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.expect("DRM callbacks require an installed direct backend");
|
||||
let output = backend
|
||||
.devices
|
||||
.get_mut(&node)
|
||||
.unwrap()
|
||||
.expect("the selected DRM device remains registered during this operation")
|
||||
.outputs
|
||||
.get_mut(&crtc)
|
||||
.unwrap();
|
||||
.expect("the selected DRM device remains registered during this operation");
|
||||
// Preserve recovery of a requested final frame if another output
|
||||
// advances this animation to rest before the watchdog is dispatched.
|
||||
let active = enabled
|
||||
@@ -2032,19 +2112,21 @@ fn arm_animation_timer(state: &mut Ferese) {
|
||||
state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.scheduling_metrics
|
||||
.fallback_recoveries += 1;
|
||||
state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.devices
|
||||
.get_mut(&node)
|
||||
.unwrap()
|
||||
.expect(
|
||||
"the selected DRM device must remain registered during this operation",
|
||||
)
|
||||
.outputs
|
||||
.get_mut(&crtc)
|
||||
.unwrap()
|
||||
.expect("the selected CRTC must remain registered during this operation")
|
||||
.animation_fallback = AnimationFallback::default();
|
||||
request_frame(state, node, crtc);
|
||||
}
|
||||
@@ -2054,7 +2136,10 @@ fn arm_animation_timer(state: &mut Ferese) {
|
||||
TimeoutAction::Drop
|
||||
}) {
|
||||
Ok(token) => {
|
||||
let backend = state.direct_backend.as_mut().unwrap();
|
||||
let backend = state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.expect("DRM callbacks require an installed direct backend");
|
||||
backend.animation_timer = Some(token);
|
||||
backend.animation_deadline = Some(deadline);
|
||||
backend.scheduling_metrics.fallback_arms += 1;
|
||||
|
||||
@@ -102,7 +102,11 @@ pub(super) fn capture_mirror(
|
||||
continue;
|
||||
};
|
||||
let element = super::mirror::fitted_texture(
|
||||
output.mirror_capture_texture.as_ref().unwrap().clone(),
|
||||
output
|
||||
.mirror_capture_texture
|
||||
.as_ref()
|
||||
.expect("successful mirror composition populated its capture texture")
|
||||
.clone(),
|
||||
&output.output,
|
||||
smithay::backend::renderer::element::Id::new(),
|
||||
Default::default(),
|
||||
|
||||
@@ -148,7 +148,11 @@ fn read_acpi() -> Option<bool> {
|
||||
|
||||
impl Reader {
|
||||
pub fn set_active(&mut self, active: bool) {
|
||||
let generation = self.lease.lock().unwrap().activate(active);
|
||||
let generation = self
|
||||
.lease
|
||||
.lock()
|
||||
.expect("lid policy lease must remain consistent across workers")
|
||||
.activate(active);
|
||||
self.suspend_requested = false;
|
||||
if active {
|
||||
let _ = self.suspend.send(PolicyRequest::Activate(generation));
|
||||
@@ -167,7 +171,9 @@ impl Reader {
|
||||
let Ok(connection) = connection else { return };
|
||||
while let Ok(request) = suspend_requests.recv() {
|
||||
if let PolicyRequest::Activate(generation) = request {
|
||||
let current = worker_lease.lock().unwrap();
|
||||
let current = worker_lease
|
||||
.lock()
|
||||
.expect("lid policy lease must remain consistent across workers");
|
||||
if !current.active || current.generation != generation {
|
||||
continue;
|
||||
}
|
||||
@@ -189,14 +195,17 @@ impl Reader {
|
||||
reply.body().deserialize::<zbus::zvariant::OwnedFd>()
|
||||
});
|
||||
match inhibitor {
|
||||
Ok(descriptor) => {
|
||||
worker_lease.lock().unwrap().accept(generation, descriptor)
|
||||
}
|
||||
Ok(descriptor) => worker_lease
|
||||
.lock()
|
||||
.expect("lid policy lease must remain consistent across workers")
|
||||
.accept(generation, descriptor),
|
||||
Err(error) => tracing::warn!(%error, "could not own logind lid policy"),
|
||||
}
|
||||
continue;
|
||||
}
|
||||
let current = worker_lease.lock().unwrap();
|
||||
let current = worker_lease
|
||||
.lock()
|
||||
.expect("lid policy lease must remain consistent across workers");
|
||||
if !current.active || !current.suspend {
|
||||
continue;
|
||||
}
|
||||
@@ -221,7 +230,9 @@ impl Reader {
|
||||
tracing::debug!("lid suspend prevented by sleep inhibitor");
|
||||
continue;
|
||||
}
|
||||
let current = worker_lease.lock().unwrap();
|
||||
let current = worker_lease
|
||||
.lock()
|
||||
.expect("lid policy lease must remain consistent across workers");
|
||||
let requested = current.active && current.suspend;
|
||||
drop(current);
|
||||
if !requested {
|
||||
@@ -237,7 +248,10 @@ impl Reader {
|
||||
tracing::warn!(%error, "logind rejected lid suspend");
|
||||
}
|
||||
}
|
||||
worker_lease.lock().unwrap().activate(false);
|
||||
worker_lease
|
||||
.lock()
|
||||
.expect("lid policy lease must remain consistent across workers")
|
||||
.activate(false);
|
||||
})?;
|
||||
let (requests, receiver) = mpsc::channel::<Probe>();
|
||||
let (replies, events) = smithay::reexports::calloop::channel::channel();
|
||||
@@ -306,7 +320,12 @@ fn request(state: &mut Ferese, reactivate: bool, wake: bool) {
|
||||
}
|
||||
}
|
||||
backend.lid_reader.suspend_requested = false;
|
||||
backend.lid_reader.lease.lock().unwrap().suspend = false;
|
||||
backend
|
||||
.lid_reader
|
||||
.lease
|
||||
.lock()
|
||||
.expect("lid policy lease must remain consistent across workers")
|
||||
.suspend = false;
|
||||
let probe = backend.lid.begin(reactivate, wake);
|
||||
let sent = backend.lid_reader.requests.send(probe).is_ok();
|
||||
let timer = state.loop_handle.insert_source(
|
||||
@@ -322,7 +341,12 @@ fn request(state: &mut Ferese, reactivate: bool, wake: bool) {
|
||||
);
|
||||
|
||||
if sent && let Ok(token) = timer {
|
||||
state.direct_backend.as_mut().unwrap().lid_reader.deadline = Some(token);
|
||||
state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.lid_reader
|
||||
.deadline = Some(token);
|
||||
} else {
|
||||
if let Ok(token) = timer {
|
||||
state.loop_handle.remove(token);
|
||||
@@ -352,7 +376,12 @@ fn complete_refresh(state: &mut Ferese, probe: Probe, value: Option<bool>) {
|
||||
backend.topology.reconciling = true;
|
||||
state.reset_animation_clock();
|
||||
state.lock_input_activity();
|
||||
state.direct_backend.as_mut().unwrap().topology.reconciling = false;
|
||||
state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.topology
|
||||
.reconciling = false;
|
||||
}
|
||||
|
||||
reconcile_outputs(state, reactivate);
|
||||
@@ -364,7 +393,12 @@ pub(super) fn apply_suspend_policy(state: &mut Ferese) {
|
||||
return;
|
||||
};
|
||||
let requested = backend.active && backend.desired_outputs.suspend && backend.lid.closed();
|
||||
backend.lid_reader.lease.lock().unwrap().suspend = requested;
|
||||
backend
|
||||
.lid_reader
|
||||
.lease
|
||||
.lock()
|
||||
.expect("lid policy lease must remain consistent across workers")
|
||||
.suspend = requested;
|
||||
if backend.lid_reader.suspend_requested != requested {
|
||||
backend.lid_reader.suspend_requested = requested;
|
||||
let _ = backend.lid_reader.suspend.send(PolicyRequest::Suspend);
|
||||
|
||||
@@ -131,7 +131,14 @@ pub(super) fn cursor_on_output(state: &crate::Ferese, output: &smithay::output::
|
||||
states
|
||||
.data_map
|
||||
.get::<CursorImageSurfaceData>()
|
||||
.map(|attributes| attributes.lock().unwrap().hotspot)
|
||||
.map(|attributes| {
|
||||
attributes
|
||||
.lock()
|
||||
.expect(
|
||||
"cursor surface attributes must remain consistent across callbacks",
|
||||
)
|
||||
.hotspot
|
||||
})
|
||||
.unwrap_or_default()
|
||||
});
|
||||
smithay::desktop::utils::bbox_from_surface_tree(
|
||||
|
||||
@@ -23,13 +23,21 @@ impl<S: Session> SessionDevice<S> {
|
||||
}
|
||||
|
||||
pub fn device_fd(&self) -> io::Result<DeviceFd> {
|
||||
self.fd.as_ref().unwrap().try_clone().map(DeviceFd::from)
|
||||
self.fd
|
||||
.as_ref()
|
||||
.expect("the session device retains its descriptor until Drop")
|
||||
.try_clone()
|
||||
.map(DeviceFd::from)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Session> Drop for SessionDevice<S> {
|
||||
fn drop(&mut self) {
|
||||
if let Err(error) = self.session.close(self.fd.take().unwrap()) {
|
||||
if let Err(error) = self.session.close(
|
||||
self.fd
|
||||
.take()
|
||||
.expect("the session device retains its descriptor until Drop"),
|
||||
) {
|
||||
tracing::warn!(
|
||||
?error,
|
||||
"could not release DRM device through the session manager"
|
||||
|
||||
@@ -176,11 +176,11 @@ fn slot_mut<'a>(state: &'a mut Ferese, path: &Path) -> &'a mut DeviceState {
|
||||
state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.topology
|
||||
.devices
|
||||
.get_mut(path)
|
||||
.unwrap()
|
||||
.expect("the discovery path must have a topology slot")
|
||||
}
|
||||
|
||||
/// The only entry point for applying the current output configuration to DRM.
|
||||
@@ -250,7 +250,12 @@ pub(super) fn reconcile_outputs(state: &mut Ferese, reactivate: bool) {
|
||||
}
|
||||
|
||||
for path in paths {
|
||||
let slot = &state.direct_backend.as_ref().unwrap().topology.devices[&path];
|
||||
let slot = &state
|
||||
.direct_backend
|
||||
.as_ref()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.topology
|
||||
.devices[&path];
|
||||
let current_path = observed
|
||||
.as_ref()
|
||||
.ok()
|
||||
@@ -261,7 +266,7 @@ pub(super) fn reconcile_outputs(state: &mut Ferese, reactivate: bool) {
|
||||
state
|
||||
.direct_backend
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.devices
|
||||
.contains_key(node)
|
||||
});
|
||||
@@ -291,7 +296,10 @@ pub(super) fn reconcile_outputs(state: &mut Ferese, reactivate: bool) {
|
||||
}
|
||||
let node = match existing {
|
||||
Some(node) => node,
|
||||
None => match open_device(state, current_path.unwrap()) {
|
||||
None => match open_device(
|
||||
state,
|
||||
current_path.expect("device discovery found a path before attempting to open it"),
|
||||
) {
|
||||
Ok(node) => node,
|
||||
Err(error) => {
|
||||
tracing::warn!(%error, ?path, device_path = ?current_path, "DRM device unavailable; will retry");
|
||||
@@ -306,10 +314,10 @@ pub(super) fn reconcile_outputs(state: &mut Ferese, reactivate: bool) {
|
||||
let device = state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.devices
|
||||
.get_mut(&node)
|
||||
.unwrap();
|
||||
.expect("the selected DRM device must remain registered during this operation");
|
||||
device.drm.activate(true).map(|()| {
|
||||
for (crtc, output) in &mut device.outputs {
|
||||
cancel_output_timers(&state.loop_handle, output);
|
||||
@@ -325,7 +333,7 @@ pub(super) fn reconcile_outputs(state: &mut Ferese, reactivate: bool) {
|
||||
state
|
||||
.desktop_transition
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.expect("desktop transition must be started before publication or retirement")
|
||||
.redraw
|
||||
.insert(output.output.clone());
|
||||
|
||||
@@ -350,13 +358,13 @@ pub(super) fn reconcile_outputs(state: &mut Ferese, reactivate: bool) {
|
||||
let mut output = state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.devices
|
||||
.get_mut(&node)
|
||||
.unwrap()
|
||||
.expect("the selected DRM device must remain registered during this operation")
|
||||
.outputs
|
||||
.remove(&crtc)
|
||||
.unwrap();
|
||||
.expect("the selected CRTC must remain registered during this operation");
|
||||
cancel_output_timers(&state.loop_handle, &mut output);
|
||||
if let Some(global) = output.global.take() {
|
||||
state.display_handle.disable_global::<Ferese>(global);
|
||||
@@ -365,7 +373,7 @@ pub(super) fn reconcile_outputs(state: &mut Ferese, reactivate: bool) {
|
||||
state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.presentation
|
||||
.remove(&(node, crtc));
|
||||
}
|
||||
@@ -376,7 +384,7 @@ pub(super) fn reconcile_outputs(state: &mut Ferese, reactivate: bool) {
|
||||
let known_good = state
|
||||
.direct_backend
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.applied_outputs
|
||||
.clone();
|
||||
if let Err(error) = refresh_monitor_policy(state).and_then(|()| {
|
||||
@@ -384,14 +392,21 @@ pub(super) fn reconcile_outputs(state: &mut Ferese, reactivate: bool) {
|
||||
validate_desired_outputs(state)
|
||||
}) {
|
||||
tracing::warn!(%error, "monitor policy validation failed; retaining applied outputs");
|
||||
state.direct_backend.as_mut().unwrap().output_error = Some(error);
|
||||
state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.output_error = Some(error);
|
||||
for (path, node) in &ready {
|
||||
slot_mut(state, path).degraded(*node, Instant::now());
|
||||
}
|
||||
} else {
|
||||
// Activate destinations before disabling former owners, including across GPUs.
|
||||
ready.sort_by_key(|(_, node)| {
|
||||
let backend = state.direct_backend.as_ref().unwrap();
|
||||
let backend = state
|
||||
.direct_backend
|
||||
.as_ref()
|
||||
.expect("DRM callbacks require an installed direct backend");
|
||||
let prefix = format!("{}/", drm_device_key(&backend.devices[node].drm));
|
||||
let desired = backend
|
||||
.desired_outputs
|
||||
@@ -416,14 +431,21 @@ pub(super) fn reconcile_outputs(state: &mut Ferese, reactivate: bool) {
|
||||
// Device boundaries are not cross-GPU atomic. Compensate already
|
||||
// applied devices using their last actual hardware settings.
|
||||
if !known_good.outputs.is_empty() {
|
||||
let backend = state.direct_backend.as_mut().unwrap();
|
||||
let backend = state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.expect("DRM callbacks require an installed direct backend");
|
||||
let requested = backend.desired_outputs.clone();
|
||||
backend.desired_outputs =
|
||||
crate::output_policy::restore_configuration(&known_good, &backend.monitors);
|
||||
for previous in succeeded {
|
||||
let _ = rescan_device(state, previous);
|
||||
}
|
||||
state.direct_backend.as_mut().unwrap().desired_outputs = requested;
|
||||
state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.desired_outputs = requested;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -431,7 +453,7 @@ pub(super) fn reconcile_outputs(state: &mut Ferese, reactivate: bool) {
|
||||
if state
|
||||
.direct_backend
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.output_error
|
||||
.is_none()
|
||||
{
|
||||
@@ -441,14 +463,24 @@ pub(super) fn reconcile_outputs(state: &mut Ferese, reactivate: bool) {
|
||||
|
||||
transaction::recover_last_output(state);
|
||||
let mut desktop_changes = publish_runtime_desktop(state);
|
||||
if std::mem::take(&mut state.direct_backend.as_mut().unwrap().topology.layout_dirty) {
|
||||
if std::mem::take(
|
||||
&mut state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.topology
|
||||
.layout_dirty,
|
||||
) {
|
||||
desktop_changes.layout = true;
|
||||
desktop_changes.shell = true;
|
||||
desktop_changes
|
||||
.outputs
|
||||
.extend(state.space.outputs().cloned());
|
||||
}
|
||||
let backend = state.direct_backend.as_mut().unwrap();
|
||||
let backend = state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.expect("DRM callbacks require an installed direct backend");
|
||||
backend.topology.reconciling = false;
|
||||
let retry = backend.topology.retry_deadline(backend.active);
|
||||
if std::mem::take(&mut backend.topology.remember_applied) {
|
||||
@@ -465,7 +497,14 @@ pub(super) fn reconcile_outputs(state: &mut Ferese, reactivate: bool) {
|
||||
reconcile_outputs(state, false);
|
||||
TimeoutAction::Drop
|
||||
}) {
|
||||
Ok(token) => state.direct_backend.as_mut().unwrap().topology.retry_timer = Some(token),
|
||||
Ok(token) => {
|
||||
state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.topology
|
||||
.retry_timer = Some(token)
|
||||
}
|
||||
Err(error) => tracing::warn!(%error, "could not arm DRM recovery deadline"),
|
||||
}
|
||||
}
|
||||
@@ -481,7 +520,7 @@ fn publish_runtime_desktop(state: &mut Ferese) -> crate::state::DesktopChanges {
|
||||
let mut outputs = state
|
||||
.direct_backend
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.devices
|
||||
.values()
|
||||
.flat_map(|device| device.outputs.values())
|
||||
@@ -525,7 +564,13 @@ fn publish_runtime_desktop(state: &mut Ferese) -> crate::state::DesktopChanges {
|
||||
let changes = state
|
||||
.publish_desktop(runtime)
|
||||
.expect("validated usable output inventory");
|
||||
for device in state.direct_backend.as_mut().unwrap().devices.values_mut() {
|
||||
for device in state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.devices
|
||||
.values_mut()
|
||||
{
|
||||
for output in device.outputs.values_mut() {
|
||||
if output.mirror_source.is_none() {
|
||||
if output.global.is_none() {
|
||||
@@ -579,7 +624,14 @@ pub(super) fn hotplug(state: &mut Ferese) {
|
||||
reconcile_outputs(state, false);
|
||||
TimeoutAction::Drop
|
||||
}) {
|
||||
Ok(token) => state.direct_backend.as_mut().unwrap().topology.settle_timer = Some(token),
|
||||
Ok(token) => {
|
||||
state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.topology
|
||||
.settle_timer = Some(token)
|
||||
}
|
||||
Err(error) => {
|
||||
tracing::warn!(%error, "could not debounce output events");
|
||||
reconcile_outputs(state, false);
|
||||
|
||||
@@ -257,7 +257,7 @@ fn retire(state: &mut Ferese, output: &mut DirectOutput, node: DrmNode, crtc: cr
|
||||
state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.presentation
|
||||
.remove(&(node, crtc));
|
||||
}
|
||||
@@ -296,7 +296,7 @@ impl crate::output_transaction::HardwareTransaction for DeviceTransaction<'_> {
|
||||
.staged
|
||||
.get_mut(&crtc)
|
||||
.or_else(|| self.device.outputs.get_mut(&crtc))
|
||||
.unwrap();
|
||||
.expect("the selected CRTC has a live or staged output in this transaction");
|
||||
output
|
||||
.surface
|
||||
.set_connectors(&[selection.connector.handle()])
|
||||
@@ -317,8 +317,15 @@ impl crate::output_transaction::HardwareTransaction for DeviceTransaction<'_> {
|
||||
// set_connectors: each tests against the other's old value. Stage a
|
||||
// replacement surface, retire the old one *before* its first commit
|
||||
// (Smithay clears the CRTC on surface drop), then publish metadata.
|
||||
let replacement = self.staged.remove(&crtc).unwrap();
|
||||
let previous = self.device.outputs.get_mut(&crtc).unwrap();
|
||||
let replacement = self
|
||||
.staged
|
||||
.remove(&crtc)
|
||||
.expect("the selected CRTC has a live or staged output in this transaction");
|
||||
let previous = self
|
||||
.device
|
||||
.outputs
|
||||
.get_mut(&crtc)
|
||||
.expect("the selected CRTC has a live or staged output in this transaction");
|
||||
previous.surface = replacement.surface;
|
||||
previous.dmabuf_feedback = replacement.dmabuf_feedback;
|
||||
self.replaced.insert(crtc);
|
||||
@@ -326,7 +333,7 @@ impl crate::output_transaction::HardwareTransaction for DeviceTransaction<'_> {
|
||||
self.staged
|
||||
.get_mut(&crtc)
|
||||
.or_else(|| self.device.outputs.get_mut(&crtc))
|
||||
.unwrap()
|
||||
.expect("the selected CRTC has a live or staged output in this transaction")
|
||||
.surface
|
||||
.commit_frame()
|
||||
.map_err(|error| error.to_string())
|
||||
@@ -336,7 +343,7 @@ impl crate::output_transaction::HardwareTransaction for DeviceTransaction<'_> {
|
||||
self.device
|
||||
.outputs
|
||||
.get_mut(&crtc)
|
||||
.unwrap()
|
||||
.expect("the selected CRTC has a live or staged output in this transaction")
|
||||
.surface
|
||||
.clear()
|
||||
.map_err(|error| error.to_string())
|
||||
@@ -384,7 +391,11 @@ impl crate::output_transaction::HardwareTransaction for DeviceTransaction<'_> {
|
||||
&replacement.settings,
|
||||
)
|
||||
.map_err(|error| error.to_string())?;
|
||||
let previous = self.device.outputs.get_mut(&crtc).unwrap();
|
||||
let previous = self
|
||||
.device
|
||||
.outputs
|
||||
.get_mut(&crtc)
|
||||
.expect("the selected CRTC has a live or staged output in this transaction");
|
||||
previous.surface = replacement.surface;
|
||||
previous.dmabuf_feedback = replacement.dmabuf_feedback;
|
||||
previous
|
||||
@@ -426,7 +437,7 @@ impl crate::output_transaction::HardwareTransaction for DeviceTransaction<'_> {
|
||||
self.state
|
||||
.desktop_transition
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.expect("desktop transition must be started before publication or retirement")
|
||||
.redraw
|
||||
.insert(output.output.clone());
|
||||
output.lock_frame_pending = false;
|
||||
@@ -504,7 +515,7 @@ pub(super) fn apply_device(
|
||||
let mut output = staged
|
||||
.remove(crtc)
|
||||
.or_else(|| device.outputs.remove(crtc))
|
||||
.unwrap();
|
||||
.expect("the selected CRTC has a live or staged output in this transaction");
|
||||
if output.connector != selection.connector.handle() || output.identity != selection.identity
|
||||
{
|
||||
retire(state, &mut output, node, *crtc);
|
||||
@@ -551,7 +562,7 @@ pub(super) fn apply_device(
|
||||
let clock = state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.presentation
|
||||
.entry((node, *crtc))
|
||||
.or_default();
|
||||
@@ -561,7 +572,10 @@ pub(super) fn apply_device(
|
||||
}
|
||||
// Retire resources now; the final inventory determines evacuation once.
|
||||
for crtc in removals {
|
||||
let mut output = device.outputs.remove(&crtc).unwrap();
|
||||
let mut output = device
|
||||
.outputs
|
||||
.remove(&crtc)
|
||||
.expect("the selected CRTC has a live or staged output in this transaction");
|
||||
retire(state, &mut output, node, crtc);
|
||||
}
|
||||
device.connected_outputs = scan.connected_outputs;
|
||||
@@ -592,7 +606,7 @@ pub(super) fn retire_disconnected(state: &mut Ferese) {
|
||||
let missing = state
|
||||
.direct_backend
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.devices
|
||||
.iter()
|
||||
.flat_map(|(node, device)| {
|
||||
@@ -610,10 +624,10 @@ pub(super) fn retire_disconnected(state: &mut Ferese) {
|
||||
if let Some(mut output) = state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.devices
|
||||
.get_mut(&node)
|
||||
.unwrap()
|
||||
.expect("the selected DRM device must remain registered during this operation")
|
||||
.outputs
|
||||
.remove(&crtc)
|
||||
{
|
||||
@@ -629,7 +643,7 @@ pub(super) fn recover_last_output(state: &mut Ferese) {
|
||||
if state
|
||||
.direct_backend
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.devices
|
||||
.values()
|
||||
.any(|device| {
|
||||
@@ -641,7 +655,10 @@ pub(super) fn recover_last_output(state: &mut Ferese) {
|
||||
{
|
||||
return;
|
||||
}
|
||||
let backend = state.direct_backend.as_ref().unwrap();
|
||||
let backend = state
|
||||
.direct_backend
|
||||
.as_ref()
|
||||
.expect("DRM callbacks require an installed direct backend");
|
||||
let requested = backend.desired_outputs.clone();
|
||||
let original_error = backend.output_error.clone();
|
||||
let mut candidates = backend
|
||||
@@ -652,7 +669,10 @@ pub(super) fn recover_last_output(state: &mut Ferese) {
|
||||
.collect::<Vec<_>>();
|
||||
candidates.sort_by_key(|monitor| (!monitor.internal, monitor.key.clone()));
|
||||
for candidate in candidates {
|
||||
let backend = state.direct_backend.as_mut().unwrap();
|
||||
let backend = state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.expect("DRM callbacks require an installed direct backend");
|
||||
backend.desired_outputs = DesiredOutputConfiguration {
|
||||
profile: None,
|
||||
suspend: requested.suspend,
|
||||
@@ -687,7 +707,7 @@ pub(super) fn recover_last_output(state: &mut Ferese) {
|
||||
if state
|
||||
.direct_backend
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.devices
|
||||
.values()
|
||||
.any(|device| {
|
||||
@@ -698,13 +718,20 @@ pub(super) fn recover_last_output(state: &mut Ferese) {
|
||||
})
|
||||
{
|
||||
remember_applied_configuration(state);
|
||||
state.direct_backend.as_mut().unwrap().output_error = original_error;
|
||||
state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.expect("DRM callbacks require an installed direct backend")
|
||||
.output_error = original_error;
|
||||
tracing::warn!(identity = %candidate.identity, "recovered last usable output with preferred mode");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
let backend = state.direct_backend.as_mut().unwrap();
|
||||
let backend = state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
.expect("DRM callbacks require an installed direct backend");
|
||||
backend.desired_outputs = requested;
|
||||
backend.output_error = original_error;
|
||||
}
|
||||
Reference in new issue
Block a user