use x11rb_protocol::{
protocol::{
bigreq::EnableRequest,
xc_misc::{GetXIDRangeReply, GetXIDRangeRequest},
xproto::{GetInputFocusReply, GetInputFocusRequest, QueryExtensionRequest},
},
x11_utils::{ExtensionInformation, ReplyRequest},
SequenceNumber,
};
use super::{
raw_request::{from_reply_request, BufferedRequest},
Display, RawReply,
};
use crate::Result;
cfg_async! {
use super::{AsyncStatus, CanBeAsyncDisplay};
use core::task::Context;
}
pub struct Prefetch<T: PrefetchTarget> {
state: PrefetchState<T>,
}
enum PrefetchState<T: PrefetchTarget> {
Formatting(Option<BufferedRequest>),
#[allow(dead_code)]
Sending(Option<BufferedRequest>, u64),
Waiting(SequenceNumber),
Complete(T::Target),
}
pub trait PrefetchTarget: ReplyRequest {
type Target;
fn map_reply(reply: Self::Reply) -> Self::Target;
fn on_x11_error() -> Self::Target;
}
impl<T: PrefetchTarget + Default> Default for Prefetch<T> {
fn default() -> Self {
Self::new(Default::default())
}
}
impl<T: PrefetchTarget> From<PrefetchState<T>> for Prefetch<T> {
fn from(state: PrefetchState<T>) -> Self {
Self { state }
}
}
impl<T: PrefetchTarget> Prefetch<T> {
pub fn new(req: T) -> Self {
#[allow(clippy::redundant_closure_for_method_calls)]
let req = from_reply_request(req, |req| req.into());
PrefetchState::Formatting(Some(req)).into()
}
pub(crate) fn get_if_resolved(&self) -> Option<&T::Target> {
match self.state {
PrefetchState::Complete(ref c) => Some(c),
_ => None,
}
}
pub(crate) fn request_to_format(&mut self) -> &mut BufferedRequest {
match self.state {
PrefetchState::Formatting(Some(ref mut req)) => req,
_ => panic!("Prefetch is not formatting"),
}
}
pub(crate) fn sent_override(&mut self, seq: u64) {
match self.state {
PrefetchState::Sending(..) | PrefetchState::Formatting(..) => {
*self = PrefetchState::Waiting(seq).into();
}
_ => panic!("Prefetch is not sending or formatting"),
}
}
pub(crate) fn sequence(&self) -> u64 {
match self.state {
PrefetchState::Waiting(ref seq) => *seq,
_ => panic!("Prefetch is not waiting"),
}
}
pub(crate) fn read_reply(&mut self, reply: RawReply) -> Result<()> {
let reply: T::Reply = reply.into_reply()?;
let mapped = T::map_reply(reply);
*self = PrefetchState::Complete(mapped).into();
Ok(())
}
pub(crate) fn on_x11_error(&mut self) {
*self = PrefetchState::Complete(T::on_x11_error()).into();
}
pub fn evaluate(&mut self, display: &mut impl Display) -> Result<&T::Target> {
let request = self.request_to_format();
let seq = request.take(|request| display.send_request_raw(request))?;
self.sent_override(seq);
match display.wait_for_reply_raw(self.sequence()) {
Ok(reply) => {
self.read_reply(reply)?;
}
Err(e) if e.is_protocol_error() => {
self.on_x11_error();
}
Err(e) => return Err(e),
};
Ok(self.get_if_resolved().unwrap())
}
}
cfg_async! {
impl<T: PrefetchTarget> Prefetch<T> {
fn not_yet_formatted(&self) -> bool {
matches!(self.state, PrefetchState::Formatting(_))
}
fn not_yet_sent(&self) -> bool {
matches!(self.state, PrefetchState::Formatting(_) | PrefetchState::Sending(..))
}
fn not_yet_read(&self) -> bool {
!matches!(self.state, PrefetchState::Complete(_))
}
pub(crate) fn sent(&mut self) {
match self.state {
PrefetchState::Sending(_, seq) => *self = PrefetchState::Waiting(seq).into(),
_ => panic!("Prefetch is not sending"),
}
}
pub(crate) fn formatted(&mut self, seq: u64) {
match self.state {
PrefetchState::Formatting(ref mut request) => {
*self = PrefetchState::Sending(request.take(), seq).into();
}
_ => panic!("Prefetch is not formatting"),
}
}
pub(crate) fn request_to_send(&mut self) -> &mut BufferedRequest {
match self.state {
PrefetchState::Sending(Some(ref mut req), ..)
| PrefetchState::Formatting(Some(ref mut req)) => req,
_ => panic!("Prefetch is not sending"),
}
}
pub fn try_evaluate(
&mut self,
display: &mut impl CanBeAsyncDisplay,
ctx: &mut Context<'_>,
) -> Result<AsyncStatus<&T::Target>> {
let span = tracing::trace_span!(
"try_evaluate",
formatted = !self.not_yet_formatted(),
sent = !self.not_yet_sent(),
read = !self.not_yet_read()
);
let _enter = span.enter();
if self.not_yet_formatted() {
tracing::trace!("formatting request");
let request = self.request_to_format();
let seq = mtry! {
request.borrow(|request| {
display.format_request(request, ctx)
})
};
self.formatted(seq);
}
if self.not_yet_sent() {
tracing::trace!("sending request");
let request = self.request_to_send();
mtry! {
request.borrow(|request| {
display.try_send_request_raw(request, ctx)
})
};
self.sent();
}
if self.not_yet_read() {
tracing::trace!("receiving reply");
match display.try_wait_for_reply_raw(self.sequence(), ctx) {
Ok(AsyncStatus::Ready(t)) => self.read_reply(t)?,
Ok(status) => return Ok(status.map(|_| unreachable!())),
Err(e) if e.is_protocol_error() => {
self.on_x11_error();
}
Err(e) => return Err(e),
}
}
Ok(AsyncStatus::Ready(self.get_if_resolved().unwrap()))
}
}
}
impl PrefetchTarget for EnableRequest {
type Target = Option<usize>;
fn map_reply(reply: Self::Reply) -> Self::Target {
let maxlen = reply.maximum_request_length as usize;
Some(maxlen)
}
fn on_x11_error() -> Self::Target {
None
}
}
impl<'a> PrefetchTarget for QueryExtensionRequest<'a> {
type Target = Option<ExtensionInformation>;
fn map_reply(reply: Self::Reply) -> Self::Target {
if !reply.present {
return None;
}
let info = ExtensionInformation {
major_opcode: reply.major_opcode,
first_error: reply.first_error,
first_event: reply.first_event,
};
Some(info)
}
fn on_x11_error() -> Self::Target {
None
}
}
impl PrefetchTarget for GetInputFocusRequest {
type Target = GetInputFocusReply;
fn map_reply(reply: Self::Reply) -> Self::Target {
reply
}
fn on_x11_error() -> Self::Target {
tracing::error!("synchronization should never error out");
GetInputFocusReply::default()
}
}
impl PrefetchTarget for GetXIDRangeRequest {
type Target = GetXIDRangeReply;
fn map_reply(reply: Self::Reply) -> Self::Target {
reply
}
fn on_x11_error() -> Self::Target {
tracing::error!("XID refresh should never error out");
GetXIDRangeReply::default()
}
}