Explain runtime invariants and avoid redundant unwraps

This commit is contained in:
Samuel Onoja committed 2026-10-22 01:28:03 +01:00
1 parent 34f2f07301
commit 1b51ba83b4
116 files changed
+1643 -515

No files matched your search

+4 -1
View File
@@ -30,7 +30,10 @@ impl Publisher {
serde_json::json!(job.snapshot.as_ref()),
));
}
let value = &cached.as_ref().unwrap().1;
let value = &cached
.as_ref()
.expect("activity serialization populated the cache for this revision")
.1;
// Lock acquisition revokes queued/in-flight serialization.
// Hold the guard through delivery to order sends before revocation.
deliver(job.epoch, &worker_epoch, job.recipients, value);
+116 -31
View File
@@ -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;
+5 -1
View File
@@ -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(),
+46 -12
View File
@@ -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);
+8 -1
View File
@@ -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"
+76 -24
View File
@@ -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);
+47 -20
View File
@@ -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;
}
+5 -2
View File
@@ -115,8 +115,11 @@ impl Config {
theme: &ferese_config::theme::ResolvedTheme,
) -> Result<ThemeSettings, ConfigError> {
let config = Self {
theme: serde_json::from_value(serde_json::to_value(&theme.tokens).unwrap())
.expect("resolved theme matches runtime schema"),
theme: serde_json::from_value(
serde_json::to_value(&theme.tokens)
.expect("typed configuration and IPC values must serialize to JSON"),
)
.expect("resolved theme matches runtime schema"),
..Self::default()
};
let mut settings = config.theme_settings()?;
+33 -11
View File
@@ -132,14 +132,20 @@ impl Loader {
loop {
let (requests, ready) = &*worker;
let mut requests = ready
.wait_while(requests.lock().unwrap(), |requests| {
!requests.closed && requests.pending.is_none()
})
.unwrap();
.wait_while(
requests.lock().expect(
"cursor decode queue must remain consistent across workers",
),
|requests| !requests.closed && requests.pending.is_none(),
)
.expect("cursor decode queue must remain consistent across workers");
if requests.closed {
break;
}
let request = requests.pending.take().unwrap();
let request = requests
.pending
.take()
.expect("the decode wait completed with a pending cursor request");
requests.in_flight = Some(request);
drop(requests);
let cursor = load(request);
@@ -155,7 +161,11 @@ impl Loader {
break;
}
signal.wakeup();
worker.0.lock().unwrap().in_flight = None;
worker
.0
.lock()
.expect("cursor decode queue must remain consistent across workers")
.in_flight = None;
}
})?;
Ok(Self { shared, results })
@@ -163,7 +173,11 @@ impl Loader {
pub(crate) fn request(&self, icon: CursorIcon, scale: i32) {
let request = (icon, scale);
let mut requests = self.shared.0.lock().unwrap();
let mut requests = self
.shared
.0
.lock()
.expect("cursor decode queue must remain consistent across workers");
if requests.in_flight == Some(request) || requests.pending == Some(request) {
return;
}
@@ -174,7 +188,11 @@ impl Loader {
impl Drop for Loader {
fn drop(&mut self) {
let mut requests = self.shared.0.lock().unwrap();
let mut requests = self
.shared
.0
.lock()
.expect("cursor decode queue must remain consistent across workers");
requests.closed = true;
requests.pending = None;
self.shared.1.notify_one();
@@ -256,7 +274,11 @@ impl crate::Ferese {
}) else {
break;
};
bytes -= self.named_cursors.remove(&evict).unwrap().bytes;
bytes -= self
.named_cursors
.remove(&evict)
.expect("cursor selection retains the requested or default cursor")
.bytes;
}
self.named_cursors.insert(icon, cursor);
if affects_current {
@@ -299,12 +321,12 @@ impl crate::Ferese {
if !visible {
return;
}
let icon = icon.unwrap();
let icon = icon.expect("the hidden-cursor branch returned before image selection");
let cursor = self
.named_cursors
.get(&icon)
.or_else(|| self.named_cursors.get(&CursorIcon::Default))
.unwrap();
.expect("cursor selection retains the requested or default cursor");
let (frame, remaining) =
cursor.sample(now.saturating_duration_since(self.cursor_animation.epoch));
if self.cursor_animation.frame != frame {
+4 -1
View File
@@ -66,7 +66,10 @@ impl<K: Eq, C: Eq, T> Queue<K, C, T> {
mut complete: impl FnMut(Pending<K, C, T>),
) {
for _ in 0..self.pending.len() {
let entry = self.pending.pop_front().unwrap();
let entry = self
.pending
.pop_front()
.expect("the bounded drain removes at most the original queue length");
if remove(&entry) {
complete(entry);
} else {
+110 -27
View File
@@ -109,22 +109,52 @@ impl SurfaceEffectsState {
}
fn commit_presentation(&self) {
let Some(pending) = self.pending.lock().unwrap().take() else {
let Some(pending) = self
.pending
.lock()
.expect("surface effect state must not be used after a mutator panics")
.take()
else {
return;
};
*self.role.lock().unwrap() = Some(pending.role);
*self.panel.lock().unwrap() = pending.panel;
*self.regions.lock().unwrap() = Some(pending.regions);
*self.region_opacities.lock().unwrap() = pending.region_opacities;
*self
.role
.lock()
.expect("surface effect state must not be used after a mutator panics") =
Some(pending.role);
*self
.panel
.lock()
.expect("surface effect state must not be used after a mutator panics") = pending.panel;
*self
.regions
.lock()
.expect("surface effect state must not be used after a mutator panics") =
Some(pending.regions);
*self
.region_opacities
.lock()
.expect("surface effect state must not be used after a mutator panics") =
pending.region_opacities;
if !self.dismissing.load(Ordering::Acquire) {
*self.opacity.lock().unwrap() = pending.opacity;
*self
.opacity
.lock()
.expect("surface effect state must not be used after a mutator panics") =
pending.opacity;
}
self.generation.fetch_add(1, Ordering::Release);
}
fn set_role(&self, role: Option<SemanticRole>) -> bool {
self.pending.lock().unwrap().take();
let mut current = self.role.lock().unwrap();
self.pending
.lock()
.expect("surface effect state must not be used after a mutator panics")
.take();
let mut current = self
.role
.lock()
.expect("surface effect state must not be used after a mutator panics");
if *current != role {
*current = role;
self.generation.fetch_add(1, Ordering::Release);
@@ -144,7 +174,11 @@ impl SurfaceEffectsUserData {
}
fn surface(&self) -> Option<WlSurface> {
self.0.lock().unwrap().upgrade().ok()
self.0
.lock()
.expect("surface effect state must not be used after a mutator panics")
.upgrade()
.ok()
}
}
@@ -182,7 +216,10 @@ impl Dispatch<FereseMaterialManagerV1, ()> for Ferese {
states
.data_map
.insert_if_missing_threadsafe(SurfaceEffectsState::new);
let effects = states.data_map.get::<SurfaceEffectsState>().unwrap();
let effects = states
.data_map
.get::<SurfaceEffectsState>()
.expect("effects state was inserted before this access");
effects.attached.swap(true, Ordering::AcqRel)
});
if attached {
@@ -248,7 +285,7 @@ pub(crate) fn surface_regions(surface: &WlSurface) -> Option<Vec<[f64; 5]>> {
.get::<SurfaceEffectsState>()?
.regions
.lock()
.unwrap()
.expect("surface effect state must not be used after a mutator panics")
.clone()
})
}
@@ -260,7 +297,7 @@ pub(crate) fn surface_panel_presentation(surface: &WlSurface) -> Option<PanelPre
.get::<SurfaceEffectsState>()?
.panel
.lock()
.unwrap()
.expect("surface effect state must not be used after a mutator panics")
.clone()
})
}
@@ -296,7 +333,13 @@ pub(crate) fn surface_region_opacities(surface: &WlSurface) -> Vec<u32> {
states
.data_map
.get::<SurfaceEffectsState>()
.map(|effects| effects.region_opacities.lock().unwrap().clone())
.map(|effects| {
effects
.region_opacities
.lock()
.expect("surface effect state must not be used after a mutator panics")
.clone()
})
.unwrap_or_default()
})
}
@@ -345,7 +388,10 @@ fn decode_regions(bytes: &[u8]) -> Option<Vec<[f64; 5]>> {
pub(crate) fn surface_role(surface: &WlSurface) -> Option<(SemanticRole, u64)> {
with_states(surface, |states| {
let effects = states.data_map.get::<SurfaceEffectsState>()?;
let role = *effects.role.lock().unwrap();
let role = *effects
.role
.lock()
.expect("surface effect state must not be used after a mutator panics");
role.map(|role| (role, effects.generation.load(Ordering::Acquire)))
})
}
@@ -388,7 +434,7 @@ impl Dispatch<FereseEffectsManagerV1, ()> for Ferese {
states
.data_map
.get::<SurfaceEffectsState>()
.unwrap()
.expect("effects state was inserted before this access")
.attached
.swap(true, Ordering::AcqRel)
});
@@ -404,7 +450,7 @@ impl Dispatch<FereseEffectsManagerV1, ()> for Ferese {
states
.data_map
.get::<SurfaceEffectsState>()
.unwrap()
.expect("effects state was inserted before this access")
.presentation_supported
.store(effects.version() >= 3, Ordering::Release)
});
@@ -448,7 +494,9 @@ impl Dispatch<FereseSurfaceEffectsV1, SurfaceEffectsUserData> for Ferese {
}
with_states(&surface, |states| {
if let Some(effects) = states.data_map.get::<SurfaceEffectsState>() {
*effects.pending.lock().unwrap() = Some(PendingPresentation {
*effects.pending.lock().expect(
"surface effect state must not be used after a mutator panics",
) = Some(PendingPresentation {
panel: Some(panel),
role: SemanticRole::Panel,
region_opacities: vec![1000; regions.len()],
@@ -473,7 +521,9 @@ impl Dispatch<FereseSurfaceEffectsV1, SurfaceEffectsUserData> for Ferese {
};
with_states(&surface, |states| {
if let Some(effects) = states.data_map.get::<SurfaceEffectsState>() {
*effects.pending.lock().unwrap() = Some(PendingPresentation {
*effects.pending.lock().expect(
"surface effect state must not be used after a mutator panics",
) = Some(PendingPresentation {
panel: None,
role,
regions,
@@ -491,7 +541,10 @@ impl Dispatch<FereseSurfaceEffectsV1, SurfaceEffectsUserData> for Ferese {
if effects.dismissing.load(Ordering::Acquire) {
return false;
}
let mut current = effects.opacity.lock().unwrap();
let mut current = effects
.opacity
.lock()
.expect("surface effect state must not be used after a mutator panics");
let opacity = opacity.min(1000);
if *current == opacity {
return false;
@@ -520,7 +573,10 @@ impl Dispatch<FereseSurfaceEffectsV1, SurfaceEffectsUserData> for Ferese {
let Some(effects) = states.data_map.get::<SurfaceEffectsState>() else {
return false;
};
let mut old = effects.region_opacities.lock().unwrap();
let mut old = effects
.region_opacities
.lock()
.expect("surface effect state must not be used after a mutator panics");
if *old == opacities {
return false;
}
@@ -540,7 +596,10 @@ impl Dispatch<FereseSurfaceEffectsV1, SurfaceEffectsUserData> for Ferese {
let Some(effects) = states.data_map.get::<SurfaceEffectsState>() else {
return false;
};
let mut old = effects.regions.lock().unwrap();
let mut old = effects
.regions
.lock()
.expect("surface effect state must not be used after a mutator panics");
if old.as_ref() == Some(&regions) {
return false;
}
@@ -606,11 +665,26 @@ fn detach(surface: &WlSurface) -> bool {
return false;
};
let role_changed = effects.set_role(None);
effects.region_opacities.lock().unwrap().clear();
effects.pending.lock().unwrap().take();
effects.panel.lock().unwrap().take();
effects
.region_opacities
.lock()
.expect("surface effect state must not be used after a mutator panics")
.clear();
effects
.pending
.lock()
.expect("surface effect state must not be used after a mutator panics")
.take();
effects
.panel
.lock()
.expect("surface effect state must not be used after a mutator panics")
.take();
effects.dismissing.store(false, Ordering::Release);
let mut opacity = effects.opacity.lock().unwrap();
let mut opacity = effects
.opacity
.lock()
.expect("surface effect state must not be used after a mutator panics");
let opacity_changed = *opacity != 1000;
*opacity = 1000;
let was_attached = effects.attached.swap(false, Ordering::AcqRel);
@@ -624,7 +698,12 @@ pub(crate) fn surface_opacity(surface: &WlSurface) -> f32 {
.data_map
.get::<SurfaceEffectsState>()
.map_or(1.0, |effects| {
*effects.opacity.lock().unwrap() as f32 / 1000.0
*effects
.opacity
.lock()
.expect("surface effect state must not be used after a mutator panics")
as f32
/ 1000.0
})
})
}
@@ -645,7 +724,11 @@ pub(crate) fn begin_surface_dismiss(surface: &WlSurface) -> bool {
pub(crate) fn fade_dismissed_surface(surface: &WlSurface, opacity: f64) {
with_states(surface, |states| {
if let Some(effects) = states.data_map.get::<SurfaceEffectsState>() {
*effects.opacity.lock().unwrap() = (opacity.clamp(0.0, 1.0) * 1000.0).round() as u32;
*effects
.opacity
.lock()
.expect("surface effect state must not be used after a mutator panics") =
(opacity.clamp(0.0, 1.0) * 1000.0).round() as u32;
effects.generation.fetch_add(1, Ordering::Release);
}
});
+18 -5
View File
@@ -213,9 +213,13 @@ impl SaveThread {
loop {
let job = {
let (mutex, ready) = &*worker;
let mut state = mutex.lock().unwrap();
let mut state = mutex.lock().expect(
"screenshot writer queue must remain consistent across workers",
);
while state.pending.is_none() && !state.closed {
state = ready.wait(state).unwrap();
state = ready.wait(state).expect(
"screenshot writer queue must remain consistent across workers",
);
}
match state.pending.take() {
Some(job) => job,
@@ -237,7 +241,10 @@ impl SaveThread {
fn queue(&self, path: PathBuf, memory: Memory) {
let (mutex, ready) = &*self.shared;
mutex.lock().unwrap().pending = Some((path, memory));
mutex
.lock()
.expect("screenshot writer queue must remain consistent across workers")
.pending = Some((path, memory));
ready.notify_one();
}
}
@@ -245,7 +252,10 @@ impl SaveThread {
impl Drop for SaveThread {
fn drop(&mut self) {
let (mutex, ready) = &*self.shared;
mutex.lock().unwrap().closed = true;
mutex
.lock()
.expect("screenshot writer queue must remain consistent across workers")
.closed = true;
ready.notify_one();
// Flush the latest snapshot only during compositor shutdown, after
// the event loop has stopped. Drag completion never joins this thread.
@@ -265,7 +275,10 @@ impl SaveWorker {
if self.worker.is_none() {
self.worker = Some(SaveThread::start(|path, memory| memory.persist(path))?);
}
self.worker.as_ref().unwrap().queue(path, memory.clone());
self.worker
.as_ref()
.expect("screenshot submission initialized the writer before queueing work")
.queue(path, memory.clone());
Ok(())
}
}
+3 -1
View File
@@ -177,7 +177,9 @@ pub fn handle_commit(state: &mut Ferese, surface: &WlSurface) {
std::sync::Mutex::new(None::<(LayoutState, KeyboardInteractivity)>)
});
let (changed, requested_focus) = {
let mut previous = previous.lock().unwrap();
let mut previous = previous
.lock()
.expect("layer configure state must remain consistent across callbacks");
let changed = previous.as_ref().map(|(layout, _)| layout) != Some(&current);
let requested_focus = policy.keyboard_interactivity == KeyboardInteractivity::OnDemand
&& previous
+14 -3
View File
@@ -250,7 +250,10 @@ impl Dispatch<ZwlrScreencopyFrameV1, Arc<FrameData>> for Ferese {
state.queue_screencopy(frame, data, buffer, true);
}
zwlr_screencopy_frame_v1::Request::Destroy => {
data.snapshot.lock().unwrap().take();
data.snapshot
.lock()
.expect("surface rendering state must remain consistent across callbacks")
.take();
}
_ => unreachable!(),
}
@@ -265,11 +268,19 @@ impl Ferese {
buffer: WlBuffer,
with_damage: bool,
) {
if data.snapshot.lock().unwrap().is_some() {
if data
.snapshot
.lock()
.expect("surface rendering state must remain consistent across callbacks")
.is_some()
{
super::window_capture::copy_snapshot(self, frame, data, &buffer);
return;
}
let mut used = data.used.lock().unwrap();
let mut used = data
.used
.lock()
.expect("surface rendering state must remain consistent across callbacks");
if self.session_lock.active() {
frame.failed();
return;
+36 -10
View File
@@ -35,7 +35,9 @@ struct Permit {
impl Permit {
fn reserve(budget: &Arc<Mutex<Budget>>, bytes: usize) -> Option<Self> {
let mut available = budget.lock().unwrap();
let mut available = budget
.lock()
.expect("surface rendering state must remain consistent across callbacks");
if bytes == 0
|| bytes > MAX_BYTES.saturating_sub(available.bytes)
|| available.frames >= MAX_FRAMES
@@ -53,7 +55,10 @@ impl Permit {
impl Drop for Permit {
fn drop(&mut self) {
let mut budget = self.budget.lock().unwrap();
let mut budget = self
.budget
.lock()
.expect("surface rendering state must remain consistent across callbacks");
budget.bytes -= self.bytes;
budget.frames -= 1;
}
@@ -167,21 +172,32 @@ fn finish_conversion(state: &Ferese, result: Conversion) {
let Some(data) = frame.data::<Arc<FrameData>>() else {
return;
};
if !frame.is_alive() || *data.used.lock().unwrap() {
if !frame.is_alive()
|| *data
.used
.lock()
.expect("surface rendering state must remain consistent across callbacks")
{
return;
}
if state.session_lock.active()
|| !super::screencopy::capture_allowed()
|| !state.capture_window_allowed(snapshot.id, snapshot.epoch)
{
*data.used.lock().unwrap() = true;
*data
.used
.lock()
.expect("surface rendering state must remain consistent across callbacks") = true;
frame.failed();
return;
}
let size = (snapshot.pixels.width as u32, snapshot.pixels.height as u32);
let logical_size = snapshot.logical_size;
*data.snapshot.lock().unwrap() = Some(snapshot);
*data
.snapshot
.lock()
.expect("surface rendering state must remain consistent across callbacks") = Some(snapshot);
if manager.is_alive() {
manager.geometry(&frame, logical_size.0, logical_size.1);
}
@@ -256,7 +272,8 @@ impl Dispatch<FereseWindowCaptureManagerV1, Global> for Ferese {
} else if global
.worker
.try_send(Conversion {
snapshot: snapshot.unwrap(),
snapshot: snapshot
.expect("conversion is queued only after obtaining a capture snapshot"),
frame: resource.clone(),
manager: manager.clone(),
})
@@ -264,10 +281,11 @@ impl Dispatch<FereseWindowCaptureManagerV1, Global> for Ferese {
{
*resource
.data::<Arc<FrameData>>()
.unwrap()
.expect("capture frame resources are initialized with their FrameData")
.used
.lock()
.unwrap() = true;
.expect("surface rendering state must remain consistent across callbacks") =
true;
manager.busy(&resource);
resource.failed();
}
@@ -386,7 +404,10 @@ pub(super) fn copy_snapshot(
data: &FrameData,
buffer: &WlBuffer,
) {
let mut used = data.used.lock().unwrap();
let mut used = data
.used
.lock()
.expect("surface rendering state must remain consistent across callbacks");
if *used {
resource.post_error(
frame::Error::AlreadyUsed,
@@ -395,7 +416,12 @@ pub(super) fn copy_snapshot(
return;
}
*used = true;
let Some(snapshot) = data.snapshot.lock().unwrap().take() else {
let Some(snapshot) = data
.snapshot
.lock()
.expect("surface rendering state must remain consistent across callbacks")
.take()
else {
resource.failed();
return;
};
+2 -2
View File
@@ -329,9 +329,9 @@ pub(crate) fn initial_configure_sent(toplevel: &ToplevelSurface) -> bool {
states
.data_map
.get::<XdgToplevelSurfaceData>()
.unwrap()
.expect("an XDG toplevel must retain its Smithay surface data")
.lock()
.unwrap()
.expect("Smithay surface state must remain consistent across callbacks")
.initial_configure_sent
})
}
+1 -1
View File
@@ -142,7 +142,7 @@ impl Ferese {
states
.data_map
.get::<XdgToplevelSurfaceData>()
.is_some_and(|data| data.lock().unwrap().parent.is_some())
.is_some_and(|data| data.lock().expect("Smithay surface metadata must remain consistent across callbacks").parent.is_some())
});
crate::window_rules::resolve(
&self.window_rules,
+11 -3
View File
@@ -245,8 +245,12 @@ async fn action(
.length_us
.map_or(position, |length| position.min(length))
.min(i64::MAX as u64) as i64;
let track = OwnedObjectPath::try_from(view.track_id.clone().unwrap())
.map_err(|error| error.to_string())?;
let track = OwnedObjectPath::try_from(
view.track_id
.clone()
.expect("media action validation requires an active track"),
)
.map_err(|error| error.to_string())?;
connection
.call_method(
Some(player.owner.as_str()),
@@ -262,7 +266,11 @@ async fn action(
.as_f64()
.filter(|delta| delta.is_finite() && delta.abs() <= 1.0)
.ok_or("Invalid volume change")?;
let volume = (view.volume.unwrap() + delta).clamp(0.0, 1.0);
let volume = (view
.volume
.expect("relative volume validation requires the current volume")
+ delta)
.clamp(0.0, 1.0);
connection
.call_method(
Some(player.owner.as_str()),
+16 -3
View File
@@ -1048,7 +1048,10 @@ impl Ferese {
let keyboard = self.seat.get_keyboard().expect("seat has a keyboard");
let modifiers = keyboard.modifier_state();
let raw_symbols = keyboard.with_xkb_state(self, |context| {
let xkb = context.xkb().lock().unwrap();
let xkb = context
.xkb()
.lock()
.expect("keyboard keymap state must remain consistent across callbacks");
xkb.raw_syms_for_key_in_layout(keycode, xkb.active_layout())
.iter()
.map(|symbol| symbol.raw())
@@ -1484,7 +1487,11 @@ fn surface_tree_offset(root: &WlSurface, target: &WlSurface) -> Option<Point<i32
states
.data_map
.get::<RendererSurfaceStateUserData>()
.and_then(|data| data.lock().unwrap().view())
.and_then(|data| {
data.lock()
.expect("Smithay surface metadata must remain consistent across callbacks")
.view()
})
.map_or(TraversalAction::SkipChildren, |view| {
TraversalAction::DoChildren(*location + view.offset)
})
@@ -1494,7 +1501,13 @@ fn surface_tree_offset(root: &WlSurface, target: &WlSurface) -> Option<Point<i32
&& let Some(view) = states
.data_map
.get::<RendererSurfaceStateUserData>()
.and_then(|data| data.lock().unwrap().view())
.and_then(|data| {
data.lock()
.expect(
"Smithay surface metadata must remain consistent across callbacks",
)
.view()
})
{
found.set(Some(*location + view.offset));
}
+23 -6
View File
@@ -174,7 +174,11 @@ impl InputCapture {
self.disable_all();
let ids = self.sessions.keys().copied().collect::<Vec<_>>();
for id in ids {
self.sessions.get_mut(&id).unwrap().barriers.clear();
self.sessions
.get_mut(&id)
.expect("input capture operations validate their session or waiter before mutation")
.barriers
.clear();
self.enqueue(id, json!({"type":"zones", "zone_set":self.zone_set}));
}
self.flush();
@@ -246,7 +250,10 @@ impl InputCapture {
failed.push(barrier.id);
}
}
self.sessions.get_mut(&id).unwrap().barriers = accepted;
self.sessions
.get_mut(&id)
.expect("input capture operations validate their session or waiter before mutation")
.barriers = accepted;
Ok(failed)
}
@@ -303,7 +310,10 @@ impl InputCapture {
self.next_activation = self.next_activation.wrapping_add(1);
let activation = self.next_activation;
self.active = Some(id);
let session = self.sessions.get_mut(&id).unwrap();
let session = self
.sessions
.get_mut(&id)
.expect("input capture operations validate their session or waiter before mutation");
session.activation = Some(activation);
self.enqueue(
id,
@@ -367,7 +377,9 @@ impl InputCapture {
self.active = None;
self.last_absolute = None;
self.restore_focus = true;
let session = self.sessions.get_mut(&id).unwrap();
let session = self.sessions.get_mut(&id).expect(
"input capture operations validate their session or waiter before mutation",
);
let activation = session.activation.take();
self.enqueue(
id,
@@ -380,7 +392,10 @@ impl InputCapture {
pub(crate) fn disable(&mut self, id: u64) -> Result<(), String> {
self.release(id, None)?;
let session = self.sessions.get_mut(&id).unwrap();
let session = self
.sessions
.get_mut(&id)
.expect("input capture operations validate their session or waiter before mutation");
if session.enabled {
session.enabled = false;
self.enqueue(id, json!({"type":"disabled"}));
@@ -436,7 +451,9 @@ impl InputCapture {
})
.collect::<Vec<_>>();
for owner in ready {
let (request, id, response) = self.waiters.remove(&owner).unwrap();
let (request, id, response) = self.waiters.remove(&owner).expect(
"input capture operations validate their session or waiter before mutation",
);
let result = if let Some(session) = self.sessions.get_mut(&id) {
json!({"events":session.events.drain(..).collect::<Vec<_>>()})
} else {
+5 -2
View File
@@ -1106,7 +1106,10 @@ impl Ferese {
self.refresh_input_capture_zones();
let keyboard = self.seat.get_keyboard().expect("seat has keyboard");
let keymap = keyboard.with_xkb_state(self, |state| {
let xkb = state.xkb().lock().unwrap();
let xkb = state
.xkb()
.lock()
.expect("keyboard keymap state must remain consistent across callbacks");
// SAFETY: the ref-counted keymap does not escape the locked Xkb;
// only its owned string is retained for the EIS keyboard.
unsafe { xkb.keymap() }
@@ -1708,7 +1711,7 @@ impl Ferese {
.filter_map(|(window, id)| {
let toplevel = window.toplevel()?;
let (app_id, title) = with_states(toplevel.wl_surface(), |states| {
let attributes = states.data_map.get::<XdgToplevelSurfaceData>().unwrap().lock().unwrap();
let attributes = states.data_map.get::<XdgToplevelSurfaceData>().expect("an XDG toplevel must retain its Smithay surface data").lock().expect("Smithay surface state must remain consistent across callbacks");
(
attributes.app_id.clone().unwrap_or_default(),
attributes.title.clone().unwrap_or_default(),
+9 -2
View File
@@ -78,7 +78,9 @@ impl Subscribers {
})
.collect::<Vec<_>>();
if let Some(last) = events.last_mut() {
Arc::get_mut(last).unwrap().last = true;
Arc::get_mut(last)
.expect("new initial-event records have not been shared with subscribers")
.last = true;
}
let initial = self
.subscribers
@@ -98,7 +100,12 @@ impl Subscribers {
let delivered = if subscriber.initial {
subscriber
.sender
.try_send(initial.as_ref().unwrap().clone())
.try_send(
initial
.as_ref()
.expect("the initial event batch is built before subscription delivery")
.clone(),
)
.is_ok()
} else {
events
+24 -20
View File
@@ -114,26 +114,30 @@ impl IdentityRegistry {
Some(base) => format!("{base}@{}", escaped(key)),
None => format!("drm:{}", escaped(key)),
};
let preferred = self
.known
.get(key)
.filter(|known| {
base.as_ref()
.is_some_and(|base| *known == base || *known == &qualified)
|| (base.is_none() && *known == &qualified)
})
.cloned()
.unwrap_or_else(|| {
if identity
.as_ref()
.is_some_and(|identity| identity.serial.is_some())
&& !self.ambiguous.contains(base.as_ref().unwrap())
{
base.clone().unwrap()
} else {
qualified.clone()
}
});
let preferred =
self.known
.get(key)
.filter(|known| {
base.as_ref()
.is_some_and(|base| *known == base || *known == &qualified)
|| (base.is_none() && *known == &qualified)
})
.cloned()
.unwrap_or_else(|| {
if identity
.as_ref()
.is_some_and(|identity| identity.serial.is_some())
&& !self.ambiguous.contains(base.as_ref().expect(
"a unique serial-bearing monitor has a computed base identity",
))
{
base.clone().expect(
"a unique serial-bearing monitor has a computed base identity",
)
} else {
qualified.clone()
}
});
let resolved = if used.insert(preferred.clone()) {
preferred
} else {
+25 -4
View File
@@ -30,7 +30,11 @@ struct FontRequests {
impl FontRequests {
fn request(&self, family: &str) {
*self.latest.lock().unwrap() = family.to_owned();
*self
.latest
.lock()
.expect("overview font request state must remain consistent across workers") =
family.to_owned();
let _ = self.wake.try_send(());
}
}
@@ -59,7 +63,10 @@ pub(crate) fn init_font_loader(
.name("ferese-overview-font".into())
.spawn(move || {
while requests.recv().is_ok() {
let family = worker_latest.lock().unwrap().clone();
let family = worker_latest
.lock()
.expect("overview font request state must remain consistent across workers")
.clone();
// Rescan on a font change so newly installed fonts remain usable.
let mut database = fontdb::Database::new();
database.load_system_fonts();
@@ -1049,7 +1056,16 @@ impl Ferese {
for (id, target) in overview_layout(window_area(bounds), &windows) {
targets.insert(
id,
(self.windows.geometry(&id).unwrap().visual.current, target),
(
self.windows
.geometry(&id)
.expect(
"overview layout includes only windows with resolved geometry",
)
.visual
.current,
target,
),
);
}
continue;
@@ -1074,7 +1090,12 @@ impl Ferese {
.collect::<Vec<_>>();
for (id, target) in overview_layout(window_area(bounds), &windows) {
let normal = self.windows.geometry(&id).unwrap().visual.current;
let normal = self
.windows
.geometry(&id)
.expect("overview layout includes only windows with resolved geometry")
.visual
.current;
// Incoming grids start at their overview size. Translation
// belongs to the workspace slide, not a second desktop zoom.
let source = if !sliding.is_empty() { target } else { normal };
+4 -1
View File
@@ -146,7 +146,10 @@ impl PortalSession {
pub(crate) fn begin_owned_query(&mut self, owner: u64) -> u32 {
let token = self.begin_query();
self.query.as_mut().unwrap().owner = Some(owner);
self.query
.as_mut()
.expect("begin_query installs the query before setting its owner")
.owner = Some(owner);
token
}
+7 -2
View File
@@ -108,10 +108,15 @@ fn update_shared<E: Element>(
) -> bool {
// Element queries can read this same backdrop's commit. Snapshot the state,
// release its lock, then inspect sources. Rendering is the sole writer.
let mut backdrop = shared.lock().unwrap().clone();
let mut backdrop = shared
.lock()
.expect("surface rendering state must remain consistent across callbacks")
.clone();
let sources = elements.iter().filter(|element| element.id() != owner);
let changed = backdrop.update(sources, region, scale);
*shared.lock().unwrap() = backdrop;
*shared
.lock()
.expect("surface rendering state must remain consistent across callbacks") = backdrop;
changed
}
+10 -2
View File
@@ -106,7 +106,11 @@ pub(crate) fn capture_window_frame(
.user_data()
.get_or_insert_threadsafe(|| Arc::new(CaptureTarget::default()))
.clone();
let reusable = cache.0.lock().unwrap().take();
let reusable = cache
.0
.lock()
.expect("the reusable capture texture cache must remain consistent across callbacks")
.take();
let mut snapshot = render_window_texture(
renderer,
window,
@@ -159,7 +163,11 @@ pub(crate) fn capture_window_frame(
return Err("Incomplete window readback".into());
}
let pixels = source[..bytes].to_vec();
*cache.0.lock().unwrap() = Some(snapshot.texture);
*cache
.0
.lock()
.expect("the reusable capture texture cache must remain consistent across callbacks") =
Some(snapshot.texture);
Ok(crate::handlers::screenshot::CaptureBuffer {
width: size.w,
+1 -1
View File
@@ -730,7 +730,7 @@ fn panel_matrices(
match permutation
.iter()
.position(|i| *i == old_corner as f32)
.unwrap()
.expect("the corner transform is a permutation containing every source corner")
{
0 => 5,
1 => 4,
+9 -2
View File
@@ -222,7 +222,11 @@ impl BlurRenderElement {
self.geometry = parameters.sample_geometry;
self.capture_rect = framebuffer_capture_rect(parameters.sample_framebuffer);
self.uniforms = blur_uniforms(parameters).into();
self.backdrop.lock().unwrap().commit.increment();
self.backdrop
.lock()
.expect("surface rendering state must remain consistent across callbacks")
.commit
.increment();
}
}
@@ -232,7 +236,10 @@ impl Element for BlurRenderElement {
}
fn current_commit(&self) -> CommitCounter {
self.backdrop.lock().unwrap().commit
self.backdrop
.lock()
.expect("surface rendering state must remain consistent across callbacks")
.commit
}
fn src(&self) -> Rectangle<f64, Buffer> {
+8 -1
View File
@@ -561,7 +561,14 @@ pub(super) fn cursor_elements(
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()
});
let physical_location =
+16 -4
View File
@@ -39,7 +39,10 @@ struct Cancellation {
impl Cancellation {
fn cancel(&self) {
let mut state = self.state.lock().unwrap();
let mut state = self
.state
.lock()
.expect("resume cancellation state must remain consistent across workers");
state.requested = true;
let waker = state.waker.take();
self.changed.notify_all();
@@ -52,7 +55,10 @@ impl Cancellation {
async fn cancelled(&self) {
future::poll_fn(|context| {
let mut state = self.state.lock().unwrap();
let mut state = self
.state
.lock()
.expect("resume cancellation state must remain consistent across workers");
if state.requested {
return Poll::Ready(());
}
@@ -66,8 +72,14 @@ impl Cancellation {
fn retry(&self, delay: Duration) -> bool {
let (state, _) = self
.changed
.wait_timeout_while(self.state.lock().unwrap(), delay, |state| !state.requested)
.unwrap();
.wait_timeout_while(
self.state
.lock()
.expect("resume cancellation state must remain consistent across workers"),
delay,
|state| !state.requested,
)
.expect("resume cancellation state must remain consistent across workers");
!state.requested
}
}
+13 -3
View File
@@ -289,7 +289,11 @@ impl Ferese {
surface.alive().then(|| {
(
surface.wl_surface().clone(),
self.space.output_geometry(output).unwrap().loc.to_f64(),
self.space
.output_geometry(output)
.expect("a lock surface is assigned to a mapped output with geometry")
.loc
.to_f64(),
)
})
}
@@ -366,7 +370,10 @@ impl Ferese {
.map(|(_, surface)| surface)
})
.map(|surface| surface.wl_surface().clone());
let keyboard = self.seat.get_keyboard().unwrap();
let keyboard = self
.seat
.get_keyboard()
.expect("the compositor seat is initialized with a keyboard");
if keyboard.current_focus() != surface {
keyboard.set_focus(self, surface, SERIAL_COUNTER.next_serial());
}
@@ -493,7 +500,10 @@ impl SessionLockHandler for Ferese {
touch.unset_grab(self);
touch.cancel(self);
}
let keyboard = self.seat.get_keyboard().unwrap();
let keyboard = self
.seat
.get_keyboard()
.expect("the compositor seat is initialized with a keyboard");
keyboard.unset_grab(self);
keyboard.set_focus(self, None, serial);
self.lock_input_activity();
+8 -6
View File
@@ -200,9 +200,9 @@ impl Ferese {
let swipe = self
.viewports
.get_mut(&workspace)
.unwrap()
.expect("an active workspace gesture retains its viewport state")
.end_gesture(None)
.unwrap();
.expect("an active workspace gesture retains its viewport state");
self.presentation_dependencies
.restore_viewport(swipe.workspace, swipe.dependencies);
self.release_presentation_dependencies();
@@ -360,10 +360,12 @@ impl Ferese {
}
}
}
let expired = self
.windows
.resizing()
.any(|id| self.windows.transaction(id).unwrap().expired(now));
let expired = self.windows.resizing().any(|id| {
self.windows
.transaction(id)
.expect("the resize candidate retains its active presentation transaction")
.expired(now)
});
self.windows.expire_transactions(now);
if expired {
self.release_presentation_dependencies();
+3 -1
View File
@@ -30,7 +30,9 @@ impl Ferese {
let Some(data) = states.data_map.get::<XdgToplevelSurfaceData>() else {
return (true, None);
};
let data = data.lock().unwrap();
let data = data
.lock()
.expect("window metadata must remain consistent during privacy checks");
let rule = crate::window_rules::resolve(
&self.window_rules,
data.app_id.as_deref(),
+4 -1
View File
@@ -156,7 +156,10 @@ impl Ferese {
self.windows.update(window, |w| w.maximized = false);
}
if self.workspaces.placement(window) == Some(WindowPlacement::Tiled) {
let workspace_id = self.workspaces.workspace_for_window(window).unwrap();
let workspace_id = self
.workspaces
.workspace_for_window(window)
.expect("a tiled window belongs to a workspace");
if let Some(workspace) = self.workspaces.workspace_mut(workspace_id)
&& let WorkspaceLayout::Scrolling(layout) = &mut workspace.layout
{
+5 -1
View File
@@ -657,7 +657,11 @@ impl Ferese {
self.resize_metrics.begin(
id,
self.workspaces.workspace_for_window(id),
&self.windows.record(id).unwrap().app_id,
&self
.windows
.record(id)
.expect("the selected window has a registered lifecycle record")
.app_id,
now,
);
self.windows.set_transaction(
+7 -2
View File
@@ -57,8 +57,13 @@ impl Ferese {
let Some(mut presentation) = self.current_window_presentation(id) else {
return;
};
let record = self.windows.record(id).unwrap();
let geometry = record.geometry.unwrap();
let record = self
.windows
.record(id)
.expect("the selected window has a registered lifecycle record");
let geometry = record
.geometry
.expect("presented windows retain their resolved geometry");
let decorations = geometry.decorations.clamp(0.0, 1.0);
let handoff_size = (!presentation.scale_content).then(|| {
presentation
+7 -4
View File
@@ -112,7 +112,7 @@ impl Ferese {
if let Some(workspace) = self.focus_swipe().map(|swipe| swipe.workspace) {
self.viewports
.get_mut(&workspace)
.unwrap()
.expect("an active workspace gesture retains its viewport state")
.update_gesture(progress);
}
@@ -145,9 +145,9 @@ impl Ferese {
let swipe = self
.viewports
.get_mut(&workspace)
.unwrap()
.expect("an active workspace gesture retains its viewport state")
.end_gesture(velocity)
.unwrap();
.expect("an active workspace gesture retains its viewport state");
self.presentation_dependencies
.restore_viewport(workspace, swipe.dependencies.clone());
let neighbor = self.output_bounds().and_then(|bounds| {
@@ -653,7 +653,10 @@ impl Ferese {
{
slide.release_with_velocity(true, self.swipe.release_velocity / self.animation_speed);
self.last_animation_tick = Instant::now();
self.workspace_slides.insert(owner, previous_slide.unwrap());
self.workspace_slides.insert(
owner,
previous_slide.expect("the reversal branch requires an existing workspace slide"),
);
return;
}
let slide_direction = slide_direction.or_else(|| {
@@ -119,7 +119,11 @@ fn tree_reflow_groups(
groups.insert(*id, group);
let mut frontier = vec![*id];
while let Some(id) = frontier.pop() {
let bounds = moving.iter().find(|(window, _)| *window == id).unwrap().1;
let bounds = moving
.iter()
.find(|(window, _)| *window == id)
.expect("the dependency frontier contains only moving window IDs")
.1;
for (other, candidate) in &moving {
if !groups.contains_key(other)
&& bounds[0].max(candidate[0]) + 0.001 < bounds[2].min(candidate[2])
@@ -448,7 +452,10 @@ impl Ferese {
if self.workspaces.workspace_for_window(*slow) != Some(workspace_id) {
continue;
}
let transaction = *self.windows.transaction(slow).unwrap();
let transaction = *self
.windows
.transaction(slow)
.expect("the resize candidate retains its active presentation transaction");
let commit = ClientCommit {
window: *slow,
serial: transaction.serial(),
@@ -504,7 +511,14 @@ impl Ferese {
self.workspaces.workspace_for_window(**id) == Some(workspace.id)
&& layout.contains(**id)
})
.map(|id| (*id, *self.windows.transaction(id).unwrap()))
.map(|id| {
(
*id,
*self.windows.transaction(id).expect(
"the resize candidate retains its active presentation transaction",
),
)
})
.collect::<Vec<_>>(),
previous_viewport.remove(&workspace.id).unwrap_or_default(),
);
+14 -3
View File
@@ -101,7 +101,12 @@ impl Ferese {
layout: !plan.affected_outputs.is_empty(),
focus: plan.focus_changed,
shell: !plan.affected_outputs.is_empty(),
outputs: self.desktop_transition.as_ref().unwrap().redraw.clone(),
outputs: self
.desktop_transition
.as_ref()
.expect("desktop transition must be started before publication or retirement")
.redraw
.clone(),
};
let floats = self
.windows
@@ -213,7 +218,10 @@ impl Ferese {
}
pub(crate) fn finish_desktop_transition(&mut self, mut changes: DesktopChanges) {
self.desktop_transition.as_mut().unwrap().publishing = true;
self.desktop_transition
.as_mut()
.expect("desktop transition must be started before publication or retirement")
.publishing = true;
let activity_output = self.space.outputs().next().map(|output| output.name());
if let Some(output) = activity_output
&& (self.activities.store.attach_output(&output)
@@ -230,7 +238,10 @@ impl Ferese {
self.reconcile_layer_keyboard_focus();
self.refresh_lock_outputs();
self.refresh_idle_inhibition();
let batch = self.desktop_transition.take().unwrap();
let batch = self
.desktop_transition
.take()
.expect("desktop transition must be started before publication or retirement");
changes.outputs.extend(batch.redraw);
// Animation/layout helpers may request snapshots, but only this point
// can emit the complete topology generation.
+10 -4
View File
@@ -236,7 +236,10 @@ impl WindowRegistry<Window> {
if let Some(surface) = surface {
self.surface_ids.insert(surface.clone(), id);
self.records.get_mut(&id).unwrap().surface = Some(surface.clone());
self.records
.get_mut(&id)
.expect("the selected window has a registered lifecycle record")
.surface = Some(surface.clone());
self.refresh_metadata(&surface);
}
}
@@ -245,14 +248,17 @@ impl WindowRegistry<Window> {
let Some(id) = self.id_for_surface(surface) else {
return false;
};
let record = self.records.get_mut(&id).unwrap();
let record = self
.records
.get_mut(&id)
.expect("the selected window has a registered lifecycle record");
with_states(surface, |states| {
let attributes = states
.data_map
.get::<XdgToplevelSurfaceData>()
.unwrap()
.expect("an XDG toplevel must retain its Smithay surface data")
.lock()
.unwrap();
.expect("Smithay surface state must remain consistent across callbacks");
let app_id = attributes.app_id.as_deref().unwrap_or_default();
let title = attributes.title.as_deref().unwrap_or_default();
let changed = record.app_id != app_id || record.title != title;
+4 -4
View File
@@ -28,9 +28,9 @@ impl Ferese {
let attributes = states
.data_map
.get::<XdgToplevelSurfaceData>()
.unwrap()
.expect("an XDG toplevel must retain its Smithay surface data")
.lock()
.unwrap();
.expect("Smithay surface state must remain consistent across callbacks");
(
attributes.app_id.clone(),
attributes.title.clone(),
@@ -350,9 +350,9 @@ impl Ferese {
let attributes = states
.data_map
.get::<XdgToplevelSurfaceData>()
.unwrap()
.expect("an XDG toplevel must retain its Smithay surface data")
.lock()
.unwrap();
.expect("Smithay surface state must remain consistent across callbacks");
(
attributes.app_id.clone(),
attributes.title.clone(),
+24 -8
View File
@@ -90,7 +90,9 @@ fn auto_context() -> ferese_config::theme::AutoContext {
Mutex::new(value)
});
ferese_config::theme::AutoContext {
system: *appearance.lock().unwrap(),
system: *appearance
.lock()
.expect("theme watcher state must remain consistent across workers"),
}
}
@@ -264,7 +266,10 @@ impl Engine {
directory.to_owned()
})
.collect();
*self.paths.lock().unwrap() = targets;
*self
.paths
.lock()
.expect("theme watcher state must remain consistent across workers") = targets;
if let Some(watcher) = &mut self.watcher {
for directory in self.watched_directories.difference(&directories) {
let _ = watcher.unwatch(directory);
@@ -349,11 +354,14 @@ pub(crate) fn init(
let (sender, events) = mpsc::sync_channel(1);
let paths = state.theme_engine.paths.clone();
if let Some(config) = crate::config::config_path() {
paths.lock().unwrap().insert(config);
paths
.lock()
.expect("theme watcher state must remain consistent across workers")
.insert(config);
}
paths
.lock()
.unwrap()
.expect("theme watcher state must remain consistent across workers")
.insert(PathBuf::from("/etc/localtime"));
let wakeup = state.loop_signal.clone();
let system_sender = sender.clone();
@@ -385,7 +393,9 @@ pub(crate) fn init(
.output()
{
if let Some(state) = SYSTEM_APPEARANCE.get() {
*state.lock().unwrap() =
*state
.lock()
.expect("theme watcher state must remain consistent across workers") =
system_appearance_value(&String::from_utf8_lossy(&output.stdout));
}
let _ = system_sender.try_send(());
@@ -398,7 +408,9 @@ pub(crate) fn init(
{
let value = system_appearance_value(&line);
if let Some(state) = SYSTEM_APPEARANCE.get() {
*state.lock().unwrap() = value;
*state.lock().expect(
"theme watcher state must remain consistent across workers",
) = value;
}
let _ = system_sender.try_send(());
system_wakeup.wakeup();
@@ -434,7 +446,7 @@ fn file_watcher(
&& event.paths.iter().any(|path| {
paths
.lock()
.unwrap()
.expect("theme watcher state must remain consistent across workers")
.iter()
.any(|target| target == path || target.starts_with(path))
})
@@ -575,7 +587,11 @@ impl Ferese {
}
}
if start {
let pending = self.theme_engine.pending.take().unwrap();
let pending = self
.theme_engine
.pending
.take()
.expect("the transition starts only when a theme candidate is pending");
let from = self.theme_engine.live.presented.clone();
let previous_wallpaper = pending
.wallpaper
+9 -1
View File
@@ -114,7 +114,15 @@ impl WallpaperState {
let decode_failed = config.path.is_some() && !owned;
let receiver = if owned {
Some(decoder.submit(config.path.clone().unwrap(), wakeup.clone()))
Some(
decoder.submit(
config
.path
.clone()
.expect("owned wallpaper decoding requires a configured image path"),
wakeup.clone(),
),
)
} else {
decoder.cancel_pending();
None
+23 -8
View File
@@ -30,15 +30,21 @@ impl Decoder {
loop {
let (queue, ready) = &*worker;
let mut queue = ready
.wait_while(queue.lock().unwrap(), |queue| {
!queue.closed && queue.pending.is_none()
})
.unwrap();
.wait_while(
queue.lock().expect(
"wallpaper decode queue must remain consistent across workers",
),
|queue| !queue.closed && queue.pending.is_none(),
)
.expect("wallpaper decode queue must remain consistent across workers");
if queue.closed {
break;
}
let request = queue.pending.take().unwrap();
let request = queue
.pending
.take()
.expect("the decode wait completed with a pending wallpaper request");
drop(queue);
let _ = request.result.send(load(&request.path));
if let Some(wakeup) = request.wakeup {
@@ -57,7 +63,10 @@ impl Decoder {
) -> mpsc::Receiver<Result<Pixels, String>> {
let (result, receiver) = mpsc::channel();
let (queue, ready) = &*self.shared;
queue.lock().unwrap().pending = Some(Request {
queue
.lock()
.expect("wallpaper decode queue must remain consistent across workers")
.pending = Some(Request {
path,
result,
wakeup,
@@ -67,14 +76,20 @@ impl Decoder {
}
pub fn cancel_pending(&self) {
self.shared.0.lock().unwrap().pending = None;
self.shared
.0
.lock()
.expect("wallpaper decode queue must remain consistent across workers")
.pending = None;
}
}
impl Drop for Decoder {
fn drop(&mut self) {
let (queue, ready) = &*self.shared;
let mut queue = queue.lock().unwrap();
let mut queue = queue
.lock()
.expect("wallpaper decode queue must remain consistent across workers");
queue.closed = true;
queue.pending = None;
ready.notify_one();
+1 -1
View File
@@ -185,7 +185,7 @@ pub fn init(event_loop: &mut EventLoop<Ferese>, state: &mut Ferese) -> Result<()
let _ = renderer
.render(
framebuffer,
output.current_mode().unwrap().size,
output.current_mode().expect("a rendered output must have a current mode").size,
output.current_transform(),
)?
.finish()?;