diff --git a/src/input_capture.rs b/src/input_capture.rs index 7bdb886..4b5886e 100644 --- a/src/input_capture.rs +++ b/src/input_capture.rs @@ -33,21 +33,58 @@ impl Plugin for InputCapturePlugin { .add_systems(First, frame_counter); } - app.init_resource::() - .init_resource::() - .init_resource::() + app.add_event::() + .add_event::() + .add_systems(First, initiate_input_capture) .add_systems( Last, ( // Capture any mocked input as well capture_input, + serialize_captured_input_on_final_capture_frame + .run_if(resource_exists::), + serialize_captured_input_on_end_capture_event, serialize_captured_input_on_exit, ) + .run_if(resource_exists::) .chain(), ); } } +/// An Event that users can send to initiate input capture. +/// +/// Data is serialized to the provided `filepath` when either an [`EndInputCapture`] or an [`AppExit`] event is detected. +#[derive(Debug, Default, Event)] +pub struct BeginInputCapture { + /// The input mechanisms that will be captured, see [`InputModesCaptured`]. + pub input_modes_captured: InputModesCaptured, + /// The filepath at which to serialize captured input data. + pub filepath: Option, + /// The number of frames for which inputs should be captured. + /// If None, inputs will be captured until an [`EndInputCapture`] or [`AppExit`] event is detected. + pub frames_to_capture: Option, + /// A `Window` entity which acts as a filter for which inputs will be captured. + /// This data will not be serialized, so that a target window can be selected on playback. + pub window_to_capture: Option, +} + +/// An Event that users can send to end input capture and serialize data to disk. +#[derive(Debug, Event)] +pub struct EndInputCapture; + +/// The final [`FrameCount`] at which inputs will stop being captured. +/// +/// If this Resource is attached, [`TimestampedInputs`] will be serialized and input capture will stop once `FrameCount` reaches this value. +#[derive(Debug, Resource)] +pub struct FinalCaptureFrame(FrameCount); + +/// The `Window` entity for which inputs will be captured. +/// +/// If this Resource is attached, only input events on the window corresponding to this entity will be captured. +#[derive(Debug, Resource)] +pub struct InputCaptureWindow(Entity); + /// The input mechanisms captured via the [`InputCapturePlugin`], configured as a resource. /// /// By default, all supported input modes will be captured. @@ -91,6 +128,30 @@ impl Default for InputModesCaptured { } } +/// Initiates input capture when a [`BeginInputCapture`] is detected. +pub fn initiate_input_capture( + mut commands: Commands, + mut begin_capture_events: EventReader, + frame_count: Res, +) { + if let Some(event) = begin_capture_events.read().next() { + commands.init_resource::(); + commands.insert_resource(event.input_modes_captured.clone()); + if let Some(path) = &event.filepath { + commands.insert_resource(PlaybackFilePath::new(path)); + } else { + commands.init_resource::(); + } + if let Some(final_frame) = event.frames_to_capture { + commands.insert_resource(FinalCaptureFrame(*frame_count + final_frame)); + } + if let Some(window_entity) = &event.window_to_capture { + commands.insert_resource(InputCaptureWindow(*window_entity)); + } + } + begin_capture_events.clear(); +} + /// Captures input from the [`bevy::window`] and [`bevy::input`] event streams. /// /// The input modes can be controlled via the [`InputModesCaptured`] resource. @@ -103,6 +164,7 @@ pub fn capture_input( mut gamepad_events: EventReader, mut app_exit_events: EventReader, mut timestamped_input: ResMut, + window_to_capture: Option>, input_modes_captured: Res, frame_count: Res, time: Res