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
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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(®ions) {
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(¤t);
|
||||
let requested_focus = policy.keyboard_interactivity == KeyboardInteractivity::OnDemand
|
||||
&& previous
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(),
|
||||
);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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()?;
|
||||
|
||||
Reference in new issue
Block a user