diff --git a/apps/effectcraft/src/main.rs b/apps/effectcraft/src/main.rs index 38a88b3d..2f99bd5e 100644 --- a/apps/effectcraft/src/main.rs +++ b/apps/effectcraft/src/main.rs @@ -93,6 +93,9 @@ fn parse_args(args: impl IntoIterator, env_port: Result return Ok(None), _ => match a.strip_prefix("--control=") { Some(v) => out.control_port = Some(control_port("--control", Some(v))?), + // macOS LaunchServices may pass a process serial number (`-psn_0_1234`), notably on + // the first launch of a downloaded app; it is not an option of ours. + None if a.starts_with("-psn_") => {} None if a.starts_with('-') && a != "-" => return Err(format!("unknown option `{a}`")), None => out.files.push(a), }, @@ -494,7 +497,7 @@ mod tests { assert!(super::wayland_drop_notice(Some(OsStr::new("wayland-0")), Some(OsStr::new("x11"))).is_none()); } - /// The window's device never asks for more than the adapter offers (#198). + /// The window's wgpu instance skips indirect-call validation (Intel Metal, #1153). #[test] fn the_window_instance_skips_indirect_call_validation() { if std::env::var_os("WGPU_VALIDATION_INDIRECT_CALL").is_some() { @@ -507,6 +510,7 @@ mod tests { assert!(!setup.instance_descriptor.flags.contains(eframe::wgpu::InstanceFlags::VALIDATION_INDIRECT_CALL)); } + /// The window's device never asks for more than the adapter offers (#198). #[test] fn device_limits_fit_the_adapter() { use eframe::wgpu::Limits; @@ -569,6 +573,8 @@ mod tests { } // Parsed left to right: an error before `--version` wins. assert_eq!(fails(&["--nope", "--version"]), "unknown option `--nope`"); + // Finder's process serial number is ignored, not imported or refused. + assert_eq!(parse(&["-psn_0_12345"]).map(|a| a.files), Some(vec![])); let a = parse(&["clip.mov", "-", "--", "--demo", "-name.png"]).unwrap_or_default(); assert_eq!(a.files, ["clip.mov", "-", "--demo", "-name.png"]); assert!(!a.demo); diff --git a/crates/effects/src/channel3d.rs b/crates/effects/src/channel3d.rs index 893bef20..0d089ba2 100644 --- a/crates/effects/src/channel3d.rs +++ b/crates/effects/src/channel3d.rs @@ -386,7 +386,7 @@ type RankPlanes<'a> = [(&'a [f32], &'a [f32])]; /// ranks (the Cryptomatte spec's matte), clamped to 0..1. #[inline] fn crypto_matte(planes: &RankPlanes, sel: &[u32], i: usize) -> f32 { - planes.iter().map(|(id, c)| (id[i], c[i])).filter(|(id, c)| *c > 0.0 && sel.contains(&id.to_bits())).map(|(_, c)| c).sum::().clamp(0.0, 1.0) + planes.iter().filter_map(|(id, c)| Some((*id.get(i)?, *c.get(i)?))).filter(|(id, c)| *c > 0.0 && sel.contains(&id.to_bits())).map(|(_, c)| c).sum::().clamp(0.0, 1.0) } /// The coverage-weighted ID colours at aux pixel `i` (the spec's preview). @@ -394,7 +394,7 @@ fn crypto_matte(planes: &RankPlanes, sel: &[u32], i: usize) -> f32 { fn crypto_colors(planes: &RankPlanes, i: usize) -> [f32; 3] { let mut c = [0.0f32; 3]; for (id, cov) in planes { - let (id, cov) = (id[i], cov[i]); + let (Some(&id), Some(&cov)) = (id.get(i), cov.get(i)) else { continue }; let k = id_color(id); for j in 0..3 { c[j] += k[j] * cov; diff --git a/crates/engine/src/commands/layer_menu.rs b/crates/engine/src/commands/layer_menu.rs index 7685d6bf..7737b8af 100644 --- a/crates/engine/src/commands/layer_menu.rs +++ b/crates/engine/src/commands/layer_menu.rs @@ -557,7 +557,6 @@ fn add_marker(s: &mut Session, p: &Value) -> Result { if ids.is_empty() { s.edit("Add Marker", None, |proj, _| { let c = proj.comp_mut(cid).ok_or(EngineError::NoComp)?; - c.markers.retain(|m| m.time != t); c.markers.push(Marker { time: t, comment, ..Default::default() }); c.markers.sort_by_key(|m| m.time); Ok(()) diff --git a/crates/script/src/model.rs b/crates/script/src/model.rs index e4bf127d..90b03ae5 100644 --- a/crates/script/src/model.rs +++ b/crates/script/src/model.rs @@ -94,9 +94,6 @@ fn label_index(l: Label) -> usize { Label::ALL.iter().position(|x| *x == l).unwrap_or(0) } -/// A comp's selected properties as `[layer, uid]`: the selected ones, then those with selected -/// keyframes (selecting a keyframe selects its property in After Effects, so scripts find -/// selected keys through `selectedProperties`). /// Layer ids selected in `comp`, including a comp that is not the active one (#1001). fn selected_layer_ids(s: &Session, comp: effectcraft_project::ItemId) -> Vec { let layers = if s.active_comp_id() == Some(comp) { @@ -118,6 +115,9 @@ fn layer_selected(s: &Session, comp: effectcraft_project::ItemId, layer: effectc s.state.comp_selections.get(&comp).is_some_and(|sel| sel.layers.contains(&layer)) } +/// A comp's selected properties as `[layer, uid]`: the selected ones, then those with selected +/// keyframes (selecting a keyframe selects its property in After Effects, so scripts find +/// selected keys through `selectedProperties`). fn selected_props(s: &Session, c: &effectcraft_project::Comp) -> Vec { let mut v: Vec<(u64, u64)> = vec![]; let keyed = s.state.selected_keys.iter().map(|k| (k.layer, k.prop));