1
0
Fork 0
forked from wry/wry
wry/src/pipewire/pw_con.rs
2025-02-21 10:44:29 +01:00

454 lines
14 KiB
Rust

use {
crate::{
async_engine::{AsyncEngine, SpawnedFuture},
io_uring::{IoUring, IoUringError},
pipewire::{
pw_formatter::{PwFormatter, format},
pw_ifs::{
pw_client::{PwClient, PwClientMethods},
pw_client_node::{
PW_CLIENT_NODE_FACTORY, PW_CLIENT_NODE_INTERFACE, PW_CLIENT_NODE_VERSION,
PwClientNode,
},
pw_core::{PW_CORE_VERSION, PwCore, PwCoreMethods},
pw_registry::{PW_REGISTRY_VERSION, PwRegistry},
},
pw_mem::PwMemPool,
pw_object::{PwObject, PwObjectData, PwObjectError, PwOpcode},
pw_parser::{PwParser, PwParserError},
},
utils::{
bitfield::Bitfield,
bufio::{BufIo, BufIoError, BufIoIncoming, BufIoMessage},
clonecell::CloneCell,
copyhashmap::CopyHashMap,
errorfmt::ErrorFmt,
hash_map_ext::HashMapExt,
numcell::NumCell,
oserror::OsError,
xrd::xrd,
},
},
std::{
cell::{Cell, RefCell},
fmt::Display,
io::Write,
rc::{Rc, Weak},
},
thiserror::Error,
uapi::{OwnedFd, c},
};
#[derive(Debug, Error)]
pub enum PwConError {
#[error("Could not create a unix socket")]
CreateSocket(#[source] OsError),
#[error("Could not connect to the pipewire daemon")]
ConnectSocket(#[source] IoUringError),
#[error(transparent)]
BufIoError(#[from] BufIoError),
#[error("Server did not sent a required fd")]
MissingFd,
#[error("XDG_RUNTIME_DIR is not set")]
XrdNotSet,
#[error(transparent)]
PwObjectError(#[from] PwObjectError),
#[error(transparent)]
PwParserError(#[from] PwParserError),
}
pub struct PwConHolder {
pub con: Rc<PwCon>,
outgoing: Cell<Option<SpawnedFuture<()>>>,
incoming: Cell<Option<SpawnedFuture<()>>>,
}
pub struct PwCon {
send_seq: NumCell<u32>,
pub io: Rc<BufIo>,
holder: CloneCell<Weak<PwConHolder>>,
dead: Cell<bool>,
pub objects: CopyHashMap<u32, Rc<dyn PwObject>>,
pub ids: RefCell<Bitfield>,
pub mem: PwMemPool,
pub ring: Rc<IoUring>,
pub eng: Rc<AsyncEngine>,
pub owner: CloneCell<Option<Rc<dyn PwConOwner>>>,
registry_generation: Cell<u64>,
ack_registry_generation: Cell<u64>,
}
pub trait PwConOwner {
fn killed(&self) {}
}
impl PwCon {
pub fn create_client_node(self: &Rc<Self>, props: &[(String, String)]) -> Rc<PwClientNode> {
let node = Rc::new(PwClientNode {
data: self.proxy_data(),
con: self.clone(),
ios: Default::default(),
owner: CloneCell::new(None),
ports: Default::default(),
port_out_free: RefCell::new(Default::default()),
port_in_free: RefCell::new(Default::default()),
activation: Default::default(),
transport_in: Cell::new(None),
transport_out: Default::default(),
activations: Default::default(),
});
if !self.dead.get() {
self.objects.set(node.data.id, node.clone());
}
self.create_object(
PW_CLIENT_NODE_FACTORY,
PW_CLIENT_NODE_INTERFACE,
PW_CLIENT_NODE_VERSION,
props,
node.data.id,
);
node.send_update();
node
}
pub fn destroy_obj(&self, obj: &impl PwObject) {
obj.break_loops();
self.send2(0, "core", PwCoreMethods::Destroy, |f| {
f.write_struct(|f| {
f.write_uint(obj.data().id);
});
});
self.objects.remove(&obj.data().id);
}
pub fn kill(&self) {
for obj in self.objects.lock().drain_values() {
obj.break_loops();
}
self.io.shutdown();
self.dead.set(true);
if let Some(con) = self.holder.get().upgrade() {
con.outgoing.take();
con.incoming.take();
}
if let Some(owner) = self.owner.take() {
owner.killed();
}
}
pub fn id(&self) -> u32 {
self.ids.borrow_mut().acquire()
}
pub fn proxy_data(&self) -> PwObjectData {
PwObjectData {
id: self.id(),
bound_id: Cell::new(None),
sync_id: Default::default(),
}
}
pub fn send<P, O, F>(&self, proxy: &P, opcode: O, f: F)
where
P: PwObject,
O: PwOpcode,
F: FnOnce(&mut PwFormatter),
{
self.send2(proxy.data().id, proxy.interface(), opcode, f);
}
pub fn send2<O, F>(&self, id: u32, interface: &str, opcode: O, f: F)
where
O: PwOpcode,
F: FnOnce(&mut PwFormatter),
{
if self.dead.get() {
return;
}
let mut buf = self.io.buf();
let mut fds = vec![];
format(
&mut buf,
&mut fds,
id,
opcode.id(),
self.send_seq.fetch_add(1),
|fmt| {
f(fmt);
if self.ack_registry_generation.get() != self.registry_generation.get() {
let generation = self.registry_generation.get();
fmt.write_struct(|f| {
f.write_id(FOOTER_REGISTRY_GENERATION);
f.write_struct(|f| {
f.write_ulong(generation);
});
});
self.ack_registry_generation.set(generation);
}
},
);
if log::log_enabled!(log::Level::Trace) {
log::trace!("CALL {}@{}: `{:?}`:", interface, id, opcode);
let mut parser = PwParser::new(&buf[16..buf.len()], &fds);
while parser.len() > 0 {
log::trace!("{:#?}", parser.read_pod().unwrap());
}
}
self.io.send(BufIoMessage {
fds,
buf: buf.unwrap(),
});
}
#[expect(dead_code)]
pub fn sync<P: PwObject>(&self, p: &P) {
let seq = p.data().sync_id.fetch_add(1) + 1;
self.send2(0, "core", PwCoreMethods::Sync, |f| {
f.write_struct(|f| {
f.write_uint(p.data().id);
f.write_uint(seq);
});
});
}
pub fn send_hello(&self) {
self.send2(0, "core", PwCoreMethods::Hello, |f| {
f.write_struct(|f| f.write_int(PW_CORE_VERSION));
});
}
#[expect(dead_code)]
pub fn get_registry(self: &Rc<Self>) -> Rc<PwRegistry> {
let registry = Rc::new(PwRegistry {
data: self.proxy_data(),
_con: self.clone(),
});
if !self.dead.get() {
self.objects.set(registry.data.id, registry.clone());
}
self.send2(0, "core", PwCoreMethods::GetRegistry, |f| {
f.write_struct(|f| {
f.write_int(PW_REGISTRY_VERSION);
f.write_uint(registry.data.id);
});
});
registry
}
pub fn create_object(
&self,
factory: &str,
ty: &str,
version: i32,
props: &[(String, String)],
new_id: u32,
) {
self.send2(0, "core", PwCoreMethods::CreateObject, |f| {
f.write_struct(|f| {
f.write_string(factory);
f.write_string(ty);
f.write_int(version);
f.write_struct(|f| {
f.write_int(props.len() as _);
for (key, val) in props {
f.write_string(key);
f.write_string(val);
}
});
f.write_uint(new_id);
});
});
}
pub fn send_properties(&self) {
self.send2(1, "client", PwClientMethods::UpdateProperties, |f| {
f.write_struct(|f| {
f.write_struct(|f| {
f.write_int(1);
f.write_string("application.name");
f.write_string("jay-portal");
});
});
});
}
async fn handle_outgoing(self: Rc<Self>) {
if let Err(e) = self.io.clone().outgoing().await {
log::error!("{}", ErrorFmt(e));
}
self.kill();
}
async fn handle_incoming(self: Rc<Self>) {
let incoming = Incoming {
incoming: self.io.clone().incoming(),
con: self.clone(),
buf: vec![],
fds: vec![],
};
incoming.run().await;
}
}
impl Drop for PwConHolder {
fn drop(&mut self) {
self.con.owner.take();
self.con.kill();
}
}
impl PwConHolder {
pub async fn new(eng: &Rc<AsyncEngine>, ring: &Rc<IoUring>) -> Result<Rc<Self>, PwConError> {
let fd = match uapi::socket(c::AF_UNIX, c::SOCK_STREAM | c::SOCK_CLOEXEC, 0) {
Ok(fd) => Rc::new(fd),
Err(e) => return Err(PwConError::CreateSocket(e.into())),
};
let mut addr = c::sockaddr_un {
sun_family: c::AF_UNIX as _,
..uapi::pod_zeroed()
};
let xrd = match xrd() {
Some(xrd) => xrd,
_ => return Err(PwConError::XrdNotSet),
};
{
let mut path = uapi::as_bytes_mut(&mut addr.sun_path[..]);
let _ = write!(path, "{}/pipewire-0", xrd);
}
if let Err(e) = ring.connect(&fd, &addr).await {
return Err(PwConError::ConnectSocket(e));
}
let io = Rc::new(BufIo::new(&fd, ring));
let data = Rc::new(PwCon {
send_seq: Default::default(),
io,
holder: Default::default(),
dead: Cell::new(false),
objects: Default::default(),
ids: Default::default(),
mem: Default::default(),
ring: ring.clone(),
eng: eng.clone(),
owner: Default::default(),
registry_generation: Cell::new(0),
ack_registry_generation: Cell::new(0),
});
let core = Rc::new(PwCore {
data: data.proxy_data(),
con: data.clone(),
});
let client = Rc::new(PwClient {
data: data.proxy_data(),
_con: data.clone(),
});
data.objects.set(0, core.clone());
data.objects.set(1, client.clone());
data.send_hello();
data.send_properties();
let con = Rc::new(PwConHolder {
outgoing: Cell::new(Some(
eng.spawn("pw outgoing", data.clone().handle_outgoing()),
)),
incoming: Cell::new(Some(
eng.spawn("pw incoming", data.clone().handle_incoming()),
)),
con: data,
});
con.con.holder.set(Rc::downgrade(&con));
Ok(con)
}
}
struct Incoming {
con: Rc<PwCon>,
incoming: BufIoIncoming,
buf: Vec<u8>,
fds: Vec<Rc<OwnedFd>>,
}
impl Incoming {
async fn run(mut self) {
loop {
if let Err(e) = self.handle_msg().await {
log::error!("Could not handle incoming message: {}", ErrorFmt(e));
self.con.kill();
return;
}
}
}
async fn handle_msg(&mut self) -> Result<(), PwConError> {
self.buf.clear();
self.incoming.fill_msg_buf(16, &mut self.buf).await?;
let id: u32 = uapi::pod_read(&self.buf[0..4]).unwrap();
let p2: u32 = uapi::pod_read(&self.buf[4..8]).unwrap();
let opcode = (p2 >> 24) as u8;
let size = (p2 & 0xff_ffff) as usize;
let _seq: u32 = uapi::pod_read(&self.buf[8..12]).unwrap();
let n_fds: u32 = uapi::pod_read(&self.buf[12..16]).unwrap();
for _ in 0..n_fds {
match self.incoming.fds.pop_front() {
Some(fd) => self.fds.push(fd),
_ => return Err(PwConError::MissingFd),
}
}
self.buf.clear();
self.incoming.fill_msg_buf(size, &mut self.buf).await?;
if let Err(e) = self.handle_msg_data(id, opcode) {
log::warn!("Could not handle incoming message: {}", ErrorFmt(e));
}
self.fds.clear();
Ok(())
}
fn handle_msg_data(&self, id: u32, opcode: u8) -> Result<(), PwConError> {
let parser = PwParser::new(&self.buf, &self.fds);
{
let mut parser = parser;
parser.skip()?;
if parser.len() > 0 {
let s1 = parser.read_struct()?;
let mut p2 = s1.fields;
while p2.len() > 0 {
let opcode = p2.read_id()?;
let s2 = p2.read_struct()?;
if opcode == FOOTER_REGISTRY_GENERATION {
let mut p3 = s2.fields;
let generation = p3.read_ulong()?;
self.con.registry_generation.set(generation);
log::debug!("registry generation = {}", generation);
} else {
log::warn!("Unknown message footer: {}", opcode);
}
}
}
}
if let Some(obj) = self.con.objects.get(&id) {
'log: {
if log::log_enabled!(log::Level::Trace) {
let s;
let op: &dyn Display = match obj.event_name(opcode) {
Some(e) => {
s = e;
if e == "Done" && obj.interface() == "core" {
break 'log;
}
&s
}
_ => &opcode,
};
log::trace!("EVENT {}@{}: `{}`:", obj.interface(), obj.data().id, op);
let mut parser = parser;
while parser.len() > 0 {
log::trace!("{:#?}", parser.read_pod().unwrap());
}
}
}
obj.handle_msg(opcode, parser)?;
}
Ok(())
}
}
const FOOTER_REGISTRY_GENERATION: u32 = 0;