breadx/display/
prefetch.rs

1//               Copyright John Nunley, 2022.
2// Distributed under the Boost Software License, Version 1.0.
3//       (See accompanying file LICENSE or copy at
4//         https://www.boost.org/LICENSE_1_0.txt)
5
6use x11rb_protocol::{
7    protocol::{
8        bigreq::EnableRequest,
9        xc_misc::{GetXIDRangeReply, GetXIDRangeRequest},
10        xproto::{GetInputFocusReply, GetInputFocusRequest, QueryExtensionRequest},
11    },
12    x11_utils::{ExtensionInformation, ReplyRequest},
13    SequenceNumber,
14};
15
16use super::{
17    raw_request::{from_reply_request, BufferedRequest},
18    Display, RawReply,
19};
20use crate::Result;
21
22cfg_async! {
23    use super::{AsyncStatus, CanBeAsyncDisplay};
24    use core::task::Context;
25}
26
27/// Some internal data that needs to be fetched through
28/// another request.
29pub struct Prefetch<T: PrefetchTarget> {
30    state: PrefetchState<T>,
31}
32
33enum PrefetchState<T: PrefetchTarget> {
34    /// In the process of formatting.
35    Formatting(Option<BufferedRequest>),
36    /// We're in the process of sending this data.
37    #[allow(dead_code)]
38    Sending(Option<BufferedRequest>, u64),
39    /// We've sent this data and are waiting for a reply.
40    Waiting(SequenceNumber),
41    /// The data is available for us; the request is complete.
42    Complete(T::Target),
43}
44
45/// A request that needs to be prefetched.
46pub trait PrefetchTarget: ReplyRequest {
47    /// The resulting data from the prefetch.
48    type Target;
49
50    /// Map the reply to the prefetch target.
51    fn map_reply(reply: Self::Reply) -> Self::Target;
52
53    /// The target when an X11 error occurs.
54    fn on_x11_error() -> Self::Target;
55}
56
57impl<T: PrefetchTarget + Default> Default for Prefetch<T> {
58    fn default() -> Self {
59        Self::new(Default::default())
60    }
61}
62
63impl<T: PrefetchTarget> From<PrefetchState<T>> for Prefetch<T> {
64    fn from(state: PrefetchState<T>) -> Self {
65        Self { state }
66    }
67}
68
69impl<T: PrefetchTarget> Prefetch<T> {
70    pub fn new(req: T) -> Self {
71        #[allow(clippy::redundant_closure_for_method_calls)]
72        let req = from_reply_request(req, |req| req.into());
73        PrefetchState::Formatting(Some(req)).into()
74    }
75
76    /// Get the target if and only if this prefetch has
77    /// already resolved.
78    pub(crate) fn get_if_resolved(&self) -> Option<&T::Target> {
79        match self.state {
80            PrefetchState::Complete(ref c) => Some(c),
81            _ => None,
82        }
83    }
84
85    /// Format the request.
86    ///
87    /// The user may use this in order to format the request.
88    pub(crate) fn request_to_format(&mut self) -> &mut BufferedRequest {
89        match self.state {
90            PrefetchState::Formatting(Some(ref mut req)) => req,
91            _ => panic!("Prefetch is not formatting"),
92        }
93    }
94
95    /// Send with an overridden sequence number.
96    pub(crate) fn sent_override(&mut self, seq: u64) {
97        match self.state {
98            PrefetchState::Sending(..) | PrefetchState::Formatting(..) => {
99                *self = PrefetchState::Waiting(seq).into();
100            }
101            _ => panic!("Prefetch is not sending or formatting"),
102        }
103    }
104
105    /// The sequence number of the reply we're waiting for.
106    pub(crate) fn sequence(&self) -> u64 {
107        match self.state {
108            PrefetchState::Waiting(ref seq) => *seq,
109            _ => panic!("Prefetch is not waiting"),
110        }
111    }
112
113    /// Read in the reply.
114    pub(crate) fn read_reply(&mut self, reply: RawReply) -> Result<()> {
115        let reply: T::Reply = reply.into_reply()?;
116        let mapped = T::map_reply(reply);
117        *self = PrefetchState::Complete(mapped).into();
118        Ok(())
119    }
120
121    /// Error out.
122    pub(crate) fn on_x11_error(&mut self) {
123        *self = PrefetchState::Complete(T::on_x11_error()).into();
124    }
125
126    /// Evaluate the prefetch while blocking.
127    pub fn evaluate(&mut self, display: &mut impl Display) -> Result<&T::Target> {
128        // call all functions in order
129        let request = self.request_to_format();
130        let seq = request.take(|request| display.send_request_raw(request))?;
131        self.sent_override(seq);
132        match display.wait_for_reply_raw(self.sequence()) {
133            Ok(reply) => {
134                self.read_reply(reply)?;
135            }
136            Err(e) if e.is_protocol_error() => {
137                self.on_x11_error();
138            }
139            Err(e) => return Err(e),
140        };
141        Ok(self.get_if_resolved().unwrap())
142    }
143}
144
145cfg_async! {
146    impl<T: PrefetchTarget> Prefetch<T> {
147        fn not_yet_formatted(&self) -> bool {
148            matches!(self.state, PrefetchState::Formatting(_))
149        }
150
151        fn not_yet_sent(&self) -> bool {
152            matches!(self.state, PrefetchState::Formatting(_) | PrefetchState::Sending(..))
153        }
154
155        fn not_yet_read(&self) -> bool {
156            !matches!(self.state, PrefetchState::Complete(_))
157        }
158
159        /// Indicate that the request has been sent.
160        pub(crate) fn sent(&mut self) {
161            match self.state {
162                PrefetchState::Sending(_, seq) => *self = PrefetchState::Waiting(seq).into(),
163                _ => panic!("Prefetch is not sending"),
164            }
165        }
166
167        /// Indicate that we've formatted the request.
168        pub(crate) fn formatted(&mut self, seq: u64) {
169            match self.state {
170                PrefetchState::Formatting(ref mut request) => {
171                    *self = PrefetchState::Sending(request.take(), seq).into();
172                }
173                _ => panic!("Prefetch is not formatting"),
174            }
175        }
176
177        /// Get the request to send.
178        pub(crate) fn request_to_send(&mut self) -> &mut BufferedRequest {
179            match self.state {
180                PrefetchState::Sending(Some(ref mut req), ..)
181                | PrefetchState::Formatting(Some(ref mut req)) => req,
182                _ => panic!("Prefetch is not sending"),
183            }
184        }
185
186        /// Evaluate the prefetch, but avoid blocking.
187        pub fn try_evaluate(
188            &mut self,
189            display: &mut impl CanBeAsyncDisplay,
190            ctx: &mut Context<'_>,
191        ) -> Result<AsyncStatus<&T::Target>> {
192            let span = tracing::trace_span!(
193                "try_evaluate",
194                formatted = !self.not_yet_formatted(),
195                sent = !self.not_yet_sent(),
196                read = !self.not_yet_read()
197            );
198            let _enter = span.enter();
199
200            // call all functions in order
201            if self.not_yet_formatted() {
202                tracing::trace!("formatting request");
203                let request = self.request_to_format();
204                let seq = mtry! {
205                    request.borrow(|request| {
206                        display.format_request(request, ctx)
207                    })
208                };
209                self.formatted(seq);
210            }
211
212            if self.not_yet_sent() {
213                tracing::trace!("sending request");
214                let request = self.request_to_send();
215                mtry! {
216                    request.borrow(|request| {
217                        display.try_send_request_raw(request, ctx)
218                    })
219                };
220                self.sent();
221            }
222
223            if self.not_yet_read() {
224                tracing::trace!("receiving reply");
225                match display.try_wait_for_reply_raw(self.sequence(), ctx) {
226                    Ok(AsyncStatus::Ready(t)) => self.read_reply(t)?,
227                    Ok(status) => return Ok(status.map(|_| unreachable!())),
228                    Err(e) if e.is_protocol_error() => {
229                        self.on_x11_error();
230                    }
231                    Err(e) => return Err(e),
232                }
233            }
234
235            Ok(AsyncStatus::Ready(self.get_if_resolved().unwrap()))
236        }
237    }
238}
239
240// prefetch targets: query for bigreq and query for extension
241
242impl PrefetchTarget for EnableRequest {
243    type Target = Option<usize>;
244
245    fn map_reply(reply: Self::Reply) -> Self::Target {
246        let maxlen = reply.maximum_request_length as usize;
247        Some(maxlen)
248    }
249
250    fn on_x11_error() -> Self::Target {
251        None
252    }
253}
254
255impl<'a> PrefetchTarget for QueryExtensionRequest<'a> {
256    type Target = Option<ExtensionInformation>;
257
258    fn map_reply(reply: Self::Reply) -> Self::Target {
259        if !reply.present {
260            return None;
261        }
262
263        let info = ExtensionInformation {
264            major_opcode: reply.major_opcode,
265            first_error: reply.first_error,
266            first_event: reply.first_event,
267        };
268
269        Some(info)
270    }
271
272    fn on_x11_error() -> Self::Target {
273        None
274    }
275}
276
277impl PrefetchTarget for GetInputFocusRequest {
278    type Target = GetInputFocusReply;
279
280    fn map_reply(reply: Self::Reply) -> Self::Target {
281        reply
282    }
283
284    fn on_x11_error() -> Self::Target {
285        tracing::error!("synchronization should never error out");
286        GetInputFocusReply::default()
287    }
288}
289
290impl PrefetchTarget for GetXIDRangeRequest {
291    type Target = GetXIDRangeReply;
292
293    fn map_reply(reply: Self::Reply) -> Self::Target {
294        reply
295    }
296
297    fn on_x11_error() -> Self::Target {
298        tracing::error!("XID refresh should never error out");
299        GetXIDRangeReply::default()
300    }
301}