Skip to main content

asdf_overlay_client/
client.rs

1//! Client side IPC connection and event stream implementation.
2//!
3//! Provides interfaces for sending requests via ipc and receive events.
4
5use std::sync::{Arc, Weak};
6
7use anyhow::{Context as AnyhowContext, bail};
8use asdf_overlay_common::{
9    event::OverlayEvent,
10    ipc::{ClientRequest, Frame, ServerToClientPacket},
11    request::{
12        self, Request, Requestable,
13        surface::{SurfaceRequest, SurfaceRequestable},
14        window::{WindowRequest, WindowRequestable},
15    },
16};
17use dashmap::DashMap;
18use serde::de::DeserializeOwned;
19use tokio::{
20    io::{AsyncReadExt, AsyncWriteExt, WriteHalf, split},
21    net::windows::named_pipe::NamedPipeClient,
22    sync::{mpsc, oneshot},
23    task::JoinHandle,
24};
25
26/// IPC client connection for handling requests and responses.
27pub struct IpcClientConn {
28    next_id: u32,
29    tx: WriteHalf<NamedPipeClient>,
30    buf: Vec<u8>,
31    map: Weak<DashMap<u32, oneshot::Sender<Vec<u8>>>>,
32    read_task: JoinHandle<anyhow::Result<()>>,
33}
34
35impl IpcClientConn {
36    /// Create a new [`IpcClientConn`] and [`IpcClientEventStream`] from a connected named pipe client.
37    pub async fn new(client: NamedPipeClient) -> anyhow::Result<(Self, IpcClientEventStream)> {
38        let (mut rx, tx) = split(client);
39
40        let map = Arc::new(DashMap::<u32, oneshot::Sender<Vec<u8>>>::new());
41        let (event_tx, event_rx) = mpsc::unbounded_channel();
42
43        let read_task = tokio::spawn({
44            let map = map.clone();
45
46            async move {
47                let mut buf = Vec::new();
48                loop {
49                    let frame = Frame::read(&mut rx).await?;
50                    buf.resize(frame.size as usize, 0_u8);
51                    rx.read_exact(&mut buf).await?;
52
53                    let packet: ServerToClientPacket = rmp_serde::from_slice(&buf)?;
54                    match packet {
55                        ServerToClientPacket::Response { id, payload } => {
56                            if let Some((_, sender)) = map.remove(&id) {
57                                _ = sender.send(payload);
58                            }
59                        }
60
61                        ServerToClientPacket::Event(event) => {
62                            let _ = event_tx.send(event);
63                        }
64                    }
65                }
66            }
67        });
68
69        let conn = IpcClientConn {
70            next_id: 0,
71            tx,
72            buf: vec![],
73            map: Arc::downgrade(&map),
74            read_task,
75        };
76
77        let stream = IpcClientEventStream { inner: event_rx };
78
79        Ok((conn, stream))
80    }
81
82    /// Get request interface for a specific window id.
83    /// The returned interface can be used to send window-specific requests.
84    #[inline]
85    pub const fn window(&mut self, id: u32) -> IpcClientConnWindow<'_> {
86        IpcClientConnWindow { inner: self, id }
87    }
88
89    /// Get request interface for a specific surface id.
90    /// The returned interface can be used to send surface-specific requests.
91    #[inline]
92    pub const fn surface(&mut self, id: u64) -> IpcClientConnSurface<'_> {
93        IpcClientConnSurface { inner: self, id }
94    }
95
96    /// Send a request and wait for the response.
97    /// Returns an error if the connection is closed or the request fails.
98    pub async fn request<T: Requestable>(&mut self, req: T) -> Result<T::Response> {
99        self.request_inner::<T::Response>(req.into()).await
100    }
101
102    async fn request_inner<T: DeserializeOwned>(&mut self, req: Request) -> Result<T> {
103        let data = self
104            .send(req)
105            .await
106            .context("failed to send request")?
107            .await
108            .context("failed to receive response")?;
109
110        let res = rmp_serde::from_slice::<request::Result<T>>(&data)
111            .context("invalid response payload")?;
112        Ok(res.map_err(|err| request::Error::new(&err))?)
113    }
114
115    /// Send a request without waiting for the response.
116    /// Returns a oneshot receiver that can be used to receive the response data.
117    async fn send(&mut self, req: Request) -> Result<oneshot::Receiver<Vec<u8>>> {
118        let Some(map) = self.map.upgrade() else {
119            bail!("connection closed");
120        };
121
122        let id = self.next_id;
123        self.next_id += 1;
124
125        self.buf.clear();
126        rmp_serde::encode::write(&mut self.buf, &ClientRequest { id, req })?;
127        Frame {
128            size: self.buf.len() as _,
129        }
130        .write(&mut self.tx)
131        .await?;
132
133        let (tx, rx) = oneshot::channel();
134        map.insert(id, tx);
135        self.tx.write_all(&self.buf).await?;
136
137        self.tx.flush().await?;
138        Ok(rx)
139    }
140}
141
142impl Drop for IpcClientConn {
143    fn drop(&mut self) {
144        self.read_task.abort();
145    }
146}
147
148/// Client request result type.
149pub type Result<T> = core::result::Result<T, anyhow::Error>;
150
151/// Error type for IPC client connection.
152#[derive(Debug, thiserror::Error)]
153pub enum Error {
154    #[error("ipc io error")]
155    Io(
156        #[from]
157        #[source]
158        anyhow::Error,
159    ),
160
161    #[error("request failed")]
162    Request(#[from] request::Error),
163}
164
165/// Request interface for a specific window id.
166pub struct IpcClientConnWindow<'a> {
167    inner: &'a mut IpcClientConn,
168    id: u32,
169}
170
171impl IpcClientConnWindow<'_> {
172    /// Send a window request.
173    pub async fn request<T: WindowRequestable>(&mut self, req: T) -> anyhow::Result<T::Response> {
174        self.inner
175            .request_inner::<T::Response>(Request::Window(WindowRequest {
176                id: self.id,
177                kind: req.into(),
178            }))
179            .await
180    }
181}
182/// Request interface for a specific surface id.
183pub struct IpcClientConnSurface<'a> {
184    inner: &'a mut IpcClientConn,
185    id: u64,
186}
187
188impl IpcClientConnSurface<'_> {
189    /// Send a surface request.
190    pub async fn request<T: SurfaceRequestable>(&mut self, req: T) -> anyhow::Result<T::Response> {
191        self.inner
192            .request_inner::<T::Response>(Request::Surface(SurfaceRequest {
193                id: self.id,
194                kind: req.into(),
195            }))
196            .await
197    }
198}
199
200/// Event stream for receiving server events.
201pub struct IpcClientEventStream {
202    inner: mpsc::UnboundedReceiver<OverlayEvent>,
203}
204
205impl IpcClientEventStream {
206    /// Receive the next event.
207    /// Returns `None` if the connection is closed.
208    #[inline]
209    pub async fn recv(&mut self) -> Option<OverlayEvent> {
210        self.inner.recv().await
211    }
212}