use std::ffi::CStr;
use std::fmt::{Debug, Formatter};
use std::pin::Pin;
use std::ptr::NonNull;
use derive_builder::Builder;
use pw_sys::{pw_buffer, pw_stream_control, pw_stream_events, pw_stream_state};
use spa_sys::{spa_command, spa_pod};
use pipewire_wrapper_proc_macro::{RawWrapper, Wrapper};
use crate::events_builder_build;
use crate::spa::interface::Hook;
use crate::spa::io::IOValue;
use crate::spa::pod::object::param_io::IOType;
use crate::spa::pod::PodRef;
use crate::spa::type_::CommandRef;
use crate::stream::buffer::BufferRef;
use crate::stream::control::ControlRef;
use crate::stream::State;
use crate::wrapper::RawWrapper;
#[derive(RawWrapper, Debug)]
#[repr(transparent)]
pub struct StreamEventsRef {
#[raw]
raw: pw_sys::pw_stream_events,
}
pub type DestroyCallback<'p> = Box<dyn FnMut() + 'p>;
pub type StateChangedCallback<'p> = Box<dyn for<'a> FnMut(State, State, Option<&'a CStr>) + 'p>;
pub type ControlInfoCallback<'p> = Box<dyn for<'a> FnMut(u32, &'a ControlRef) + 'p>;
pub type IOChangedCallback<'p> = Box<dyn for<'a> FnMut(IOValue) + 'p>;
pub type ParamChangedCallback<'p> = Box<dyn for<'a> FnMut(u32, &'a PodRef) + 'p>;
pub type AddBufferCallback<'p> = Box<dyn for<'a> FnMut(&'a BufferRef) + 'p>;
pub type RemoveBufferCallback<'p> = Box<dyn for<'a> FnMut(&'a BufferRef) + 'p>;
pub type ProcessCallback<'p> = Box<dyn FnMut() + 'p>;
pub type DrainedCallback<'p> = Box<dyn FnMut() + 'p>;
pub type CommandCallback<'p> = Box<dyn for<'a> FnMut(&'a CommandRef) + 'p>;
pub type TriggerDoneCallback<'p> = Box<dyn FnMut() + 'p>;
#[derive(Wrapper, Builder)]
#[builder(setter(skip, strip_option), build_fn(skip), pattern = "owned")]
pub struct StreamEvents<'p> {
#[raw_wrapper]
ref_: NonNull<StreamEventsRef>,
raw: Pin<Box<StreamEventsRef>>,
hook: Pin<Box<Hook>>,
#[builder(setter)]
destroy: Option<DestroyCallback<'p>>,
#[builder(setter)]
state_changed: Option<StateChangedCallback<'p>>,
#[builder(setter)]
control_info: Option<ControlInfoCallback<'p>>,
#[builder(setter)]
io_changed: Option<IOChangedCallback<'p>>,
#[builder(setter)]
param_changed: Option<ParamChangedCallback<'p>>,
#[builder(setter)]
add_buffer: Option<AddBufferCallback<'p>>,
#[builder(setter)]
remove_buffer: Option<RemoveBufferCallback<'p>>,
#[builder(setter)]
process: Option<ProcessCallback<'p>>,
#[builder(setter)]
drained: Option<DrainedCallback<'p>>,
#[builder(setter)]
command: Option<CommandCallback<'p>>,
#[builder(setter)]
trigger_done: Option<TriggerDoneCallback<'p>>,
}
impl<'p> StreamEvents<'p> {
unsafe extern "C" fn destroy_call(data: *mut ::std::os::raw::c_void) {
if let Some(events) = (data as *mut StreamEvents).as_mut() {
if let Some(callback) = &mut events.destroy {
callback();
}
}
}
unsafe extern "C" fn state_changed_call(
data: *mut ::std::os::raw::c_void,
old: pw_stream_state,
state: pw_stream_state,
error: *const ::std::os::raw::c_char,
) {
if let Some(events) = (data as *mut StreamEvents).as_mut() {
if let Some(callback) = &mut events.state_changed {
callback(
State::from_raw(old),
State::from_raw(state),
error.as_ref().map(|e| CStr::from_ptr(e)),
);
}
}
}
unsafe extern "C" fn control_info_call(
data: *mut ::std::os::raw::c_void,
id: u32,
control: *const pw_stream_control,
) {
if !control.is_null() {
if let Some(events) = (data as *mut StreamEvents).as_mut() {
if let Some(callback) = &mut events.control_info {
callback(id, ControlRef::from_raw_ptr(control));
}
}
}
}
unsafe extern "C" fn io_changed_call(
data: *mut ::std::os::raw::c_void,
id: u32,
area: *mut ::std::os::raw::c_void,
size: u32,
) {
if !area.is_null() {
if let Some(events) = (data as *mut StreamEvents).as_mut() {
if let Some(callback) = &mut events.io_changed {
callback(IOValue::from_type_and_ptr(IOType::from(id), area));
}
}
}
}
unsafe extern "C" fn param_changed_call(
data: *mut ::std::os::raw::c_void,
id: u32,
param: *const spa_pod,
) {
if !param.is_null() {
if let Some(events) = (data as *mut StreamEvents).as_mut() {
if let Some(callback) = &mut events.param_changed {
callback(id, PodRef::from_raw_ptr(param));
}
}
}
}
unsafe extern "C" fn add_buffer_call(
data: *mut ::std::os::raw::c_void,
buffer: *mut pw_buffer,
) {
if !buffer.is_null() {
if let Some(events) = (data as *mut StreamEvents).as_mut() {
if let Some(callback) = &mut events.add_buffer {
callback(BufferRef::from_raw_ptr(buffer));
}
}
}
}
unsafe extern "C" fn remove_buffer_call(
data: *mut ::std::os::raw::c_void,
buffer: *mut pw_buffer,
) {
if !buffer.is_null() {
if let Some(events) = (data as *mut StreamEvents).as_mut() {
if let Some(callback) = &mut events.remove_buffer {
callback(BufferRef::from_raw_ptr(buffer));
}
}
}
}
unsafe extern "C" fn process_call(data: *mut ::std::os::raw::c_void) {
if let Some(events) = (data as *mut StreamEvents).as_mut() {
if let Some(callback) = &mut events.process {
callback();
}
}
}
unsafe extern "C" fn drained_call(data: *mut ::std::os::raw::c_void) {
if let Some(events) = (data as *mut StreamEvents).as_mut() {
if let Some(callback) = &mut events.drained {
callback();
}
}
}
unsafe extern "C" fn command_call(
data: *mut ::std::os::raw::c_void,
command: *const spa_command,
) {
if !command.is_null() {
if let Some(events) = (data as *mut StreamEvents).as_mut() {
if let Some(callback) = &mut events.command {
callback(CommandRef::from_raw_ptr(command));
}
}
}
}
unsafe extern "C" fn trigger_done_call(data: *mut ::std::os::raw::c_void) {
if let Some(events) = (data as *mut StreamEvents).as_mut() {
if let Some(callback) = &mut events.trigger_done {
callback();
}
}
}
pub fn hook(&self) -> &Pin<Box<Hook>> {
&self.hook
}
}
impl<'p> StreamEventsBuilder<'p> {
events_builder_build! {
StreamEvents<'p>,
pw_stream_events,
destroy => destroy_call,
state_changed => state_changed_call,
control_info => control_info_call,
io_changed => io_changed_call,
param_changed => param_changed_call,
add_buffer => add_buffer_call,
remove_buffer => remove_buffer_call,
process => process_call,
drained => drained_call,
command => command_call,
trigger_done => trigger_done_call,
}
}
impl Debug for StreamEvents<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("StreamEvents")
.field("raw", &self.raw)
.finish()
}
}