1use x11rb::connection::{BufWithFds, ReplyOrError, RequestKind};
6use x11rb_protocol::protocol::xproto::ListFontsWithInfoReply;
7use x11rb_protocol::{DiscardMode, SequenceNumber};
8
9use crate::connection::{Connection, RequestConnection};
10use crate::errors::{ConnectionError, ReplyError};
11use crate::x11_utils::{TryParse, TryParseFd};
12
13use futures_lite::{ready, stream::Stream};
14use std::future::Future;
15use std::marker::PhantomData;
16use std::mem;
17use std::pin::Pin;
18use std::task::{Context, Poll};
19
20#[cfg(feature = "record")]
21use crate::protocol::record::EnableContextReply;
22
23#[derive(Debug)]
25pub struct VoidCookie<'conn, C: RequestConnection + ?Sized> {
26 conn: &'conn C,
27 sequence: SequenceNumber,
28}
29
30impl<'conn, C: Connection + ?Sized> VoidCookie<'conn, C> {
31 pub fn new(conn: &'conn C, sequence: SequenceNumber) -> Self {
33 Self { conn, sequence }
34 }
35
36 pub fn sequence_number(&self) -> SequenceNumber {
38 self.sequence
39 }
40
41 pub async fn check(self) -> Result<(), ReplyError> {
43 let res = self.conn.check_for_raw_error(self.sequence).await;
44
45 let (conn, _) = self.consume();
47
48 match res? {
49 Some(e) => Err(conn.parse_error(e.as_ref())?.into()),
50 None => Ok(()),
51 }
52 }
53
54 pub fn ignore_error(self) {
56 let (conn, seq) = self.consume();
57 conn.discard_reply(seq, RequestKind::IsVoid, DiscardMode::DiscardReplyAndError);
58 }
59
60 fn consume(self) -> (&'conn C, SequenceNumber) {
62 let res = (self.conn, self.sequence);
63 mem::forget(self);
64 res
65 }
66}
67
68impl<'conn, C: RequestConnection + ?Sized> Drop for VoidCookie<'conn, C> {
69 fn drop(&mut self) {
70 self.conn.discard_reply(
71 self.sequence,
72 RequestKind::IsVoid,
73 DiscardMode::DiscardReply,
74 );
75 }
76}
77
78#[derive(Debug)]
80struct RawCookie<'a, C: RequestConnection + ?Sized> {
81 conn: &'a C,
82 sequence: SequenceNumber,
83}
84
85impl<'a, C: RequestConnection + ?Sized> RawCookie<'a, C> {
86 fn new(conn: &'a C, sequence: SequenceNumber) -> Self {
87 Self { conn, sequence }
88 }
89
90 fn consume(self) -> (&'a C, SequenceNumber) {
91 let res = (self.conn, self.sequence);
92 mem::forget(self);
93 res
94 }
95}
96
97impl<'a, C: RequestConnection + ?Sized> Drop for RawCookie<'a, C> {
98 fn drop(&mut self) {
99 self.conn.discard_reply(
100 self.sequence,
101 RequestKind::HasResponse,
102 DiscardMode::DiscardReply,
103 );
104 }
105}
106
107#[derive(Debug)]
109pub struct Cookie<'conn, C: RequestConnection + ?Sized, R> {
110 raw: RawCookie<'conn, C>,
111 capture: PhantomData<R>,
112}
113
114impl<'conn, C: Connection + ?Sized, R: TryParse> Cookie<'conn, C, R> {
115 pub fn new(conn: &'conn C, sequence: SequenceNumber) -> Self {
117 Self {
118 raw: RawCookie::new(conn, sequence),
119 capture: PhantomData,
120 }
121 }
122
123 pub fn sequence_number(&self) -> SequenceNumber {
125 self.raw.sequence
126 }
127
128 pub async fn raw_reply(self) -> Result<C::Buf, ReplyError> {
130 let reply_or_error = self
132 .raw
133 .conn
134 .wait_for_reply_or_raw_error(self.raw.sequence)
135 .await;
136
137 let (conn, _) = self.raw.consume();
139
140 match reply_or_error? {
142 ReplyOrError::Reply(reply) => Ok(reply),
143 ReplyOrError::Error(error) => Err(conn.parse_error(error.as_ref())?.into()),
144 }
145 }
146
147 pub async fn raw_reply_unchecked(self) -> Result<Option<C::Buf>, ConnectionError> {
149 let res = self.raw.conn.wait_for_reply(self.raw.sequence).await;
151
152 let _ = self.raw.consume();
154
155 res
157 }
158
159 pub async fn reply(self) -> Result<R, ReplyError> {
161 let buf = self.raw_reply().await?;
162
163 let (reply, _) = R::try_parse(buf.as_ref())?;
165 Ok(reply)
166 }
167
168 pub async fn reply_unchecked(self) -> Result<Option<R>, ConnectionError> {
170 let buf = self.raw_reply_unchecked().await?;
171
172 let reply = buf.map(|buf| R::try_parse(buf.as_ref()).unwrap().0);
174 Ok(reply)
175 }
176}
177
178#[derive(Debug)]
180pub struct CookieWithFds<'conn, C: RequestConnection + ?Sized, R> {
181 raw: RawCookie<'conn, C>,
182 capture: PhantomData<R>,
183}
184
185impl<'conn, C: Connection + ?Sized, R: TryParseFd> CookieWithFds<'conn, C, R> {
186 pub fn new(conn: &'conn C, sequence: SequenceNumber) -> Self {
188 Self {
189 raw: RawCookie::new(conn, sequence),
190 capture: PhantomData,
191 }
192 }
193
194 pub fn sequence_number(&self) -> SequenceNumber {
196 self.raw.sequence
197 }
198
199 pub async fn raw_reply(self) -> Result<BufWithFds<C::Buf>, ReplyError> {
201 let reply_or_error = self
203 .raw
204 .conn
205 .wait_for_reply_with_fds_raw(self.raw.sequence)
206 .await;
207
208 let (conn, _) = self.raw.consume();
210
211 match reply_or_error? {
213 ReplyOrError::Reply(reply) => Ok(reply),
214 ReplyOrError::Error(error) => Err(conn.parse_error(error.as_ref())?.into()),
215 }
216 }
217
218 pub async fn reply(self) -> Result<R, ReplyError> {
220 let (buf, mut fds) = self.raw_reply().await?;
221
222 let (reply, _) = R::try_parse_fd(buf.as_ref(), &mut fds)?;
224 Ok(reply)
225 }
226}
227
228macro_rules! multiple_reply_cookie {
229 (
230 $(#[$outer:meta])*
231 pub struct $name:ident for $reply:ident
232 ) => {
233 $(#[$outer])*
234 pub struct $name<'conn, C> where C: RequestConnection + ?Sized {
235 raw: Option<RawCookie<'conn, C>>,
237
238 wait: Option<Pin<Box<dyn Future<Output = Result<C::Buf, ReplyError>> + Send + 'conn>>>,
240 }
241
242 impl<'conn, C: RequestConnection + std::fmt::Debug + ?Sized> std::fmt::Debug for $name<'conn, C> {
243 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
244 f.debug_struct(stringify!($name))
245 .field("raw", &self.raw)
246 .finish_non_exhaustive()
247 }
248 }
249
250 impl<'conn, C: RequestConnection + ?Sized> $name<'conn, C> {
251 pub(crate) fn new(
252 cookie: Cookie<'conn, C, $reply>,
253 ) -> Self {
254 Self {
255 raw: Some(cookie.raw),
256 wait: None,
257 }
258 }
259
260 pub fn sequence_number(&self) -> Option<SequenceNumber> {
262 self.raw.as_ref().map(|raw| raw.sequence)
263 }
264 }
265
266 impl<C: RequestConnection + ?Sized> Stream for $name<'_, C> {
267 type Item = Result<$reply, ReplyError>;
268
269 fn poll_next(
270 mut self: Pin<&mut Self>,
271 cx: &mut Context<'_>,
272 ) -> Poll<Option<Self::Item>> {
273 loop {
274 if let Some(wait) = self.wait.as_mut() {
276 let reply = {
278 let raw_reply = match ready!(wait.as_mut().poll(cx)) {
279 Ok(reply) => reply,
280 Err(e) => return Poll::Ready(Some(Err(e))),
281 };
282
283 match $reply::try_parse(raw_reply.as_ref()) {
285 Ok((reply, _)) => reply,
286 Err(e) => return Poll::Ready(Some(Err(e.into()))),
287 }
288 };
289
290 if Self::is_last(&reply) {
291 self.wait = None;
293 self.raw = None;
294 return Poll::Ready(Some(Ok(reply)));
295 } else {
296 return Poll::Ready(Some(Ok(reply)));
298 }
299 }
300
301 let cookie = match self.raw.take() {
303 Some(cookie) => cookie,
304 None => return Poll::Ready(None),
305 };
306
307 self.wait = Some(
309 cookie.conn.wait_for_reply_or_error(cookie.sequence)
310 );
311 self.raw = Some(cookie);
312 }
313 }
314 }
315 };
316}
317
318multiple_reply_cookie!(
319 pub struct ListFontsWithInfoCookie for ListFontsWithInfoReply
324);
325
326impl<C> ListFontsWithInfoCookie<'_, C>
327where
328 C: RequestConnection + ?Sized,
329{
330 fn is_last(reply: &ListFontsWithInfoReply) -> bool {
331 reply.name.is_empty()
332 }
333}
334
335#[cfg(feature = "record")]
336multiple_reply_cookie!(
337 pub struct RecordEnableContextCookie for EnableContextReply
342);
343
344#[cfg(feature = "record")]
345impl<C> RecordEnableContextCookie<'_, C>
346where
347 C: RequestConnection + ?Sized,
348{
349 fn is_last(reply: &EnableContextReply) -> bool {
350 reply.category == 5
353 }
354}