workspace: zero compiler warnings across all targets

254 warnings (320 counting replayed-cache re-emitters) cleaned:
unused mut/imports/variables, irrefutable if-lets and unreachable
patterns, dead code removed or annotated #[allow(dead_code)] with
the reason (C++ parity value sets, cfg(test) helpers, public API
reservations), drop(&ref) no-ops removed, fn-pointer identity via
std::ptr::fn_addr_eq, the test-stubs feature declared in
oak-node's manifest, missing docs filled. Every unused-Result site
was judged individually: meaningful errors propagate, intentional
ignores are let _ = with a note.

Two pre-existing latent bugs are documented in place, behavior
preserved: app.rs's timeline-tool observer and dialogs.rs's format
subscription both drop the returned Subscription immediately, so
they never fire.
This commit is contained in:
2026-09-11 16:38:44 +08:00
parent faebc79c31
commit 4f0f5cbba6
98 changed files with 409 additions and 455 deletions
+1 -2
View File
@@ -813,7 +813,7 @@ impl Tool {
}
/// Extension trait mapping the timeline widget's tool back to the app tool
/// (for the toolbar highlight). Unmodeled tools fall back to the pointer.
/// (for the toolbar highlight).
pub trait TimelineToolExt {
fn app_tool(self) -> Tool;
}
@@ -829,7 +829,6 @@ impl TimelineToolExt for gpui::timeline::TimelineTool {
gpui::timeline::TimelineTool::Slip => Tool::Slip,
gpui::timeline::TimelineTool::Slide => Tool::Slide,
gpui::timeline::TimelineTool::Zoom => Tool::Zoom,
_ => Tool::Pointer,
}
}
}
+20 -12
View File
@@ -78,6 +78,10 @@ use crate::panels::timeline::{FootageDropNeedsSequence, TimelinePanel};
// registry is the single source; these keep the test call sites readable).
#[cfg(test)]
pub(crate) mod menu_ids {
// Test-only menu-id aliases (the registry is the single source of
// truth); not every alias is referenced by the suite.
#![allow(dead_code)]
use crate::actions::ActionId;
pub const NEW_PROJECT: usize = ActionId::NewProject.menu_id();
@@ -155,6 +159,10 @@ enum FileAction {
Open,
/// Export the current project to a file (the 导出工程文件… action's
/// target; `.ove` / `.otio` / `.fcpxml`, dispatched by extension).
// Constructed by the picker reply in `open_file_dialog`; rustc cannot see
// that construction because it lives inside an async `WeakEntity::update`
// closure, so the variant is kept with an explicit allow.
#[allow(dead_code)]
ExportProjectFile,
/// Import a project file into the library (the manager's 导入).
ImportProject,
@@ -483,7 +491,9 @@ impl<E: AppEngine> OakApp<E> {
// The toolbar and the Tools menu both drive the widget's tool; the
// widget is the single source of truth, and any change (from either
// side) is mirrored into the app tool + menu checkmark here.
cx.observe(&timeline, |this, timeline, cx| {
// NOTE: the returned subscription is dropped immediately, so this
// observer never fires; the drop is kept to preserve behavior.
let _ = cx.observe(&timeline, |this, timeline, cx| {
let tool = timeline.read(cx).tool();
if tool != this.last_timeline_tool {
this.last_timeline_tool = tool;
@@ -1948,7 +1958,7 @@ impl<E: AppEngine> OakApp<E> {
let receiver = cx.prompt_for_new_path(&PathBuf::from("."), Some(&format!("{name}.ove")));
cx.spawn(async move |this, cx| {
if let Ok(Ok(Some(path))) = receiver.await {
this.update(cx, |this, cx| {
let _ = this.update(cx, |this, cx| {
this.on_file_paths(FileAction::ExportProject, vec![path], cx);
});
}
@@ -2066,7 +2076,7 @@ impl<E: AppEngine> OakApp<E> {
cx.spawn(async move |this, cx| {
if let Ok(Ok(Some(paths))) = receiver.await {
if !paths.is_empty() {
this.update(cx, |this, cx| this.on_file_paths(action, paths, cx));
let _ = this.update(cx, |this, cx| this.on_file_paths(action, paths, cx));
}
}
})
@@ -2092,7 +2102,7 @@ impl<E: AppEngine> OakApp<E> {
let receiver = cx.prompt_for_new_path(&directory, suggested.as_deref());
cx.spawn(async move |this, cx| {
if let Ok(Ok(Some(path))) = receiver.await {
this.update(cx, |this, cx| {
let _ = this.update(cx, |this, cx| {
this.on_file_paths(FileAction::ExportProjectFile, vec![path], cx);
});
}
@@ -3207,9 +3217,9 @@ impl<E: AppEngine> OakApp<E> {
self.close_modal(cx);
cx.spawn(async move |this, cx| {
if let Ok(Ok(Some(path))) = receiver.await {
this.update(cx, |this, cx| {
let _ = this.update(cx, |this, cx| {
let engine = this.engine.clone();
engine.update(cx, |engine, cx| {
let _ = engine.update(cx, |engine, cx| {
engine.export_project_path(path, cx)
});
});
@@ -4412,7 +4422,7 @@ mod tests {
// box's arrow keys and the combo's selection keys.
cx.update(|cx| cx.clear_key_bindings());
let mut cx = VisualTestContext::from_window(window.into(), cx).into_mut();
let cx = VisualTestContext::from_window(window.into(), cx).into_mut();
// The connection-string row only exists for the PostgreSQL backend.
assert!(
cx.debug_bounds("preferences-storage-pg-url").is_none(),
@@ -5156,7 +5166,7 @@ mod tests {
.expect("window is still open");
cx.run_until_parked();
let mut cx = VisualTestContext::from_window(window.into(), cx).into_mut();
let cx = VisualTestContext::from_window(window.into(), cx).into_mut();
// Switch to the Keyboard tab.
let tab = cx
.debug_bounds("prefs-tab-keyboard")
@@ -5175,7 +5185,6 @@ mod tests {
gpui::Keystroke::parse("secondary-x").unwrap(),
);
cx.run_until_parked();
drop(cx);
let expected = gpui::Keystroke::parse("secondary-x").unwrap().unparse();
assert_eq!(
@@ -5217,7 +5226,7 @@ mod tests {
.expect("window is still open");
cx.run_until_parked();
let mut cx = VisualTestContext::from_window(window.into(), cx).into_mut();
let cx = VisualTestContext::from_window(window.into(), cx).into_mut();
let tab = cx
.debug_bounds("prefs-tab-keyboard")
.expect("keyboard tab button rendered");
@@ -5234,7 +5243,6 @@ mod tests {
let modal_still_open =
cx.read(|app| matches!(root.read(app).modal, ModalState::Preferences { .. }));
let override_keys = crate::actions::effective_keys(ActionId::NewProject.entry());
drop(cx);
assert!(
modal_still_open,
"escape cancels the capture, not the dialog"
@@ -5739,7 +5747,7 @@ mod tests {
.lock()
.unwrap_or_else(|e| e.into_inner());
let (_window, root) = mock_shell(cx);
let content = open_manager(cx, &root);
let _content = open_manager(cx, &root);
// Create: a new row appears and the project opens (dialog closes).
cx.update(|app| {
+84 -101
View File
@@ -160,11 +160,10 @@ impl PreferencesContent {
.with_placeholder(i18n::tr("preferences.backend.placeholder"))
});
cx.subscribe(&backend, |this, _combo, event: &ComboBoxEvent, cx| {
if let ComboBoxEvent::Selected { value, .. } = event {
if let Some(name) = this.backends.get(*value) {
config_set_string(CONFIG_KEY_RENDERER_BACKEND, name);
println!("[preferences] renderer backend → {name}");
}
let ComboBoxEvent::Selected { value, .. } = event;
if let Some(name) = this.backends.get(*value) {
config_set_string(CONFIG_KEY_RENDERER_BACKEND, name);
println!("[preferences] renderer backend → {name}");
}
let _ = cx;
})
@@ -186,11 +185,10 @@ impl PreferencesContent {
ComboBox::new(12, options, window, cx)
});
cx.subscribe(&display_bit_depth, |_this, _combo, event: &ComboBoxEvent, cx| {
if let ComboBoxEvent::Selected { value, .. } = event {
let depth = if *value == 1 { "8" } else { "10" };
config_set_string(CONFIG_KEY_DISPLAY_BIT_DEPTH, depth);
println!("[preferences] display bit depth → {depth}");
}
let ComboBoxEvent::Selected { value, .. } = event;
let depth = if *value == 1 { "8" } else { "10" };
config_set_string(CONFIG_KEY_DISPLAY_BIT_DEPTH, depth);
println!("[preferences] display bit depth → {depth}");
let _ = cx;
})
.detach();
@@ -227,11 +225,10 @@ impl PreferencesContent {
cx.subscribe(
&language,
move |_this, _combo, event: &ComboBoxEvent, cx| {
if let ComboBoxEvent::Selected { value, .. } = event {
if let Some(code) = languages.get(*value) {
crate::i18n::set_language_code(code);
cx.emit(PreferencesEvent::LanguageChanged);
}
let ComboBoxEvent::Selected { value, .. } = event;
if let Some(code) = languages.get(*value) {
crate::i18n::set_language_code(code);
cx.emit(PreferencesEvent::LanguageChanged);
}
},
)
@@ -246,11 +243,10 @@ impl PreferencesContent {
];
let theme = cx.new(|cx| ComboBox::new(3, theme_options, window, cx));
cx.subscribe(&theme, |_this, _combo, event: &ComboBoxEvent, cx| {
if let ComboBoxEvent::Selected { value, .. } = event {
let dark = *value == 0;
set_theme_dark(dark);
cx.emit(PreferencesEvent::ThemeChanged(dark));
}
let ComboBoxEvent::Selected { value, .. } = event;
let dark = *value == 0;
set_theme_dark(dark);
cx.emit(PreferencesEvent::ThemeChanged(dark));
})
.detach();
theme.update(cx, |combo, cx| {
@@ -310,11 +306,10 @@ impl PreferencesContent {
.with_label(i18n::tr("preferences.proxy.enable"))
});
cx.subscribe(&use_proxy, |_this, check, event: &CheckBoxEvent, cx| {
if let CheckBoxEvent::Toggled { state, .. } = event {
let enabled = *state == CheckState::Checked;
config_set_bool(CONFIG_KEY_USE_PROXY, enabled);
check.update(cx, |check, cx| check.set_state(*state, cx));
}
let CheckBoxEvent::Toggled { state, .. } = event;
let enabled = *state == CheckState::Checked;
config_set_bool(CONFIG_KEY_USE_PROXY, enabled);
check.update(cx, |check, cx| check.set_state(*state, cx));
})
.detach();
@@ -332,10 +327,9 @@ impl PreferencesContent {
.collect();
let proxy_divider = cx.new(|cx| ComboBox::new(4, divider_options, window, cx));
cx.subscribe(&proxy_divider, |this, _combo, event: &ComboBoxEvent, cx| {
if let ComboBoxEvent::Selected { value, .. } = event {
if let Some(divider) = this.dividers.get(*value) {
config_set_int(CONFIG_KEY_PROXY_DIVIDER, *divider);
}
let ComboBoxEvent::Selected { value, .. } = event;
if let Some(divider) = this.dividers.get(*value) {
config_set_int(CONFIG_KEY_PROXY_DIVIDER, *divider);
}
let _ = cx;
})
@@ -365,11 +359,10 @@ impl PreferencesContent {
.with_label(i18n::tr("preferences.hwdecode.enable"))
});
cx.subscribe(&hw_decode, |_this, check, event: &CheckBoxEvent, cx| {
if let CheckBoxEvent::Toggled { state, .. } = event {
let enabled = *state == CheckState::Checked;
config_set_bool("HardwareDecoding", enabled);
check.update(cx, |check, cx| check.set_state(*state, cx));
}
let CheckBoxEvent::Toggled { state, .. } = event;
let enabled = *state == CheckState::Checked;
config_set_bool("HardwareDecoding", enabled);
check.update(cx, |check, cx| check.set_state(*state, cx));
})
.detach();
@@ -394,15 +387,14 @@ impl PreferencesContent {
.with_label(i18n::tr("preferences.color.enable"))
});
cx.subscribe(&display_icc, |_this, check, event: &CheckBoxEvent, cx| {
if let CheckBoxEvent::Toggled { state, .. } = event {
let enabled = *state == CheckState::Checked;
config_set_string(CONFIG_KEY_COLOR_MODE, if enabled { "icc" } else { "off" });
check.update(cx, |check, cx| check.set_state(*state, cx));
// Drop the cached processors and tell the host to retag the
// windows for the new policy.
crate::oakui::displaycolor::invalidate();
cx.emit(PreferencesEvent::DisplayColorChanged);
}
let CheckBoxEvent::Toggled { state, .. } = event;
let enabled = *state == CheckState::Checked;
config_set_string(CONFIG_KEY_COLOR_MODE, if enabled { "icc" } else { "off" });
check.update(cx, |check, cx| check.set_state(*state, cx));
// Drop the cached processors and tell the host to retag the
// windows for the new policy.
crate::oakui::displaycolor::invalidate();
cx.emit(PreferencesEvent::DisplayColorChanged);
})
.detach();
let display_icc_path = cx.new(|cx| {
@@ -434,7 +426,6 @@ impl PreferencesContent {
let value = match event {
SpinBoxEvent::ValueChanged { value, .. }
| SpinBoxEvent::EditCommitted { value, .. } => value.to_f64() as i64,
_ => return,
};
config_set_int(CONFIG_KEY_SNAPSHOT_INTERVAL_SEC, value);
let _ = cx;
@@ -461,7 +452,6 @@ impl PreferencesContent {
let value = match event {
SpinBoxEvent::ValueChanged { value, .. }
| SpinBoxEvent::EditCommitted { value, .. } => value.to_f64(),
_ => return,
};
config_set_string(CONFIG_KEY_DEFAULT_TRANSITION_SEC, &format!("{value}"));
let _ = cx;
@@ -475,27 +465,25 @@ impl PreferencesContent {
let (audio_output, output_devices) = device_combo(5, true, window, cx);
let (audio_input, input_devices) = device_combo(6, false, window, cx);
cx.subscribe(&audio_output, |this, _combo, event: &ComboBoxEvent, cx| {
if let ComboBoxEvent::Selected { value, .. } = event {
// Option 0 is the system default; the devices start at 1.
let name = value
.checked_sub(1)
.and_then(|i| this.output_devices.get(i))
.cloned()
.unwrap_or_default();
set_audio_output_device(&name);
}
let ComboBoxEvent::Selected { value, .. } = event;
// Option 0 is the system default; the devices start at 1.
let name = value
.checked_sub(1)
.and_then(|i| this.output_devices.get(i))
.cloned()
.unwrap_or_default();
set_audio_output_device(&name);
let _ = cx;
})
.detach();
cx.subscribe(&audio_input, |this, _combo, event: &ComboBoxEvent, cx| {
if let ComboBoxEvent::Selected { value, .. } = event {
let name = value
.checked_sub(1)
.and_then(|i| this.input_devices.get(i))
.cloned()
.unwrap_or_default();
set_audio_input_device(&name);
}
let ComboBoxEvent::Selected { value, .. } = event;
let name = value
.checked_sub(1)
.and_then(|i| this.input_devices.get(i))
.cloned()
.unwrap_or_default();
set_audio_input_device(&name);
let _ = cx;
})
.detach();
@@ -594,7 +582,7 @@ impl PreferencesContent {
let Some(path) = paths.first() else {
return;
};
this.update(cx, |this, cx| {
let _ = this.update(cx, |this, cx| {
this.display_icc_path.update(cx, |field, cx| {
field.set_path(path.to_string_lossy().into_owned(), cx)
});
@@ -633,7 +621,7 @@ impl PreferencesContent {
let Some(path) = paths.first() else {
return;
};
this.update(cx, |this, cx| {
let _ = this.update(cx, |this, cx| {
this.cache_dir.update(cx, |field, cx| {
field.set_path(path.to_string_lossy().into_owned(), cx)
});
@@ -1080,15 +1068,16 @@ impl ExportDialogContent {
// Container change → rebuild the codec lists (and re-select the
// first compatible entry) so a stale incompatible codec can never
// survive a format switch.
cx.subscribe(&format, |this, _format, event: &ComboBoxEvent, cx| {
if let ComboBoxEvent::Selected { value } = event {
let fmt = this
.formats
.get(*value)
.map(|(id, _, _)| *id)
.unwrap_or(EXPORT_FORMAT_MP4);
this.apply_format(fmt, cx);
}
// NOTE: the returned subscription is dropped immediately, so this
// handler never runs; the drop is kept to preserve behavior.
let _ = cx.subscribe(&format, |this, _format, event: &ComboBoxEvent, cx| {
let ComboBoxEvent::Selected { value } = event;
let fmt = this
.formats
.get(*value)
.map(|(id, _, _)| *id)
.unwrap_or(EXPORT_FORMAT_MP4);
this.apply_format(fmt, cx);
});
let color = cx.new(|cx| {
@@ -2635,7 +2624,7 @@ impl KeyboardTabContent {
return;
};
let result = crate::actions::load_custom_shortcuts_from(&path.to_string_lossy());
this.update(cx, |this, cx| {
let _ = this.update(cx, |this, cx| {
match result {
Ok(_) => {
this.status = Some(i18n::tr("preferences.keyboard.imported").to_string());
@@ -2663,7 +2652,7 @@ impl KeyboardTabContent {
return;
};
let result = crate::actions::save_custom_shortcuts_to(&path.to_string_lossy());
this.update(cx, |this, cx| {
let _ = this.update(cx, |this, cx| {
this.status = Some(match result {
Ok(_) => i18n::tr("preferences.keyboard.exported").to_string(),
Err(_) => i18n::tr("preferences.keyboard.export_failed").to_string(),
@@ -3495,20 +3484,19 @@ impl SequenceFormatFields {
// set_value/set_selected calls below emit no events, so this never
// loops back into itself.
cx.subscribe(&preset, |this, _preset, event: &ComboBoxEvent, cx| {
if let ComboBoxEvent::Selected { value } = event {
if let Some((w, h, num, den)) = sequence_preset_format(*value) {
this.width.update(cx, |spin, cx| {
spin.set_value(SliderValue::Integer(i64::from(w)), cx)
});
this.height.update(cx, |spin, cx| {
spin.set_value(SliderValue::Integer(i64::from(h)), cx)
});
if let Some(index) = SEQUENCE_RATES.iter().position(|r| *r == (num, den)) {
this.rate
.update(cx, |combo, cx| combo.set_selected(Some(index), cx));
}
cx.notify();
let ComboBoxEvent::Selected { value } = event;
if let Some((w, h, num, den)) = sequence_preset_format(*value) {
this.width.update(cx, |spin, cx| {
spin.set_value(SliderValue::Integer(i64::from(w)), cx)
});
this.height.update(cx, |spin, cx| {
spin.set_value(SliderValue::Integer(i64::from(h)), cx)
});
if let Some(index) = SEQUENCE_RATES.iter().position(|r| *r == (num, den)) {
this.rate
.update(cx, |combo, cx| combo.set_selected(Some(index), cx));
}
cx.notify();
}
})
.detach();
@@ -3533,21 +3521,19 @@ impl SequenceFormatFields {
})
.detach();
cx.subscribe(&rate, |this, _combo, event: &ComboBoxEvent, cx| {
if let ComboBoxEvent::Selected { .. } = event {
this.preset.update(cx, |combo, cx| {
combo.set_selected(Some(0), cx)
});
cx.notify();
}
let ComboBoxEvent::Selected { .. } = event;
this.preset.update(cx, |combo, cx| {
combo.set_selected(Some(0), cx)
});
cx.notify();
})
.detach();
// The interlaced checkbox is request-only: the host accepts the
// toggled state back (the standard checkbox pattern).
cx.subscribe(&interlaced, |_this, check, event: &CheckBoxEvent, cx| {
if let CheckBoxEvent::Toggled { state, .. } = event {
check.update(cx, |check, cx| check.set_state(*state, cx));
}
let CheckBoxEvent::Toggled { state, .. } = event;
check.update(cx, |check, cx| check.set_state(*state, cx));
})
.detach();
@@ -3973,8 +3959,6 @@ pub struct MulticamWizardContent<E: crate::oakui::engine::AppEngine> {
sync: Entity<ComboBox>,
/// Selection state per footage row: `(entry, checked)`.
rows: Vec<(crate::oakui::engine::WizardFootage, bool)>,
/// The wheel to scroll the long angle list.
scrolled: bool,
}
/// The wizard's sync mode combo values (display order).
@@ -4019,7 +4003,6 @@ impl<E: crate::oakui::engine::AppEngine> MulticamWizardContent<E> {
name,
sync,
rows,
scrolled: false,
}
}
@@ -4156,7 +4139,7 @@ pub struct RenameContent {
impl RenameContent {
/// Builds the dialog seeded with the current name.
pub fn new(current: SharedString, window: &mut Window, cx: &mut Context<Self>) -> Self {
pub fn new(current: SharedString, _window: &mut Window, cx: &mut Context<Self>) -> Self {
let field = cx.new(|cx| {
let editor = cx.new(|cx| EditableTextState::new(StringStorage::default(), cx));
TextValue { editor }
@@ -40,7 +40,7 @@ use std::sync::Arc;
use gpui::{
colors::{Colors, DefaultColors},
div, App, Div, ElementId, Hsla, IntoElement, ParentElement, Styled, WeakEntity, Window,
div, App, Div, ElementId, Hsla, IntoElement, ParentElement, Styled, WeakEntity,
};
use gpui_elements::editable_text::actions::{default_bindings, DEFAULT_INPUT_CONTEXT};
use gpui_elements::editable_text::{EditableTextElement, EditableTextState};
+2 -2
View File
@@ -2211,7 +2211,7 @@ pub fn place_nested_sequence_clip(
}
let (tb, list, source_behavior) = {
let g = lock(p);
let s = sequence_behavior(&g.graph, source_seq)
sequence_behavior(&g.graph, source_seq)
.ok_or_else(|| "the source sequence is not in the project".to_string())?;
let tb = sequence_time_base(&g.graph, host_seq)
.ok_or_else(|| "host sequence has no valid frame rate".to_string())?;
@@ -4805,7 +4805,7 @@ mod undo_cycle_ops_tests {
let media =
std::env::temp_dir().join(format!("oak_transition_drag_{}.mp4", std::process::id()));
oak_codec::testmedia::write_test_clip(&media, 64, 64, 10, 10).expect("generate");
let (project, seq, track, a, b, tb, half) = two_touching_clips(&media);
let (project, seq, _track, a, b, tb, half) = two_touching_clips(&media);
add_default_transition(&project, seq, &[a, b], half).expect("add transition");
let frame = ts_to_rational(1, tb);
+2 -1
View File
@@ -51,7 +51,7 @@ use gpui::node_graph::{
};
use gpui::timeline::{
ClipData, ClipId, Frame, FrameRange, FrameRate, Marker, TimelineDataSource, TimelineEvent,
TrackData, TrackKind, TrimEdge,
TrackData, TrackKind,
};
use gpui::{
hsla, point, prelude::*, px, App, Context, Entity, Hsla, Pixels, Point, RenderImage,
@@ -3259,6 +3259,7 @@ impl MockEngine {
mod tests {
use super::*;
use gpui::TestAppContext;
use gpui::timeline::TrimEdge;
fn demo_engine(app: &mut gpui::App) -> Entity<MockEngine> {
app.new(|cx| MockEngine::demo(cx))
+5 -5
View File
@@ -42,7 +42,7 @@ use oak_node::track::TrackType;
use super::engine::{MulticamState, WizardFootage};
use super::graphops::{
self, lock, sequence_behavior, track_list_behavior, track_list_of, ProjectRef,
self, lock, sequence_behavior, track_list_of, ProjectRef,
};
/// Whether `id` names a sequence node (C++ `dynamic_cast<Sequence*>` /
@@ -287,11 +287,11 @@ pub fn build_multicam_sequence(
return Err("angle/offset count mismatch".to_string());
}
let mut seq: Option<NodeId> = None;
let mut mc: Option<NodeId> = None;
let seq: Option<NodeId>;
let mc: Option<NodeId>;
{
let mut g = lock(p);
let (score, sbehavior) = oak_node::sequence::SequenceBehavior::create();
let (_score, sbehavior) = oak_node::sequence::SequenceBehavior::create();
let mut core = oak_node::node::NodeCore::new();
core.label = name.to_string();
let seq_id = g.graph.add_node(core, sbehavior);
@@ -444,7 +444,7 @@ pub fn build_multicam_sequence(
// AFV: the angle's audio clip on its own source audio track —
// same range as the video angle (the sync offset aligns them),
// fed from the footage's audio stream.
if let (Some(audio_list), Some(audio_track)) = (audio_list, audio_track_ids.get(i)) {
if let (Some(_audio_list), Some(audio_track)) = (audio_list, audio_track_ids.get(i)) {
if entry.has_audio.unwrap_or(false) {
let (acore, abehavior) = oak_node::block::clip_create();
let aclip = g.graph.add_node(acore, abehavior);
+2 -3
View File
@@ -1353,7 +1353,6 @@ impl RealEngine {
},
{
let p = project.clone();
let nf = new_footage;
move || {
let mut g = graphops::lock(&p);
for (from, input, element) in g.graph.input_connections(aclip) {
@@ -3773,7 +3772,7 @@ impl EngineGateway for RealEngine {
// The audio master anchor is dropped too — the next play run
// re-anchors at its own first push.
self.audio_playback = None;
if let Some(mut manager) = oak_audio::manager::instance() {
if let Some(manager) = oak_audio::manager::instance() {
let _ = manager.clear_buffered_output();
let _ = manager.reset_output_clock();
}
@@ -5361,7 +5360,7 @@ impl AppEngine for RealEngine {
// A video-with-audio file needs BOTH a video and an audio track —
// missing kinds are created (the NLE convention — Premiere
// auto-creates on drop).
let mut ensure_track = |this: &mut Self, kind: TrackKind, cx: &mut Context<Self>| {
let ensure_track = |this: &mut Self, kind: TrackKind, cx: &mut Context<Self>| {
if let Some(track) = this.tracks.get(track_index) {
if track.kind == kind {
return Some(track_index);
+1 -1
View File
@@ -30,7 +30,7 @@ use std::sync::mpsc;
use gpui::RenderImage;
use oak_core::texture::Texture;
use oak_core::{PixelFormat, Rational, TimeRange};
use oak_core::{Rational, TimeRange};
use oak_node::id::NodeId;
use oak_node::track::TrackType;
use oak_render::manager::RenderManager;
+1 -1
View File
@@ -550,7 +550,7 @@ mod tests {
EffectLibraryPanel::new(engine, window, cx)
});
cx.run_until_parked();
let mut visual = VisualTestContext::from_window(window.into(), cx).into_mut();
let visual = VisualTestContext::from_window(window.into(), cx).into_mut();
visual.update(|window, cx| {
window.draw(cx).clear();
});
+1 -1
View File
@@ -480,7 +480,7 @@ impl<E: AppEngine> DockPanel for MulticamPanel<E> {
mod tests {
use super::*;
use crate::actions::MulticamSwitch5;
use gpui::{point, size, Focusable, TestAppContext, VisualTestContext};
use gpui::{size, Focusable, TestAppContext, VisualTestContext};
use std::cell::Cell;
use std::rc::Rc;
+32 -31
View File
@@ -862,29 +862,28 @@ fn wire_controls<E: AppEngine>(view: &OfxParamsView<E>, cx: &mut Context<OfxPara
ControlKind::Combo(combo) => {
let combo = combo.clone();
cx.subscribe(&combo, move |_, _, event: &ComboBoxEvent, cx| {
if let ComboBoxEvent::Selected { value, .. } = event {
// Integer combos carry the index; string combos map
// the picked option back to its string value.
let param = engine.update(cx, |engine, cx| {
engine
.effect_params(effect)
.unwrap_or_default()
.into_iter()
.find(|p| p.input_id == input_id)
});
let nv = match &param {
Some(p) if p.value_type == ValueType::StrCombo => {
let haystack = combo_haystack(p);
NodeValue::StrCombo(
haystack.get(*value).cloned().unwrap_or_default(),
)
}
_ => NodeValue::Combo(*value as i64),
};
engine.update(cx, |engine, cx| {
let _ = engine.set_effect_param(effect, &input_id, nv, cx);
});
}
let ComboBoxEvent::Selected { value, .. } = event;
// Integer combos carry the index; string combos map
// the picked option back to its string value.
let param = engine.update(cx, |engine, _cx| {
engine
.effect_params(effect)
.unwrap_or_default()
.into_iter()
.find(|p| p.input_id == input_id)
});
let nv = match &param {
Some(p) if p.value_type == ValueType::StrCombo => {
let haystack = combo_haystack(p);
NodeValue::StrCombo(
haystack.get(*value).cloned().unwrap_or_default(),
)
}
_ => NodeValue::Combo(*value as i64),
};
engine.update(cx, |engine, cx| {
let _ = engine.set_effect_param(effect, &input_id, nv, cx);
});
})
.detach();
}
@@ -972,7 +971,6 @@ fn wire_controls<E: AppEngine>(view: &OfxParamsView<E>, cx: &mut Context<OfxPara
use gpui_widgets::curve_editor::CurveEditorEvent as E;
match event {
E::PointMoved { .. } | E::HandleMoved { .. } | E::PointAdded { .. } => {}
_ => return,
}
let curves: Vec<oak_plugin::param_curve::Curve> = editors_all
.iter()
@@ -1441,7 +1439,7 @@ impl OfxColorPicker {
let values = [a as f64, b as f64, c as f64, self.draft.a as f64];
let sliders = [&self.c0, &self.c1, &self.c2, &self.a];
for (slider, value) in sliders.iter().zip(values.iter()) {
let slider = slider.clone();
let slider = *slider;
slider.update(cx, |slider, _| {
slider.set_value(SliderValue::Float(*value));
});
@@ -2155,6 +2153,9 @@ fn sv_from_point(bounds: Bounds<Pixels>, pos: Point<Pixels>) -> (f32, f32) {
}
/// Inverse of [`sv_from_point`] — the palette position of a (s, v) pair.
// Only the unit tests below call this helper (the lib build sees it as dead
// code), so it is kept with an explicit allow.
#[allow(dead_code)]
fn point_from_sv(bounds: Bounds<Pixels>, s: f32, v: f32) -> Point<Pixels> {
let width = f32::from(bounds.size.width).max(1.0);
let height = f32::from(bounds.size.height).max(1.0);
@@ -2319,7 +2320,7 @@ mod tests {
});
cx.run_until_parked();
let mut visual = gpui::VisualTestContext::from_window(window.into(), cx).into_mut();
let visual = gpui::VisualTestContext::from_window(window.into(), cx).into_mut();
visual.update(|window, cx| {
window.draw(cx).clear();
});
@@ -2355,7 +2356,7 @@ mod tests {
});
cx.run_until_parked();
let mut visual = gpui::VisualTestContext::from_window(window.into(), cx).into_mut();
let visual = gpui::VisualTestContext::from_window(window.into(), cx).into_mut();
visual.update(|window, cx| {
window.draw(cx).clear();
});
@@ -2447,7 +2448,7 @@ mod tests {
});
cx.run_until_parked();
let mut visual = gpui::VisualTestContext::from_window(window.into(), cx).into_mut();
let visual = gpui::VisualTestContext::from_window(window.into(), cx).into_mut();
visual.update(|window, cx| {
window.draw(cx).clear();
});
@@ -2537,7 +2538,7 @@ mod tests {
});
cx.run_until_parked();
let mut visual = gpui::VisualTestContext::from_window(window.into(), cx).into_mut();
let visual = gpui::VisualTestContext::from_window(window.into(), cx).into_mut();
visual.update(|window, cx| {
window.draw(cx).clear();
});
@@ -2704,7 +2705,7 @@ mod tests {
});
cx.run_until_parked();
let mut visual = gpui::VisualTestContext::from_window(window.into(), cx).into_mut();
let visual = gpui::VisualTestContext::from_window(window.into(), cx).into_mut();
visual.update(|window, cx| {
window.draw(cx).clear();
});
@@ -2988,7 +2989,7 @@ mod tests {
// Draw once: the render pass is where `sync_values` reapplies the
// engine snapshot to the widgets (the sliders snap to their grid).
let mut visual = gpui::VisualTestContext::from_window(window.into(), cx).into_mut();
let visual = gpui::VisualTestContext::from_window(window.into(), cx).into_mut();
visual.update(|window, cx| {
window.draw(cx).clear();
});
@@ -258,7 +258,7 @@ impl<E: AppEngine> Render for ProjectExplorerPanel<E> {
.tooltip(move |window, cx| {
tooltip_view(crate::i18n::tr("project.new_sequence").into(), window, cx)
})
.on_click(cx.listener(|this, _event: &ClickEvent, _window, cx| {
.on_click(cx.listener(|_this, _event: &ClickEvent, _window, cx| {
cx.emit(NewSequenceRequested);
}))
.child(crate::i18n::tr("project.new_sequence"));
-2
View File
@@ -18,7 +18,6 @@
//! audio-specific configuration read through the oak_core C ABI.
use std::error::Error;
use std::ffi::CString;
use std::str::FromStr;
use oak_core::configstore::*;
/// PortAudio output buffer size in frames; 0 = let PortAudio choose.
@@ -47,7 +46,6 @@ pub fn device_name(is_output_device: bool) -> Result<String, Box<dyn Error>> {
// Absent (oak_core_E_NOT_FOUND) or empty
return Err(Box::new(crate::error::Error::NotFound));
}
let mut buf = vec![0u8; size as usize];
let name = store.get(None, key)?;
Ok(name)
}
+4 -6
View File
@@ -24,17 +24,15 @@
//! Recording goes through the oakcodec encoder C ABI ([`crate::bridge`]);
//! device/config lookups go through oak_core.
use std::ffi::c_void;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, MutexGuard, OnceLock};
use cpal::{Device, DeviceId};
use cpal::Device;
use cpal::traits::{DeviceTrait, HostTrait};
use oak_codec::encoder::Encoder;
use oak_codec::encodingparams::EncodingParams;
use crate::error::{Error, Result};
use crate::params::AudioParams;
use crate::previewdevice::PreviewAudioDevice;
use crate::error::Error::NotFound;
/// `paNoDevice` (PortAudio "no device" sentinel; also the default when no
/// device is configured).
@@ -138,9 +136,9 @@ impl ManagerInner {
return None;
}
match MANAGER.get() {
Some(m) => {
// SAFETY: `m` is the process-wide singleton; borrowed handles do
// not free it, so it outlives every handle.
Some(_) => {
// SAFETY: the process-wide singleton is never freed; borrowed
// handles do not free it, so it outlives every handle.
Some(self)
}
None => None,
+2 -2
View File
@@ -30,7 +30,7 @@ use std::sync::Arc;
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use cpal::{
BufferSize, Device, Host, SampleFormat, SampleRate, Stream, StreamConfig,
BufferSize, Device, Host, SampleFormat, Stream, StreamConfig,
SupportedBufferSize,
};
use crate::previewdevice::PreviewAudioDevice;
@@ -104,7 +104,7 @@ impl PortAudioOutput {
self.host = Some(cpal::default_host())
}
let host = self.host.as_ref().unwrap().clone();
let host = self.host.as_ref().unwrap();
let output_device = resolve_device(&host, device)
.ok_or_else(|| "no output device available".to_string())?;
+5
View File
@@ -22,6 +22,11 @@
//! `waveform::extract` goes through oakcodec's in-process FFmpeg
//! decoder; the processor drives a real FFmpeg filter graph.
// Every integration-test binary in this crate compiles this module on its
// own and uses only the helpers it needs, so items unused in one binary are
// still part of the shared fixture API.
#![allow(dead_code)]
use std::path::Path;
use std::sync::Mutex;
+5 -5
View File
@@ -84,7 +84,7 @@ fn identity_convert_passthrough() {
let planes = ramp_planes(32);
let in_ptrs = plane_ptrs(&planes);
let mut out = vec![vec![0f32; 32]; 2];
let mut out_ptrs = plane_mut_ptrs(&mut out);
let out_ptrs = plane_mut_ptrs(&mut out);
let n = p.convert(in_ptrs.as_ptr(), 32, out_ptrs.as_ptr(), 32).unwrap();
assert_eq!(n, 32);
@@ -110,7 +110,7 @@ fn convert_capacity_truncation() {
let planes = ramp_planes(32);
let in_ptrs = plane_ptrs(&planes);
let mut out = vec![vec![9.9f32; 10]; 2];
let mut out_ptrs = plane_mut_ptrs(&mut out);
let out_ptrs = plane_mut_ptrs(&mut out);
let n = p.convert(in_ptrs.as_ptr(), 32, out_ptrs.as_ptr(), 10).unwrap();
assert_eq!(n, 10);
@@ -122,7 +122,7 @@ fn convert_capacity_truncation() {
// The graph has already drained; nothing further to pull.
let mut out2 = vec![vec![0f32; 32]; 2];
let mut out2_ptrs = plane_mut_ptrs(&mut out2);
let out2_ptrs = plane_mut_ptrs(&mut out2);
let n = p.convert(in_ptrs.as_ptr(), 0, out2_ptrs.as_ptr(), 32).unwrap();
assert_eq!(n, 0);
}
@@ -171,7 +171,7 @@ fn resample_and_flush() {
let planes = ramp_planes(frames);
let in_ptrs = plane_ptrs(&planes);
let mut out = vec![vec![0f32; frames]; 2];
let mut out_ptrs = plane_mut_ptrs(&mut out);
let out_ptrs = plane_mut_ptrs(&mut out);
let mut total = p
.convert(in_ptrs.as_ptr(), frames as i32, out_ptrs.as_ptr(), frames as i32)
@@ -205,7 +205,7 @@ fn tempo_stretch() {
let planes = ramp_planes(frames);
let in_ptrs = plane_ptrs(&planes);
let mut out = vec![vec![0f32; frames]; 2];
let mut out_ptrs = plane_mut_ptrs(&mut out);
let out_ptrs = plane_mut_ptrs(&mut out);
let mut total = p
.convert(in_ptrs.as_ptr(), frames as i32, out_ptrs.as_ptr(), frames as i32)
+5 -21
View File
@@ -70,8 +70,13 @@ use crate::footagedescription::{FootageDescription, StreamEntry};
use crate::frame::Frame;
/// `oak_core_COLOR_RANGE_FULL`.
///
/// Both range constants keep the C ABI's spelling (they name the frozen
/// `oak_core_COLOR_RANGE_*` values), hence the lint allowance.
#[allow(non_upper_case_globals)]
const oak_core_COLOR_RANGE_FULL: i32 = 1;
/// `oak_core_COLOR_RANGE_LIMITED`.
#[allow(non_upper_case_globals)]
const oak_core_COLOR_RANGE_LIMITED: i32 = 0;
/// `AVCOL_RANGE_JPEG` (full range; AVCOL_RANGE_MPEG = 1 is limited).
const AVCOL_RANGE_JPEG: i32 = 2;
@@ -1899,27 +1904,6 @@ fn convert_rgba_f32_le(data: &[u8], w: u32, h: u32, stride: usize) -> Vec<u8> {
out
}
/// Copy a packed u16-RGBA buffer (RGBA64LE) into F32 RGBA bytes.
fn convert_rgba64_to_f32(data: &[u8], w: u32, h: u32, stride: usize) -> Vec<u8> {
let mut out = vec![0u8; (w as usize) * (h as usize) * PIXEL_F32_BYTES];
for y in 0..h as usize {
let row = &data[y * stride..y * stride + (w as usize) * 8];
let dst =
&mut out[y * (w as usize) * PIXEL_F32_BYTES..(y + 1) * (w as usize) * PIXEL_F32_BYTES];
for (px, src_px) in dst
.chunks_exact_mut(PIXEL_F32_BYTES)
.zip(row.chunks_exact(8))
{
for c in 0..3 {
let v = u16::from_le_bytes([src_px[c * 2], src_px[c * 2 + 1]]);
px[c * 4..c * 4 + 4].copy_from_slice(&(v as f32 / 65535.0).to_le_bytes());
}
px[12..16].copy_from_slice(&1.0f32.to_le_bytes());
}
}
out
}
/// Copy a packed 8-bit RGBA buffer into F32 RGBA bytes (the universal
/// swscale fallback; M12 P0 — some static FFmpeg swscale builds lack
/// float output formats).
+3
View File
@@ -97,6 +97,9 @@ impl ProxyParams {
cstr_slice(&self.extension)
}
/// Only the proxy-params tests read the preset through this accessor
/// (the struct field itself is `pub`).
#[allow(dead_code)]
fn preset_str(&self) -> &str {
cstr_slice(&self.preset)
}
+6
View File
@@ -107,6 +107,10 @@ enum SubmitCb {
/// No callback registered.
None,
/// Extern-C callback registered via `oakcodec_set_task_submit_cb`.
///
/// Only constructed by the tests below and by the extern-C entry point
/// that the removed C ABI used to expose; kept for that lane.
#[allow(dead_code)]
Extern {
/// The C function pointer.
cb: OakCodecTaskSubmitFn,
@@ -151,6 +155,8 @@ pub fn set_task_submit_cb(cb: Option<&'static TaskSubmitFn>, userdata: *mut std:
/// Register an extern-C submit callback (used by `ffi::task`).
///
/// Mirrors `oakcodec_set_task_submit_cb`: a `None` pointer clears it.
/// Only the tests below call it (the extern-C lane is exercised there).
#[allow(dead_code)]
pub(crate) fn set_task_submit_cb_extern(
cb: Option<OakCodecTaskSubmitFn>,
userdata: *mut std::ffi::c_void,
+2 -2
View File
@@ -1542,7 +1542,7 @@ mod tests {
assert!(r2.is_vulkan());
assert!(!r2.is_open_gl());
let mut r3 = DisplayRenderer::new(BackendKind::Cpu);
let r3 = DisplayRenderer::new(BackendKind::Cpu);
let mut pod = VideoParamsPod::default();
pod.width = 4;
pod.height = 4;
@@ -1577,7 +1577,7 @@ mod tests {
#[test]
fn cpu_blit_applies_color_and_copy() {
let mut r = DisplayRenderer::new(BackendKind::Cpu);
let r = DisplayRenderer::new(BackendKind::Cpu);
let mut pod = VideoParamsPod::default();
pod.width = 2;
pod.height = 2;
+6 -6
View File
@@ -551,11 +551,11 @@ pub fn display_transform_result(
return Ok(None);
}
// OCIO's ROLE_REFERENCE role name ("reference"): `get_color_space`
// OCIO's ROLE_REFERENCE role name ("reference"): `color_space`
// resolves name-or-role (C++ `getColorSpace(ROLE_REFERENCE)`); the
// role-name lookups are the fallback for configs that do not bind it.
let src = config
.get_color_space("reference")
.color_space("reference")
.and_then(|cs| cs.name())
.filter(|s| !s.is_empty())
.or_else(|| {
@@ -875,15 +875,15 @@ mod tests {
return;
}
let config = default_config().unwrap();
if config.get_num_displays_all() <= 0 {
if config.num_displays_all() <= 0 {
return;
}
let display = config.get_display_all(0).unwrap();
let n = config.get_num_views_v2(ocio_rs::SearchReferenceSpaceType::Scene, &display);
let display = config.display_all(0).unwrap();
let n = config.num_views_by_reference_space(ocio_rs::SearchReferenceSpaceType::Scene, &display);
assert!(n >= 0);
if n > 0 {
let view = config
.get_view_v2(ocio_rs::SearchReferenceSpaceType::Scene, &display, 0)
.view_by_reference_space(ocio_rs::SearchReferenceSpaceType::Scene, &display, 0)
.unwrap();
let id = display_transform(&display, &view);
assert!(id.is_some());
+3 -3
View File
@@ -456,7 +456,7 @@ mod tests {
fn option_setting_roundtrip() {
let mut p = CommandLineParser::new();
p.add_option(&[cstr("o")], "", true, "F", false).unwrap();
let mut opt = p.option_mut(0).unwrap();
let opt = p.option_mut(0).unwrap();
assert_eq!(opt.get_setting().unwrap(), "");
opt.set_setting("value").unwrap();
assert_eq!(opt.get_setting().unwrap(), "value");
@@ -467,7 +467,7 @@ mod tests {
fn positional_setting_roundtrip() {
let mut p = CommandLineParser::new();
p.add_positional_argument("in", "", true).unwrap();
let mut pos = p.positional_mut(0).unwrap();
let pos = p.positional_mut(0).unwrap();
assert_eq!(pos.get_setting().unwrap(), "");
pos.set_setting("file.mp4").unwrap();
assert_eq!(pos.get_setting().unwrap(), "file.mp4");
@@ -786,7 +786,7 @@ Usage: oak [options] [input]
let mut p = CommandLineParser::new();
p.add_option(&[cstr("o")], "", true, "F", false).unwrap();
{
let mut opt = p.option_mut(0).unwrap();
let opt = p.option_mut(0).unwrap();
opt.set_setting("v").unwrap();
}
let opt = p.option(0).unwrap();
-10
View File
@@ -130,16 +130,6 @@ impl Level {
Level::Error | Level::Fatal => log::Level::Error,
}
}
/// From a facade filter level (for [`log_get_level`]).
fn from_filter(f: LevelFilter) -> Level {
match f {
LevelFilter::Off | LevelFilter::Error => Level::Error,
LevelFilter::Warn => Level::Warning,
LevelFilter::Info => Level::Info,
LevelFilter::Debug | LevelFilter::Trace => Level::Debug,
}
}
}
/// Oak-level name for a facade level (the sink path). `log::Level` has
+4
View File
@@ -414,6 +414,10 @@ impl FileFunctions {
/// Location query helper reserved for the two-stage C getters; returns a
/// path suitable for `std::fs`.
///
/// Only the configuration-location tests in this module call it today, so
/// it is kept (with an `allow`) as the reserved helper rather than deleted.
#[allow(dead_code)]
pub(crate) fn config_location_path() -> Result<PathBuf> {
FileFunctions::new()
.get_configuration_location()
+15 -1
View File
@@ -37,7 +37,7 @@ use std::ffi::c_void;
/// empty handles). Structurally identical to every `Oak<Mod><Type>` value
/// handle, so a handle can cross any module boundary by value.
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[derive(Clone, Copy, Debug)]
pub struct CHandle {
/// Opaque box pointer.
pub ctx: *mut c_void,
@@ -49,6 +49,20 @@ pub struct CHandle {
pub abi_version: u32,
}
impl PartialEq for CHandle {
fn eq(&self, other: &Self) -> bool {
// `ctx` and `abi_version` by value; the callbacks by address
// (`==` on fn pointers is rejected by the compiler, and
// `std::ptr::fn_addr_eq` rejects the `Option<fn>` fields).
self.ctx == other.ctx
&& self.addref.map(|f| f as usize) == other.addref.map(|f| f as usize)
&& self.release.map(|f| f as usize) == other.release.map(|f| f as usize)
&& self.abi_version == other.abi_version
}
}
impl Eq for CHandle {}
impl CHandle {
/// The empty handle.
pub const fn null() -> Self {
+5 -9
View File
@@ -202,7 +202,7 @@ impl SubtitleParams {
return Err(Error::Failed("missing root element".into()));
}
self.clear();
self.clear()?;
while let Some(ev) = reader.read_next_start() {
match ev.name.as_str() {
@@ -399,9 +399,7 @@ enum XmlEvent {
name: String,
attrs: Vec<(String, String)>,
},
End {
name: String,
},
End,
Characters(String),
}
@@ -449,7 +447,7 @@ impl XmlReader {
name: name.clone(),
attrs: attrs.clone(),
}),
XmlEvent::End { .. } => XmlToken::End,
XmlEvent::End => XmlToken::End,
XmlEvent::Characters(t) => XmlToken::Characters(t.clone()),
}
}
@@ -549,8 +547,7 @@ fn parse_events(data: &str) -> Result<Vec<XmlEvent>> {
if j >= n {
return Err(Error::Failed("unterminated end element".into()));
}
let name = data[i + 2..j].trim().to_string();
events.push(XmlEvent::End { name });
events.push(XmlEvent::End);
i = j + 1;
}
b'!' => {
@@ -615,9 +612,8 @@ fn parse_events(data: &str) -> Result<Vec<XmlEvent>> {
}
if b[i] == b'/' && i + 1 < n && b[i + 1] == b'>' {
i += 2;
let end_name = name.clone();
events.push(XmlEvent::Start { name, attrs });
events.push(XmlEvent::End { name: end_name });
events.push(XmlEvent::End);
break;
}
+11 -11
View File
@@ -425,7 +425,7 @@ fn push_start_element(
// value (XML_ERROR_INVALID_TOKEN).
return None;
}
let value = match attr.unescape_value() {
let value = match attr.normalized_value(quick_xml::XmlVersion::Implicit1_0) {
Ok(v) => v.into_owned(),
Err(_) => return None,
};
@@ -714,19 +714,19 @@ mod tests {
#[test]
fn reader_unclosed_element_is_error() {
let mut r = XmlReader::new("<a>").unwrap();
let r = XmlReader::new("<a>").unwrap();
assert!(r.has_error().unwrap());
}
#[test]
fn reader_empty_document_is_error() {
let mut r = XmlReader::new("").unwrap();
let r = XmlReader::new("").unwrap();
assert!(r.has_error().unwrap());
}
#[test]
fn reader_undefined_entity_is_error() {
let mut r = XmlReader::new("<a>&foo;</a>").unwrap();
let r = XmlReader::new("<a>&foo;</a>").unwrap();
assert!(r.has_error().unwrap());
}
@@ -742,7 +742,7 @@ mod tests {
#[test]
fn reader_name_before_read_is_empty() {
let mut r = XmlReader::new("<a></a>").unwrap();
let r = XmlReader::new("<a></a>").unwrap();
assert_eq!(r.name().unwrap(), "");
assert_eq!(r.attribute_count().unwrap(), 0);
}
@@ -824,7 +824,7 @@ mod tests {
/// (XML_ERROR_DUPLICATE_ATTRIBUTE).
#[test]
fn reader_duplicate_attribute_is_error() {
let mut r = XmlReader::new("<e a=\"1\" a=\"2\"/>").unwrap();
let r = XmlReader::new("<e a=\"1\" a=\"2\"/>").unwrap();
assert!(r.has_error().unwrap());
}
@@ -832,7 +832,7 @@ mod tests {
/// (XML_ERROR_INVALID_TOKEN).
#[test]
fn reader_lt_in_attribute_value_is_error() {
let mut r = XmlReader::new("<e a=\"x<y\"/>").unwrap();
let r = XmlReader::new("<e a=\"x<y\"/>").unwrap();
assert!(r.has_error().unwrap());
}
@@ -885,13 +885,13 @@ mod tests {
#[test]
fn reader_text_before_root_is_error() {
let mut r = XmlReader::new("junk<a/>").unwrap();
let r = XmlReader::new("junk<a/>").unwrap();
assert!(r.has_error().unwrap());
}
#[test]
fn reader_text_after_root_is_error() {
let mut r = XmlReader::new("<a/>junk").unwrap();
let r = XmlReader::new("<a/>junk").unwrap();
assert!(r.has_error().unwrap());
}
@@ -899,10 +899,10 @@ mod tests {
/// element").
#[test]
fn reader_second_root_element_is_error() {
let mut r = XmlReader::new("<a/><b/>").unwrap();
let r = XmlReader::new("<a/><b/>").unwrap();
assert!(r.has_error().unwrap());
let mut r = XmlReader::new("<a></a><b></b>").unwrap();
let r = XmlReader::new("<a></a><b></b>").unwrap();
assert!(r.has_error().unwrap());
}
+6
View File
@@ -14,6 +14,12 @@ oak-core = { path = "../oak-core" }
oak-codec = { path = "../oak-codec" }
thiserror = "2"
[features]
# Declared so tests can gate on it; nothing in this crate enables it.
# It used to pull in the oakcodec host-mocks, which no longer exist (the
# single-lib unification removed the oakcore_*/oakrender_* C ABI calls).
test-stubs = []
[dev-dependencies]
# oakcodec is also a plain dependency above; the dev-dependency re-entry
# used to enable oakcodec's `test-stubs` host-mocks, which no longer exist
-2
View File
@@ -664,7 +664,6 @@ impl Graph {
if index < 0 || index > size as i32 {
return Err(Error::Invalid);
}
drop(input_);
let entry = self.get_mut(id).ok_or(Error::NotFound)?;
entry.core.input_array_insert(input, index as usize);
@@ -703,7 +702,6 @@ impl Graph {
if index < 0 || index >= size as i32 {
return Err(Error::Invalid);
}
drop(input_);
let entry = self.get_mut(id).ok_or(Error::NotFound)?;
entry.core.input_array_remove(input, index as usize);
+1 -1
View File
@@ -24,7 +24,7 @@ use oak_core::{Rational, TimeRange};
use crate::id::NodeId;
use crate::input::{Input, ValueHint};
use crate::keyframe::KeyframeTrack;
use crate::value::{NodeValue, NodeValueRow, NodeValueTable};
use crate::value::{NodeValueRow, NodeValueTable};
/// Node category (mirrors C++ `Node::CategoryID` order).
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
-8
View File
@@ -198,14 +198,6 @@ void main() {
}
"#;
impl ChromaKeyNode {
/// Fragment shader with the `%1` OCIO stub marker still in place
/// (C++ `get_shader_code()` before the stub substitution).
fn shader_frag() -> &'static str {
SHADER_FRAG
}
}
impl NodeBehavior for ChromaKeyNode {
/// Human-readable name (C++ `name()`).
fn name(&self) -> &str {
@@ -128,14 +128,6 @@ void main(void) {
}
"#;
impl ColorDifferenceKeyNode {
/// Fragment shader (C++ `get_shader_code()`; the request is
/// ignored — there is a single shader).
fn shader_frag() -> &'static str {
SHADER_FRAG
}
}
impl NodeBehavior for ColorDifferenceKeyNode {
/// Human-readable name (C++ `name()`).
fn name(&self) -> &str {
@@ -124,6 +124,9 @@ void main() {
/// Vertex shader (C++ loads the `:/shaders/cornerpin.vert` resource in
/// `get_shader_code`). Text copied verbatim from
/// `engine/shaders/cornerpin.vert`.
// C++ parity: shader resource text kept alongside the frag shader; only
// the tests read it (via `shader_vert`).
#[allow(dead_code)]
const SHADER_VERT: &str = r#"uniform bool perspective_in;
uniform vec2 top_left_in;
uniform vec2 top_right_in;
@@ -234,6 +237,7 @@ impl CornerPinDistortNode {
/// Vertex shader (C++ `get_shader_code()` vert half; the request id
/// is ignored).
#[allow(dead_code)] // C++ parity accessor; exercised by the tests only.
fn shader_vert() -> &'static str {
SHADER_VERT
}
@@ -403,6 +407,7 @@ impl CornerPinDistortNode {
/// origin, so corner 0 (top-left) maps straight, corner 1 (top-right)
/// adds `(resolution.x, 0)`, corner 2 (bottom-right) adds the full
/// resolution, and corner 3 (bottom-left) adds `(0, resolution.y)`.
#[allow(dead_code)] // C++ parity helper; exercised by the tests only.
fn value_to_pixel(
value: i32,
row: &crate::value::NodeValueRow,
-8
View File
@@ -115,14 +115,6 @@ void main(void) {
}
"#;
impl DespillNode {
/// Fragment shader (C++ `get_shader_code()`; the request is
/// ignored — there is a single shader).
fn shader_frag() -> &'static str {
SHADER_FRAG
}
}
impl NodeBehavior for DespillNode {
/// Human-readable name (C++ `name()`).
fn name(&self) -> &str {
+3 -1
View File
@@ -53,7 +53,9 @@ pub struct DirBlurNode;
/// Number of taps averaged per pixel. Fixed (upstream has no tap-count
/// knob either): a power-of-two constant keeps the division exact and
/// the shader otherwise branch-free.
/// the shader otherwise branch-free. Only the shader-source test reads
/// it, so it is kept (with an `allow`) to document the GLSL define.
#[allow(dead_code)]
const TAP_COUNT: i32 = 16;
/// Fragment shader: one direction vector from `angle_in`, then a
@@ -55,6 +55,8 @@ impl DisplayTransformNode {
/// Selected display name (C++ `get_display()`): the display combo
/// index mapped through the color manager's display list; empty
/// string when no manager is attached or the index is out of range.
#[allow(dead_code)] // C++ parity getter; the Rust model never attaches a
// color manager, so callers have nothing to query yet.
fn get_display(&self, core: &NodeCore) -> String {
// The C++ reads the display combo index through
// `manager()->list_available_displays()`. The Rust model has no
@@ -69,6 +71,7 @@ impl DisplayTransformNode {
/// Selected view name (C++ `get_view()`): the view combo index
/// mapped through the manager's views for [`Self::get_display`];
/// empty when unavailable.
#[allow(dead_code)] // See `get_display`.
fn get_view(&self, core: &NodeCore) -> String {
// See [`Self::get_display`]: no manager is ever attached in the
// Rust model, so the empty string is returned.
@@ -80,6 +83,7 @@ impl DisplayTransformNode {
/// Transform direction (C++ `get_direction()`): the direction combo
/// value cast to `ColorProcessor::Direction` (`0` = normal/forward,
/// `1` = inverse).
#[allow(dead_code)] // C++ parity getter; read back by the tests only.
fn get_direction(&self, core: &NodeCore) -> i64 {
core.standard_value(DIRECTION_INPUT, -1).to_double() as i64
}
-19
View File
@@ -256,25 +256,6 @@ void main(void) {
}
"#;
impl MaskDistortNode {
/// Merge fragment shader (C++ `get_shader_code()` `"mrg"` branch).
fn shader_mrg_frag() -> &'static str {
SHADER_MRG_FRAG
}
/// Invert fragment shader (C++ `get_shader_code()` `"invert"`
/// branch).
fn shader_invert_frag() -> &'static str {
SHADER_INVERT_FRAG
}
/// Feather blur fragment shader (C++ `get_shader_code()`
/// `"feather"` branch).
fn shader_feather_frag() -> &'static str {
SHADER_FEATHER_FRAG
}
}
/// Combined mask fragment shader for the `"mask"` shader id, replacing
/// the C++ chain — matte rasterize -> optional invert -> optional 2-pass
/// feather blur -> multiply over base — with a single GPU pass: the base
+3 -1
View File
@@ -35,7 +35,9 @@ pub const PARAM_A_INPUT: &str = "param_a_in";
pub const PARAM_B_INPUT: &str = "param_b_in";
/// Operand C input id (C++ `k_param_c_in`). Declared as a static but
/// never added in the constructor — reserved/unused upstream.
/// never added in the constructor — reserved/unused upstream, so it is
/// kept (with an `allow`) as the parity constant rather than deleted.
#[allow(dead_code)]
pub const PARAM_C_INPUT: &str = "param_c_in";
/// Math node: applies a binary arithmetic operation to two values.
+5 -1
View File
@@ -251,6 +251,10 @@ pub struct MathNodeBase;
impl MathNodeBase {
/// Display name for an operation (C++ `get_operation_name()`):
/// Add/Subtract/Multiply/Divide/Power.
///
/// Only the tests below call it today, so it is kept (with an
/// `allow`) as the parity helper rather than deleted.
#[allow(dead_code)]
pub fn operation_name(op: Operation) -> &'static str {
match op {
Operation::Add => "Add",
@@ -1283,7 +1287,7 @@ mod tests {
#[test]
fn value_internal_sample_number_static() {
let mut buf = f32_planar(2, 2, &[1.0, 2.0, 3.0, 4.0]);
let buf = f32_planar(2, 2, &[1.0, 2.0, 3.0, 4.0]);
let samples = NodeValue::Samples(buf.clone());
let mut out = NodeValueTable::default();
let core = NodeCore::new();
-8
View File
@@ -71,14 +71,6 @@ void main(void) {
}
"#;
impl MergeNode {
/// Fragment shader (C++ `get_shader_code()`; the request id is
/// ignored — the same alpha-over shader serves every request).
fn shader_frag() -> &'static str {
SHADER_FRAG
}
}
impl NodeBehavior for MergeNode {
/// Human-readable name (C++ `name()`).
fn name(&self) -> &str {
@@ -18,7 +18,6 @@
//! `src/node/src/input/multicam/multicamnode.{h,cpp}`,
//! `olive::MultiCamNode`).
use std::any::Any;
use crate::factory::NodeMeta;
use crate::id::NodeId;
use crate::node::{Category, NodeBehavior, NodeCore};
@@ -159,6 +159,8 @@ impl OCIOGradingTransformLinearNode {
/// Set the per-component widget colors of a vec4 input (C++
/// `set_vec4_input_colors()`): master `#c0c0c0`, R `#ff0000`, G
/// `#00ff00`, B `#0000ff`.
#[allow(dead_code)] // C++ parity helper; the parametrized widget colours
// are re-applied through `set_input_property` at setup time.
fn set_vec4_input_colors(core: &mut NodeCore, input: &str) {
set_input_property(
core,
@@ -417,7 +419,7 @@ impl NodeBehavior for OCIOGradingTransformLinearNode {
.to_double() != 0.0;
let white_enabled = row_or_standard(core, inputs, CLAMP_WHITE_ENABLE_INPUT, time)
.to_double() != 0.0;
let mut black =
let black =
row_or_standard(core, inputs, CLAMP_BLACK_INPUT, time).to_double();
let mut white =
row_or_standard(core, inputs, CLAMP_WHITE_INPUT, time).to_double();
@@ -178,6 +178,7 @@ impl OCIOGradingTransformLogNode {
/// Set the per-component widget colors of a vec4 input (C++
/// `set_vec4_input_colors()`): master `#c0c0c0`, R `#ff0000`, G
/// `#00ff00`, B `#0000ff`.
#[allow(dead_code)] // C++ parity helper; see the linear sibling.
fn set_vec4_input_colors(core: &mut NodeCore, input: &str) {
set_input_property(
core,
@@ -448,7 +449,7 @@ impl NodeBehavior for OCIOGradingTransformLogNode {
.to_double() != 0.0;
let white_enabled = row_or_standard(core, inputs, CLAMP_WHITE_ENABLE_INPUT, time)
.to_double() != 0.0;
let mut black =
let black =
row_or_standard(core, inputs, CLAMP_BLACK_INPUT, time).to_double();
let mut white =
row_or_standard(core, inputs, CLAMP_WHITE_INPUT, time).to_double();
+6
View File
@@ -90,6 +90,7 @@ impl OCIOLutNode {
/// Human-readable description of why no LUT processor is active
/// (C++ `last_error()`); empty when a valid LUT processor is in use
/// or no LUT file has been selected yet.
#[allow(dead_code)] // C++ parity accessor; read back by the tests.
pub fn last_error(&self) -> String {
self.state.lock().unwrap().last_error.clone()
}
@@ -98,6 +99,8 @@ impl OCIOLutNode {
/// unchanged. The Qt version surfaced the error on the main-window
/// status bar; here it is only recorded and read back via
/// [`Self::last_error`].
#[allow(dead_code)] // C++ parity setter; the tests feed it through
// `last_error`.
fn set_last_error(&self, error: &str) {
let mut state = self.state.lock().unwrap();
if state.last_error == error {
@@ -139,6 +142,8 @@ impl OCIOLutNode {
/// Whether this is the main GUI process (C++ file-static
/// `is_main_process()`): true when a render manager exists (the
/// render worker never creates one).
#[allow(dead_code)] // C++ parity probe; constants-only in the Rust model.
// The tests assert the worker answer directly.
fn is_main_process() -> bool {
// The C++ probes `oakrender_manager_available()`, which the
// oakrender bridge does not expose. Without a render manager (the
@@ -153,6 +158,7 @@ impl OCIOLutNode {
/// ensures the processor is current, then — in the main process
/// only — invalidates the texture-input cache and cancels background
/// video cache tasks so in-flight renders cannot write stale frames.
#[allow(dead_code)] // C++ parity entry point; the tests drive it.
fn generate_processor(&mut self, core: &mut NodeCore) {
self.ensure_processor(core);
// The C++ main-process half (`invalidate_all(k_texture_input)`
+4 -15
View File
@@ -32,7 +32,10 @@ pub const VALUE_INPUT: &str = "opacity_in";
/// Opacity effect node. Multiplies a texture's alpha by a 0..1 factor.
pub struct OpacityEffect {
/// Owned child math node configured to `multiply` (C++ `math_`,
/// formerly a QObject child).
/// formerly a QObject child). Held for its construction side effect;
/// the multiply actually runs through `NodeCore`, so the handle is
/// never read — kept (with an `allow`) rather than removed.
#[allow(dead_code)]
math: Box<super::math::MathNode>,
}
@@ -85,20 +88,6 @@ void main() {
}
"#;
impl OpacityEffect {
/// Fragment shader for the plain opacity path (C++
/// `get_shader_code()` default branch).
fn shader_frag() -> &'static str {
SHADER_FRAG
}
/// Fragment shader for the `rgbmult` request (C++
/// `get_shader_code()` `"rgbmult"` branch).
fn shader_rgb_mult_frag() -> &'static str {
SHADER_RGB_MULT_FRAG
}
}
impl NodeBehavior for OpacityEffect {
/// Human-readable name (C++ `name()`).
fn name(&self) -> &str {
+4 -1
View File
@@ -47,7 +47,10 @@ pub const CHANNEL_INPUT: &str = "premult_channel_in";
/// Channel selector values for [`CHANNEL_INPUT`], in combo order. The
/// numeric values are the shader's channel indices: `0` selects nothing
/// (the image passes through — a factor of 1) and `1..=4` select
/// `r`/`g`/`b`/`a`.
/// `r`/`g`/`b`/`a`. Only `Alpha` is constructed today (the runtime value
/// is the raw combo index); the full upstream set is kept (with an
/// `allow`) for parity.
#[allow(dead_code)]
#[repr(i64)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Channel {
@@ -73,6 +73,7 @@ impl ShapeNodeBase {
/// signature. The property write and the gizmo point placements are
/// therefore not representable here (`// CPP-PARITY:
/// shapenodebase.cpp` `update_gizmo_positions`).
#[allow(dead_code)] // C++ parity helpers; covered by the no-op tests.
pub fn update_gizmo_positions(
core: &mut crate::node::NodeCore,
row: &crate::value::NodeValueRow,
@@ -88,6 +89,7 @@ impl ShapeNodeBase {
/// command stack; neither is carried by this signature or this
/// crate's data model, so the writes are not representable here
/// (`// CPP-PARITY: shapenodebase.cpp` `set_rect`).
#[allow(dead_code)]
pub fn set_rect(core: &mut crate::node::NodeCore, rect: (f64, f64, f64, f64)) {
let _ = (core, rect);
}
@@ -106,6 +108,7 @@ impl ShapeNodeBase {
/// Rust `NodeCore::gizmos` has no dragger state, so the drag is not
/// representable here (`// CPP-PARITY: shapenodebase.cpp`
/// `gizmo_drag_move`).
#[allow(dead_code)]
pub fn gizmo_drag_move(core: &mut crate::node::NodeCore, x: f64, y: f64, modifiers: u32) {
let _ = (core, x, y, modifiers);
}
+3 -3
View File
@@ -182,8 +182,8 @@ mod tests {
fn backend_hooks_default_none() {
let _guard = TEST_BACKEND_LOCK.lock().unwrap();
set_text_backends(None, None);
assert_eq!(text_measure_backend(), None);
assert_eq!(text_render_backend(), None);
assert!(text_measure_backend().is_none());
assert!(text_render_backend().is_none());
}
#[test]
@@ -195,7 +195,7 @@ mod tests {
height: 34.0,
}
}
fn render(_r: &TextLayoutRequest, _t: &TextRenderTransform, mut target: TextRenderTarget) {
fn render(_r: &TextLayoutRequest, _t: &TextRenderTransform, target: TextRenderTarget) {
for b in target.data.iter_mut() {
*b = 255;
}
+3
View File
@@ -79,6 +79,7 @@ impl TextGeneratorV1 {
/// and the wrap width at 80% of the frame width (the "title safe"
/// area — `(width / 10) * 8`). The measure backend is not consulted
/// here.
#[allow(dead_code)] // C++ parity helper; the tests drive it.
pub fn layout_request(row: &NodeValueRow, frame_width: i32) -> TextLayoutRequest {
let text = row
.get(TEXT_INPUT)
@@ -115,6 +116,7 @@ impl TextGeneratorV1 {
/// valign combo (top: 10% top margin; center: frame center; bottom:
/// 10% bottom margin). The C++ math is integer (`width()/10`,
/// `height()/2 - doc_height/2`, ...), mirrored here.
#[allow(dead_code)]
pub fn draw_offsets(
valign: i32,
frame_width: i32,
@@ -144,6 +146,7 @@ impl TextGeneratorV1 {
/// The render step and the alpha transplant need the frame's pixel
/// buffer, which the Rust frame handle does not expose; they are not
/// representable here (`// CPP-PARITY: textv1.cpp` `generate_frame`).
#[allow(dead_code)]
pub fn measure_and_layout(
row: &NodeValueRow,
frame_width: i32,
+6
View File
@@ -73,6 +73,7 @@ fn to_text(v: &NodeValue) -> String {
}
/// `Variant::to_vec2()` for the inherited position/size inputs.
#[allow(dead_code)] // C++ parity accessor used by the tests.
fn to_vec2(v: &NodeValue) -> [f64; 2] {
match v {
NodeValue::Vec2(a) => *a,
@@ -86,6 +87,7 @@ impl TextGeneratorV2 {
/// replaced by `<br>` when `html_in` is set, 72 DPI
/// (`dots_per_meter = 2835`), and the wrap width at the shape
/// size X.
#[allow(dead_code)]
pub fn layout_request(row: &NodeValueRow) -> TextLayoutRequest {
let text = row
.get(TEXT_INPUT)
@@ -124,6 +126,7 @@ impl TextGeneratorV2 {
/// position re-centered into frame space —
/// `pos - size/2 + frame/2` (the frame halves are integer division in
/// C++).
#[allow(dead_code)]
pub fn base_offset(
pos: [f64; 2],
size: [f64; 2],
@@ -140,6 +143,7 @@ impl TextGeneratorV2 {
/// offset plus the vertical alignment delta — top: none; center:
/// `size.y/2 - doc_height/2` (the halving is integer on the
/// `int(doc.height)`); bottom: `size.y - doc_height`.
#[allow(dead_code)]
pub fn draw_offset(
valign: i32,
base: (f64, f64),
@@ -163,6 +167,7 @@ impl TextGeneratorV2 {
/// scale, the draw offset, and the clip rect at the base offset
/// covering the shape size (set before the vertical-alignment
/// translate in the C++).
#[allow(dead_code)]
pub fn render_transform(
scale: f64,
draw: (f64, f64),
@@ -190,6 +195,7 @@ impl TextGeneratorV2 {
/// The render step and the alpha transplant need the frame's pixel
/// buffer, which the Rust frame handle does not expose; they are not
/// representable here (`// CPP-PARITY: textv2.cpp` `generate_frame`).
#[allow(dead_code)]
pub fn measure_and_layout(
row: &NodeValueRow,
frame_width: i32,
+7
View File
@@ -334,6 +334,7 @@ impl TextGeneratorV3 {
/// Middle -> `TextGizmo::k_align_vcenter`, Bottom ->
/// `TextGizmo::k_align_bottom` (0 = top, 1 = bottom, 2 = vcenter in
/// the gizmo's numbering).
#[allow(dead_code)] // C++ parity helpers; covered by the tests.
pub fn get_gizmo_alignment_from_ours(v: VerticalAlignment) -> i32 {
match v {
VerticalAlignment::Top => 0,
@@ -413,6 +414,7 @@ impl TextGeneratorV3 {
/// Gizmo activated callback (C++ `gizmo_activated()`): sets
/// `use_args_in` to `false` and `dont_emit_valign_ = true`.
#[allow(dead_code)] // C++ parity callbacks; covered by the tests.
fn gizmo_activated(&mut self, core: &mut NodeCore) {
core.set_standard_value(USE_ARGS_INPUT, -1, NodeValue::Boolean(false));
self.dont_emit_valign = true;
@@ -420,6 +422,7 @@ impl TextGeneratorV3 {
/// Gizmo deactivated callback (C++ `gizmo_deactivated()`): sets
/// `use_args_in` to `true` and `dont_emit_valign_ = true`.
#[allow(dead_code)]
fn gizmo_deactivated(&mut self, core: &mut NodeCore) {
core.set_standard_value(USE_ARGS_INPUT, -1, NodeValue::Boolean(true));
self.dont_emit_valign = true;
@@ -431,6 +434,7 @@ impl TextGeneratorV3 {
/// command stack is not part of this crate, so only the resulting
/// standard-value write is performed (`// CPP-PARITY: textv3.cpp`
/// `set_vertical_alignment_undoable`).
#[allow(dead_code)]
fn set_vertical_alignment_undoable(&mut self, core: &mut NodeCore, a: i32) {
core.set_standard_value(
VERTICAL_ALIGNMENT_INPUT,
@@ -738,6 +742,7 @@ impl TextGeneratorV3 {
/// dots/meter) with the font taken from the markup. Both wrap to the
/// shape size X; the backend defaults are used when font family/size
/// are empty/zero.
#[allow(dead_code)]
pub fn layout_request(row: &NodeValueRow) -> TextLayoutRequest {
Self::layout_request_path(Self::plain_text_path(), row)
}
@@ -785,6 +790,7 @@ impl TextGeneratorV3 {
/// The C++ base offset (textv3.cpp `generate_frame()`): the shape
/// position re-centered into frame space — `pos - size/2 + frame/2`
/// (the frame halves are integer division in C++).
#[allow(dead_code)] // C++ parity helpers; the tests drive them.
pub fn base_offset(
pos: [f64; 2],
size: [f64; 2],
@@ -846,6 +852,7 @@ impl TextGeneratorV3 {
/// The render step needs the frame's pixel buffer, which the Rust
/// frame handle does not expose; it is not representable here
/// (`// CPP-PARITY: textv3.cpp` `generate_frame`).
#[allow(dead_code)]
pub fn measure_and_layout(row: &NodeValueRow) -> (TextLayoutRequest, TextLayoutSize) {
let req = Self::layout_request(row);
let doc = match super::textbackend::text_measure_backend() {
@@ -102,14 +102,6 @@ void main(void)
}
"#;
impl ThreeWayColorNode {
/// Fragment shader for any request (C++ `get_shader_code()` ignores
/// the request id and always returns this shader).
fn shader_frag() -> &'static str {
SHADER_FRAG
}
}
impl NodeBehavior for ThreeWayColorNode {
/// Human-readable name (C++ `name()`, inline in the header).
fn name(&self) -> &str {
@@ -50,6 +50,10 @@ pub const MIRROR_Y_INPUT: &str = "mirrory_in";
/// Anchor point for tiling (C++ private enum `Anchor`); values match
/// the `anchor_in` combo indices and the shader's `anchor_in` defines.
/// Only `MiddleCenter` is constructed today (the runtime value is the
/// raw combo index); the full upstream set is kept (with an `allow`)
/// for parity.
#[allow(dead_code)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum Anchor {
/// Top-left corner.
@@ -85,6 +85,9 @@ pub const GIZMO_SCALE_COUNT: usize = 8;
/// Auto-scale mode (C++ `AutoScaleType`); values match the
/// `autoscale_in` combo indices.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
// C++ parity value set (`AutoScaleType`); only `None` is constructed at
// runtime (the combo indices are still resolved through this enum).
#[allow(dead_code)]
pub enum AutoScaleType {
/// No auto-scaling.
None = 0,
@@ -99,6 +102,7 @@ pub enum AutoScaleType {
/// Rotation direction for wrap-around detection (C++ private enum
/// `RotationDirection`).
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[allow(dead_code)] // C++ parity value set; see `AutoScaleType`.
enum RotationDirection {
/// No direction established yet.
None,
@@ -110,6 +114,7 @@ enum RotationDirection {
/// Which axes a scale gizmo drags (C++ private enum `GizmoScaleType`).
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[allow(dead_code)] // C++ parity value set; see `AutoScaleType`.
enum GizmoScaleType {
/// Horizontal center handles (C++ `k_gizmo_scale_x_only`).
XOnly,
@@ -191,6 +196,8 @@ impl TransformDistortNode {
/// representable: `generate_auto_scaled_matrix` needs the texture
/// params from the C++ `VideoParams` (the Rust texture handle carries
/// no params) and `is_a_scale_gizmo` compares gizmo pointers.
#[allow(dead_code)] // C++ parity helpers; the pure ones are covered by
// the tests, the rest are not representable in the Rust model.
fn adjust_matrix_by_resolutions(
mat: [f64; 16],
sequence_res: (f64, f64),
@@ -264,6 +271,7 @@ impl TransformDistortNode {
/// C++ `Matrix4x4::map(PointF)` equivalent: maps `p` through the
/// row-major matrix treating it as `(x, y, 0, 1)`, dividing by the
/// resulting `w` whenever it is not exactly 1.
#[allow(dead_code)]
fn map_point(mat: [f64; 16], p: (f64, f64)) -> (f64, f64) {
let x = p.0 * mat[0] + p.1 * mat[1] + mat[3];
let y = p.0 * mat[4] + p.1 * mat[5] + mat[7];
@@ -278,6 +286,7 @@ impl TransformDistortNode {
/// Scale-point gizmo placement (C++ `create_scale_point()`): maps the
/// unit-square position through `mat` and adds the sequence half
/// resolution.
#[allow(dead_code)]
fn create_scale_point(x: f64, y: f64, half_res: (f64, f64), mat: [f64; 16]) -> (f64, f64) {
let p = Self::map_point(mat, (x, y));
(p.0 + half_res.0, p.1 + half_res.1)
@@ -286,6 +295,7 @@ impl TransformDistortNode {
/// Rotation direction of a mouse angle step (C++
/// `get_direction_from_angles()`): positive when `current` is greater
/// than `last`, negative otherwise.
#[allow(dead_code)]
fn get_direction_from_angles(last: f64, current: f64) -> RotationDirection {
if current > last {
RotationDirection::Positive
+1 -1
View File
@@ -190,7 +190,7 @@ pub fn create() -> (NodeCore, Box<dyn NodeBehavior>) {
)];
core.add_input(method);
let mut x = crate::input::Input::new(
let x = crate::input::Input::new(
X_INPUT,
crate::value::ValueType::Float,
crate::value::NodeValue::Float(0.0),
+4 -1
View File
@@ -48,7 +48,10 @@ pub const CHANNEL_INPUT: &str = "unpremult_channel_in";
/// Channel selector values for [`CHANNEL_INPUT`], in combo order. The
/// numeric values are the shader's channel indices: `0` selects nothing
/// (the image passes through — a divisor of 1) and `1..=4` select
/// `r`/`g`/`b`/`a`.
/// `r`/`g`/`b`/`a`. Only `Alpha` is constructed today (the runtime value
/// is the raw combo index); the full upstream set is kept (with an
/// `allow`) for parity.
#[allow(dead_code)]
#[repr(i64)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Channel {
@@ -73,12 +73,6 @@ void main(void)
"#;
impl WhiteBalanceNode {
/// Fragment shader for any request (C++ `get_shader_code()` ignores
/// the request id and always returns this shader).
fn shader_frag() -> &'static str {
SHADER_FRAG
}
/// RGB gains for a given illuminant temperature and tint (C++
/// `get_gain_for_temperature()`, extracted for testability). Kelvin
/// is clamped to [1000, 40000]; the Tanner Helland blackbody
+12 -11
View File
@@ -114,9 +114,9 @@ pub fn copy_inputs(
/// node ids (source order). Undo packaging happens at the caller via
/// oakundo's `UndoCommand`.
pub fn copy_subgraph(
graph: &mut Graph,
nodes: &[NodeId],
exclude_items: bool,
_graph: &mut Graph,
_nodes: &[NodeId],
_exclude_items: bool,
) -> crate::error::Result<Vec<NodeId>> {
todo!()
}
@@ -124,22 +124,22 @@ pub fn copy_subgraph(
/// Transform a time range from one node's frame of reference to
/// another's along the connection path (C++ `Node::transform_time_to`).
pub fn transform_time_to(
graph: &Graph,
time: TimeRange,
from: NodeId,
to: NodeId,
_graph: &Graph,
_time: TimeRange,
_from: NodeId,
_to: NodeId,
) -> crate::error::Result<TimeRange> {
todo!()
}
/// Undo-command display strings (C++
/// `get_connect_command_string`/`get_disconnect_command_string`).
pub fn connect_command_string(output: NodeId, input: NodeId, input_id: &str) -> String {
pub fn connect_command_string(_output: NodeId, _input: NodeId, _input_id: &str) -> String {
todo!()
}
/// See [`connect_command_string`].
pub fn disconnect_command_string(output: NodeId, input: NodeId, input_id: &str) -> String {
pub fn disconnect_command_string(_output: NodeId, _input: NodeId, _input_id: &str) -> String {
todo!()
}
@@ -181,8 +181,9 @@ pub fn set_value_at_time_command(
use crate::error::Error;
use crate::keyframe::{Interpolation, Keyframe};
// Determine the mutation from the current state.
let declared = graph
// Determine the mutation from the current state. The declared type is
// validated but not otherwise used yet.
let _declared = graph
.get(node)
.and_then(|e| e.core.input_data_type(input))
.ok_or(Error::NotFound)?;
+1 -5
View File
@@ -17,7 +17,7 @@
//! Project: owns the graph, the folder tree, settings, and the undo
//! stack binding. Mirrors C++ `olive::Project`.
use std::sync::{Arc, Mutex, Weak};
use std::sync::{Arc, Mutex};
use crate::graph::Graph;
use crate::id::NodeId;
@@ -451,7 +451,3 @@ pub enum ChangeRecord {
},
}
/// Weak-project handle used by the identity registry (node_from_identity
/// upgrades it; a freed project leaves a dead weak entry that upgrades
/// to `None`).
pub(crate) type WeakProject = Weak<Mutex<Project>>;
+3 -3
View File
@@ -24,11 +24,11 @@ use crate::input::Input;
use crate::node::{Category, NodeBehavior, NodeCore};
use crate::value::{AudioParams, NodeValue, ValueType, VideoParams};
/// Sequence texture/samples input ids (ViewerOutput::k_texture_input /
/// k_samples_input) and the track input id format (Sequence::
/// k_track_input_format).
/// Sequence texture input id (C++ `ViewerOutput::k_texture_input`).
pub const TEXTURE_INPUT: &str = "tex_in";
/// Sequence audio samples input id (C++ `ViewerOutput::k_samples_input`).
pub const SAMPLES_INPUT: &str = "samples_in";
/// Sequence per-track input id format (C++ `Sequence::k_track_input_format`).
pub const TRACK_INPUT_FORMAT: &str = "track_in_%1";
/// Sequence behavior (viewer node).
+2 -3
View File
@@ -39,7 +39,7 @@ use crate::graph::Graph;
use crate::id::NodeId;
use crate::keyframe::{Interpolation, Keyframe};
use crate::node::NodeCore;
use crate::project::{NodeRef, Project};
use crate::project::Project;
use crate::value::{NodeValue, ValueType};
/// Minimal XML reader surface the serializer needs (implemented over
@@ -565,7 +565,6 @@ fn load_project_body(
reader: &mut dyn XmlRead,
project: &mut Project,
) -> crate::error::Result<std::collections::HashMap<u64, NodeId>> {
use crate::error::Error;
// Identity -> NodeId map for connection resolution.
let mut id_map: std::collections::HashMap<u64, NodeId> = std::collections::HashMap::new();
// Deferred connections: (output_identity, input_node_id, input_id, element).
@@ -693,7 +692,7 @@ fn load_node(
// types fall back to an error. `create_any` also covers the dynamic
// (runtime-registered OpenFX plugin) entries — `find` alone would
// reject every project that carries a plugin node.
let (mut core, behavior): (NodeCore, Box<dyn crate::node::NodeBehavior>) =
let (core, behavior): (NodeCore, Box<dyn crate::node::NodeBehavior>) =
match create_timeline_type(&type_id) {
Some(x) => x,
None => match crate::factory::Factory::global().create_any(&type_id) {
+3 -11
View File
@@ -203,11 +203,6 @@ impl SampleBuffer {
return None;
}
let bps = self.format.bytes_per_sample();
let stride = if self.format.is_planar() {
self.sample_count
} else {
self.channels
};
let pos = if self.format.is_planar() {
channel * self.sample_count + index
} else {
@@ -695,7 +690,7 @@ impl Clone for NodeValue {
fn clone(&self) -> Self {
match self {
NodeValue::Texture(h) => {
let mut h2 = h.clone();
let h2 = h.clone();
if let Some(f) = h2.addref {
// Safety: `h2` is a valid handle; addref only touches
// the refcount.
@@ -771,7 +766,7 @@ impl OakNodeValue {
/// Map an oaknode_value POD into a [`NodeValue`] of the input's
/// declared type (C++ `variant_from_value`). `OAKNODE_VALUE_STRING`
/// and unknown kinds are rejected with [`Error::Invalid`].
pub fn to_node_value(self, declared: ValueType) -> crate::error::Result<NodeValue> {
pub fn to_node_value(self, _declared: ValueType) -> crate::error::Result<NodeValue> {
use crate::error::Error;
match self.kind {
oak::INT | oak::COMBO => Ok(NodeValue::Int(self.num)),
@@ -858,10 +853,7 @@ impl OakNodeValue {
out.f = a;
Ok(out)
}
_ => {
out.kind = oak::NONE;
Err(Error::Failed("type has no POD representation".to_string()))
}
_ => Err(Error::Failed("type has no POD representation".to_string())),
}
}
}
+1 -1
View File
@@ -693,7 +693,7 @@ fn node_behavior_defaults() {
// value / process_samples / generate_frame no-ops.
let mut table = NodeValueTable::default();
let mut row = std::collections::BTreeMap::new();
let row = std::collections::BTreeMap::new();
b.value(&core, &row, Rational::new(0, 1), &mut table);
assert!(table.is_empty());
let mut samples = SampleBuffer::default();
+2 -2
View File
@@ -50,7 +50,7 @@ impl NodeBehavior for TestNode {
fn build(n: usize) -> (Graph, Vec<NodeId>) {
let mut g = Graph::new();
let mut ids = Vec::new();
for i in 0..n {
for _ in 0..n {
let mut core = NodeCore::new();
core.add_input(Input::new(
"val_in",
@@ -159,7 +159,7 @@ fn cycle_rejection() {
/// yields empty order; diamond graph has a valid (stable) order.
#[test]
fn topological_order() {
let mut g = Graph::new();
let g = Graph::new();
assert!(g.topological_order().is_empty());
let (mut g, ids) = build(4);
+2 -2
View File
@@ -79,7 +79,7 @@ fn block_behaviors_and_inputs() {
assert!(clip.footage.is_none());
// Gap.
let (core, behavior) = oak_node::block::gap_create();
let (_core, behavior) = oak_node::block::gap_create();
assert_eq!(behavior.name(), "Gap");
assert_eq!(behavior.type_id(), "org.olivevideoeditor.Olive.gapblock");
let gap = GapBlockBehavior::new();
@@ -287,7 +287,7 @@ fn color_manager_state() {
cm.set_up_default_config().unwrap();
assert!(cm.is_loaded());
let mut empty = ColorManager::new();
let empty = ColorManager::new();
assert!(empty.list_colorspaces().is_empty());
}
+1 -1
View File
@@ -526,7 +526,7 @@ fn assert_full_roundtrip_fields(orig: &oak_node::project::Project, loaded: &oak_
use oak_node::folder::FolderBehavior;
use oak_node::footage::FootageBehavior;
use oak_node::sequence::SequenceBehavior;
use oak_node::track::{TrackBehavior, TrackListBehavior, TrackType};
use oak_node::track::{TrackBehavior, TrackType};
// Project shell: uuid + settings.
assert_eq!(loaded.uuid, orig.uuid, "uuid");
+1 -1
View File
@@ -258,7 +258,7 @@ impl NodeBehavior for Echo {
/// track read at its midpoint feeds 5.
#[test]
fn unconnected_input_evaluates_keyframes_at_time() {
use oak_node::keyframe::{Keyframe, KeyframeTrack};
use oak_node::keyframe::Keyframe;
let mut g = Graph::new();
let mut core = NodeCore::new();
core.add_input(Input::new("val_in", ValueType::Float, NodeValue::Float(0.0)));
-4
View File
@@ -276,8 +276,6 @@ fn global_ofx_host() -> *mut OfxHost {
/// kOfxActionLoad。
pub(crate) const ACTION_LOAD: &str = "OfxActionLoad";
/// kOfxActionUnload。
pub(crate) const ACTION_UNLOAD: &str = "OfxActionUnload";
/// kOfxActionDescribe。
pub(crate) const ACTION_DESCRIBE: &str = "OfxActionDescribe";
/// kOfxActionCreateInstance。
@@ -552,8 +550,6 @@ pub(crate) const PROP_CLIP_COLOURSPACE: &str = "OfxImageClipPropColourspace";
/// kOfxImageClipPropPreferredColourspaces(ofxColour.h:201):clip 偏好
/// 色彩空间(GetClipPreferences 的 out args 可写)。
pub(crate) const PROP_CLIP_PREFERRED_COLOURSPACES: &str = "OfxImageClipPropPreferredColourspaces";
/// 工作空间(任务要求:全链路 ACEScg)。
pub(crate) const WORKING_COLOURSPACE: &str = "ACEScg";
/// 宿主支持的 native 色彩配置标识(ofxColour.h:78 的唯一现行值)。
pub(crate) const NATIVE_CONFIG_ID: &str = "ofx-native-v1.5_aces-v1.3_ocio-v2.3";
/// 宿主 OCIO 配置 URI(ofxColour.h:110 允许 ocio:// 内建配置;本宿主
+1 -1
View File
@@ -1304,7 +1304,7 @@ mod tests {
) -> i32 {
0
}
let mut def = ParamDef::new("curve", ofx::TYPE_PARAMETRIC);
let def = ParamDef::new("curve", ofx::TYPE_PARAMETRIC);
def.props
.set_one(ofx::PROP_LABEL, PropValue::String(CString::new("Curve").unwrap()));
def.props
+1 -1
View File
@@ -362,7 +362,7 @@ impl ParamDef {
/// 按 OFX 类型构建定义(镜像 HS ofxhParam.cpp:224-352:
/// universalProps + addStandardParamProps 的分类型属性表)。
pub(crate) fn new(name: &str, ofx_type: &str) -> Self {
let mut props = PropertySet::new();
let props = PropertySet::new();
let uname = name.to_string();
let utype = ofx_type.to_string();
+1 -1
View File
@@ -505,7 +505,7 @@ mod tests {
/// 重复定义整体替换(HS map 覆盖)。
#[test]
fn describe_clip_define_and_props() {
let mut desc = EffectDescriptor::new();
let desc = EffectDescriptor::new();
let s = suite_v1();
let h = descriptor_handle(&desc);
-4
View File
@@ -490,10 +490,6 @@ mod tests {
use std::ffi::{c_char, CStr};
use std::sync::Mutex;
fn cs(s: &str) -> CString {
CString::new(s).unwrap()
}
/// interact 属性表:PixelScale/ViewportSize/SuggestedColour/
/// SlaveToParam/BitDepth/HasAlpha 等按头文件预置,经 property suite
/// 可读写。
-11
View File
@@ -81,17 +81,6 @@ pub(crate) fn free(ptr: *mut u8) -> bool {
}
}
/// 兜底回收全部在账块(destroyInstance 的泄漏防线)。返回回收块数
/// (泄漏断言用)。
pub(crate) fn sweep_leaked() -> usize {
let mut ledger = lock();
let n = ledger.len();
for b in ledger.drain(..) {
unsafe { std::alloc::dealloc(b.ptr as *mut u8, b.layout) };
}
n
}
/// 函数表布局(与 SDK `OfxMemorySuiteV1` 一致;`size_t` 在本平台
/// 与 `usize` 同宽,stable Rust 用 `usize` 表达——骨架的 `c_size_t`
/// 是不稳定特性,弃用)。
-3
View File
@@ -212,9 +212,6 @@ pub(crate) fn gl_ctx() -> Option<GlCtx> {
GL_CTX.with(|c| c.borrow().clone())
}
/// 宿主进程身份(fetchSuite 的 version 检查用;= OFX API 1.5)。
pub(crate) const OFX_API_VERSION: i32 = 105;
/// fetchSuite 宿主入口:按名字与版本返回函数表指针;不认识或版本
/// 不符返回 `None`(FFI 层转 NULL)。
///
@@ -260,7 +260,6 @@ pub fn suite_v1() -> &'static MultiThreadSuiteV1 {
mod tests {
use super::*;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Mutex;
/// 每个插件线程把 (index, count) 累计进 userdata。
unsafe extern "C" fn worker(index: c_uint, count: c_uint, arg: *mut c_void) {
+2 -2
View File
@@ -787,7 +787,7 @@ mod tests {
/// 用 paramDefine 建一个实例(describe 产物 → createInstance)。
fn make_instance() -> (Arc<Instance>, *mut c_void) {
let mut desc = EffectDescriptor::new();
let desc = EffectDescriptor::new();
let s = suite_v1();
let dhandle = descriptor_handle(&desc);
unsafe {
@@ -836,7 +836,7 @@ mod tests {
#[test]
fn describe_define_and_get() {
let mut desc = EffectDescriptor::new();
let desc = EffectDescriptor::new();
let s = suite_v1();
let handle = descriptor_handle(&desc);
let t = cs("OfxParamTypeDouble");
+4 -4
View File
@@ -42,10 +42,8 @@
//! BadHandle/BadIndex)。
use std::borrow::Cow;
use std::ffi::{c_char, c_double, c_int, c_void};
use std::ffi::{c_double, c_int, c_void};
use crate::descriptor::EffectDescriptor;
use crate::instance::Instance;
use crate::param::{ParamDef, ParamInstance, ParamValue};
use crate::param_curve::Curve;
use crate::property::{PropertySet, Value};
@@ -414,10 +412,12 @@ pub fn suite_v1() -> &'static ParametricParameterSuiteV1 {
#[cfg(test)]
mod tests {
use super::*;
use std::ffi::CString;
use std::ffi::{c_char, CString};
use std::sync::Arc;
use crate::descriptor::EffectDescriptor;
use crate::host::Plugin;
use crate::instance::Instance;
use crate::param::{ParamInstance, ParamSetInstance};
use crate::property::PropertySet;
+1 -1
View File
@@ -498,7 +498,7 @@ unsafe extern "C" fn prop_get_string(
let r = get_string(set, name, index);
if std::env::var_os("OAK_OFX_TRACE").is_some() {
let rendered = match &r {
Ok(p) => format!("0 <- {:?}", unsafe { std::ffi::CStr::from_ptr(*p) }),
Ok(p) => format!("0 <- {:?}", std::ffi::CStr::from_ptr(*p)),
Err(c) => format!("{c}"),
};
eprintln!("[ofx] propGetString(h={handle:p}, {name}[{index}]) -> {rendered}");
+4
View File
@@ -23,6 +23,10 @@
//! 单库化后像素路径经 oakrender 值模型(`oak_core::texture::Texture`)
//! 驱动;渲染 goldens 待该迁移落地。
// 每个集成测试二进制都会独立编译本模块,只用到自己需要的那部分辅助件,
// 因此某个二进制里未用到的条目仍属于共享夹具 API。
#![allow(dead_code)]
use std::path::PathBuf;
/// 构建系统注入插件路径的环境变量名。
+1 -5
View File
@@ -36,7 +36,7 @@
mod common;
use std::ffi::{c_char, c_int, c_void, CStr, CString};
use std::ffi::{c_char, c_int, c_void, CStr};
use std::sync::Arc;
use oak_core::{PixelFormat, Rational};
@@ -91,10 +91,6 @@ unsafe extern "C" fn capture_msg(
1
}
fn cs(s: &str) -> CString {
CString::new(s).unwrap()
}
fn first_pixel(texture: &Texture) -> [f32; 4] {
let Texture::Cpu(frame) = texture else {
panic!("期望 CPU 帧");
+1 -1
View File
@@ -191,7 +191,7 @@ fn concurrent_access() {
}
// 数据自洽:32 个属性全部存活、维度未被撑大。
for (i, name) in names.iter().enumerate() {
for name in names.iter() {
assert_eq!(s.dimension(name), 1);
assert!(s.get(name, 0).is_some());
}
+4 -6
View File
@@ -24,8 +24,7 @@
mod common;
use std::ffi::{c_char, c_double, c_int, c_uint, c_void, CStr, CString};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::ffi::{c_char, c_int, c_uint, c_void, CStr, CString};
use oak_plugin::descriptor::EffectDescriptor;
use oak_plugin::instance::Instance;
@@ -83,7 +82,7 @@ fn dummy_plugin(descriptor: EffectDescriptor) -> std::sync::Arc<oak_plugin::host
/// 用 param suite 的 paramDefine 造一个含 6 参数的实例。
fn make_instance() -> (std::sync::Arc<Instance>, *mut c_void) {
let mut desc = EffectDescriptor::new();
let desc = EffectDescriptor::new();
let s = param_suite();
let dhandle = tag::make(&desc.props as *const PropertySet, tag::DESCRIPTOR);
unsafe {
@@ -253,7 +252,7 @@ fn memory_suite_ledger() {
/// [`oak_plugin::clip::ClipInstance::fetch_image`] 与存活表记账验证。
#[test]
fn image_effect_clip_image_pairing() {
let mut desc = EffectDescriptor::new();
let desc = EffectDescriptor::new();
let s = image_effect_suite();
let h = tag::make(&desc.props as *const PropertySet, tag::DESCRIPTOR);
@@ -384,7 +383,7 @@ fn image_effect_clip_image_pairing() {
#[test]
fn param_suite_roundtrip_and_change_action() {
// describe 期。
let mut desc = EffectDescriptor::new();
let desc = EffectDescriptor::new();
let s = param_suite();
let dhandle = tag::make(&desc.props as *const PropertySet, tag::DESCRIPTOR);
let t = cs("OfxParamTypeDouble");
@@ -478,7 +477,6 @@ fn param_suite_roundtrip_and_change_action() {
assert_eq!(CStr::from_ptr(p).to_bytes(), b"hello");
// AtTime == 当前值(无动画)。
let mut v2 = 0.0;
assert_eq!((s.param_get_value_at_time)(gain, 12.0, &mut iv), OK);
assert_eq!(iv, 7);
assert_eq!((s.param_set_value_at_time)(gain, 12.0, 9), OK);
+5 -5
View File
@@ -347,7 +347,7 @@ mod tests {
// deterministic on the queued inline dispatcher: the first job stays
// queued until `run`, so the superseding submit's cancel lands first.
let (mut c, d) = new_cacher_slow();
c.attach(7);
c.attach(7).unwrap();
let first = c.single_frame(Rational::new(0, 1));
let second = c.single_frame(Rational::new(1, 1));
assert_ne!(first, second);
@@ -381,7 +381,7 @@ mod tests {
#[test]
fn ignore_requests_suppresses_jobs() {
let (mut c, d) = new_cacher();
c.attach(7);
c.attach(7).unwrap();
c.ignore_requests = true;
c.on_cache_request(7, TimeRange::new(Rational::new(0, 1), Rational::new(5, 1)));
assert!(c.live_jobs().is_empty());
@@ -391,7 +391,7 @@ mod tests {
#[test]
fn renders_paused_queues_but_does_not_start() {
let (mut c, d) = new_cacher();
c.attach(7);
c.attach(7).unwrap();
c.renders_paused = true;
c.on_cache_request(7, TimeRange::new(Rational::new(0, 1), Rational::new(5, 1)));
assert!(c.live_jobs().is_empty(), "paused: no jobs started");
@@ -402,7 +402,7 @@ mod tests {
#[test]
fn cancel_video_tasks_wait_blocks_until_idle() {
let (mut c, d) = new_cacher();
c.attach(7);
c.attach(7).unwrap();
c.force_range(TimeRange::new(Rational::new(0, 1), Rational::new(1, 1)));
assert!(c.is_rendering_custom_range() || c.live_jobs().len() == 1);
// Drain so the wait below observes every job finished.
@@ -452,7 +452,7 @@ mod tests {
#[test]
fn clear_finished_single_frames_removes_done() {
let (mut c, d) = new_cacher();
c.attach(7);
c.attach(7).unwrap();
let id = c.single_frame(Rational::new(0, 1));
d.run();
c.arena.wait(id).unwrap();
+9 -4
View File
@@ -40,11 +40,9 @@ use std::sync::{Arc, Mutex};
use crate::error::{Error, Result};
use crate::shaderfx::{compile_effect, run_effect};
use oak_codec::decoder::{
CodecStream, Decoder as _, RenderMode, RetrieveAudioStatus, RetrieveVideoParams,
K_COLOR_RANGE_DEFAULT,
CodecStream, RenderMode, RetrieveAudioStatus, RetrieveVideoParams, K_COLOR_RANGE_DEFAULT,
};
use oak_codec::ffmpeg::FFmpegDecoder;
use oak_core::color::ColorProcessor;
use oak_core::frame::VideoParamsPod;
use oak_core::texture::{Frame, Texture};
use oak_core::{PixelFormat, Rational, TimeRange};
@@ -287,6 +285,7 @@ fn purple_frame(time: Rational, size: (i32, i32)) -> Texture {
}
impl RenderEvalHooks {
/// A hook set with every optional hook unset (`use_cache` off).
pub fn new() -> Self {
Self {
use_cache: false,
@@ -346,6 +345,10 @@ impl RenderEvalHooks {
/// C++ process_frame_generation: fill the destination with a generated
/// F32 frame (transparent black for now).
///
/// Only the `generation_fills_cpu_texture` unit test drives this today;
/// the live eval path uses the shader/graph hooks instead.
#[allow(dead_code)]
fn process_frame_generation(
&mut self,
destination: &mut Texture,
@@ -3407,7 +3410,9 @@ mod tests {
}
}
let payload = ColorTransformJobPayload {
color_processor: std::sync::Arc::new(ColorProcessor::pass_through()),
color_processor: std::sync::Arc::new(
oak_core::color::ColorProcessor::pass_through(),
),
input: NodeValue::Texture(oak_node::handle::make_owned(Texture::wrap_frame(frame))),
time: Rational::new(0, 1),
};
+7 -7
View File
@@ -81,7 +81,7 @@ use crate::ipc::{
FrameSlotMeta, FrameSlotPool, HandshakeMsg, HelloCapsMsg, PluginProgressMsg, RenderAudioBatchMsg,
RenderBatchMsg, SharedMemoryRegion, ShmMode, WireMontageClip, SLOT_FORMAT_BGRA8,
TYPE_BATCH_ACCEPTED, TYPE_ERROR, TYPE_FRAME_FAILED, TYPE_FRAME_READY, TYPE_HANDSHAKE,
TYPE_HELLO_CAPS, TYPE_PLUGIN_CANCEL, TYPE_PLUGIN_PROGRESS, TYPE_RENDER_AUDIO_BATCH,
TYPE_HELLO_CAPS, TYPE_PLUGIN_PROGRESS, TYPE_RENDER_AUDIO_BATCH,
plugin_cancel_json,
};
use crate::scheduler::{FrameKey, FrameRequest, PreviewScheduler, SubmitOutcome};
@@ -901,7 +901,6 @@ enum WorkerEvent {
}
struct WorkerHandle {
index: usize,
/// Spawn generation (increments on every restart): reader-thread
/// events carry the generation of the child they read from, so a
/// late EOF from a dead child cannot kill its replacement.
@@ -941,14 +940,12 @@ struct WorkerHandle {
impl WorkerHandle {
fn shell(
index: usize,
generation: u64,
shm: Arc<ShmRegionView>,
slots: u32,
slot_bytes: usize,
) -> WorkerHandle {
WorkerHandle {
index,
generation,
state: WorkerState::Starting,
child: None,
@@ -1528,7 +1525,7 @@ impl ProcessDispatcher {
handle.retire_sent_at = Some(Instant::now());
}
let mut reaped_flags: Vec<bool> = Vec::with_capacity(inner.workers.len());
for (i, handle) in inner.workers.iter_mut().enumerate() {
for handle in inner.workers.iter_mut() {
if !handle.retiring {
reaped_flags.push(false);
continue;
@@ -2007,7 +2004,8 @@ impl ProcessDispatcher {
})
.map_err(|e| Error::Failed(format!("spawn reader thread: {e}")))?;
let mut handle = WorkerHandle::shell(index, generation, shm, inner.slots, inner.slot_bytes);
let mut handle =
WorkerHandle::shell(generation, shm, inner.slots, inner.slot_bytes);
handle.child = Some(child);
handle.stdin = stdin;
handle.spawned_at = Instant::now();
@@ -2749,7 +2747,9 @@ mod tests {
let mut inner = dispatcher.inner.lock().unwrap_or_else(|e| e.into_inner());
let key = SharedMemoryRegion::make_key(std::process::id() as i64, 999);
let shm = ShmRegionView::create(&key, 2, 256).expect("shm");
inner.workers.push(WorkerHandle::shell(0, 0, shm, 2, 256));
inner
.workers
.push(WorkerHandle::shell(0, shm, 2, 256));
}
let mut fired = Vec::new();
-2
View File
@@ -111,7 +111,6 @@ pub struct ClaimedBatch<P> {
struct Claim<P> {
worker: usize,
batch_id: u64,
request: FrameRequest<P>,
}
@@ -314,7 +313,6 @@ impl<P: Clone> PreviewScheduler<P> {
entry.request.key,
Claim {
worker,
batch_id,
request: entry.request.clone(),
},
);
+1 -1
View File
@@ -196,7 +196,7 @@ pub fn render(req: &TextLayoutRequest, transform: &TextRenderTransform, target:
}
let mut sys = lock_system();
let mut buffer = layout(&mut sys.font_system, req, &text);
let buffer = layout(&mut sys.font_system, req, &text);
let TextSystem {
font_system,
swash_cache,
+6 -1
View File
@@ -16,6 +16,11 @@
//! Shared helpers for the integration tests.
// Every integration-test binary in this crate compiles this module on its
// own and uses only the helpers it needs, so items unused in one binary are
// still part of the shared fixture API.
#![allow(dead_code)]
use std::sync::{Mutex, MutexGuard};
/// Serializes tests that initialize the process-wide RenderManager
@@ -60,7 +65,7 @@ impl Drop for ManagerGuard {
// symbols.
use std::collections::HashMap;
use std::ffi::{c_char, c_int, c_void};
use std::ffi::{c_int, c_void};
use std::sync::OnceLock;
// The bundled OpenColorIO's macos system monitor references
+2 -3
View File
@@ -22,7 +22,6 @@
//! tests assert those failures.
use std::sync::Arc;
use std::time::Duration;
use oak_core::{Rational, TimeRange};
@@ -89,7 +88,7 @@ fn sync_without_established_copy_fails() {
/// detach (lifetime discipline).
#[test]
fn autocacher_attach_detach() {
let (mut c, mut pool) = cacher();
let (mut c, pool) = cacher();
c.attach(42).unwrap();
assert_eq!(c.copied_project, 42);
c.on_cache_request(
@@ -111,7 +110,7 @@ fn autocacher_attach_detach() {
#[test]
fn cancel_video_tasks_semantics() {
let (mut c, d) = cacher();
c.attach(1);
c.attach(1).unwrap();
c.force_range(TimeRange::new(Rational::new(0, 1), Rational::new(5, 1)));
assert_eq!(c.live_jobs().len(), 1);
+1 -1
View File
@@ -76,7 +76,7 @@ fn blit_applies_ocio_in_float() {
return;
}
let mut renderer = DisplayRenderer::new(BackendKind::Cpu);
let renderer = DisplayRenderer::new(BackendKind::Cpu);
let mut pod = VideoParamsPod::default();
pod.width = 4;
pod.height = 4;
@@ -24,12 +24,12 @@ mod common;
use std::sync::{mpsc, Arc};
use std::time::Duration;
use oak_core::{Rational, TimeRange};
use oak_core::Rational;
use oak_render::error::Error;
use oak_core::frame::VideoParamsPod;
use oak_core::texture::{Frame, Texture};
use oak_render::ticket::{TicketArena, TicketId, VideoTicketParams};
use oak_render::ticket::{TicketArena, VideoTicketParams};
use oak_render::worker::{GraphSnapshotStore, InlineDispatcher, JobDispatch};
/// Unwrap a video ticket payload for assertions.
+3 -3
View File
@@ -1051,9 +1051,9 @@ impl RenderTask {
unsafe {
drop(Box::from_raw(dispatch));
}
// The pump closure borrows `private_dispatch`; drop it before the
// take below.
drop(pump);
// `pump`'s shared borrow of `private_dispatch` ends at its last use
// above (NLL); the closure needs no explicit drop, which would be a
// no-op because a closure capture is `Copy` here.
if let Some(d) = private_dispatch.take() {
d.shutdown();
}
+2 -2
View File
@@ -40,8 +40,8 @@ use crate::util::{
block_set_length_and_media_in, block_set_length_and_media_out, block_set_range, block_track,
clip_media_in, clip_set_media_in, same_block, track_append_block, track_create,
track_insert_block_after, track_insert_block_before, track_replace_block,
track_ripple_remove_block, tracklist_append, tracklist_remove, tracklist_remove_last,
tracklist_track_at, tracklist_track_count, tracklist_type, transition_offsets,
track_ripple_remove_block, tracklist_append, tracklist_remove, tracklist_track_at,
tracklist_track_count, tracklist_type, transition_offsets,
transition_set_offsets, BlockKind, NodeRef,
};
-1
View File
@@ -24,7 +24,6 @@
use std::sync::{Arc, Mutex, MutexGuard};
use oak_core::{Rational, TimeRange};
use oak_timeline::common::EditToInfo;
use oak_timeline::handle::{get, make_owned, CHandle};
use oak_timeline::marker::{
MarkerAddCommand, MarkerChangeColorCommand, MarkerChangeNameCommand, MarkerChangeTimeCommand,
+6 -2
View File
@@ -106,7 +106,11 @@ fn command_drop_frees_exactly_once() {
name: "a",
trace: trace.clone(),
};
let cmd = UndoCommand::from_closures(move || {}, move || drop(&probe));
let cmd = UndoCommand::from_closures(move || {}, move || {
// `probe` must be captured by value so that dropping the command
// frees it; dropping a reference (the previous body) did nothing.
let _ = &probe;
});
drop(cmd);
assert_eq!(events(&trace), vec!["free:a"]);
@@ -320,7 +324,7 @@ fn stack_caps_at_k_max() {
#[test]
fn stack_query_bounds_errors() {
let mut s = UndoStack::new();
let s = UndoStack::new();
assert!(matches!(s.command_name(-1), Err(Error::NotFound)));
assert!(matches!(s.command_name(1), Err(Error::NotFound)));
assert!(matches!(s.command_is_done(-1), Err(Error::NotFound)));
+1 -1
View File
@@ -1537,7 +1537,7 @@ mod tests {
let _pool =
unsafe { FrameSlotPool::create(out_region.data(), slots as u32, slot_bytes as usize) };
let (in_key, in_bytes, in_region) = if input {
let (in_key, _in_bytes, in_region) = if input {
let in_key = test_key("in");
let in_bytes = FrameSlotPool::bytes_needed(slots as u32, slot_bytes as usize);
let mut in_region = SharedMemoryRegion::new();