use crate::platform::prelude::*;
use crate::platform::x11::handler::Handler;
use crate::platform::x11::sizing::SizingState;
use crate::platform::x11::window_shared::WindowShared;
use crate::platform::x11::window_thread::RedrawRequested;
use crate::DamageArea;
use calloop::timer::{TimeoutAction, Timer};
use calloop::LoopHandle;
use dpi::{PhysicalPosition, PhysicalSize};
use std::cell::Cell;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Mutex;
use std::time::{Duration, Instant};
use x11rb::connection::Connection;
use x11rb::protocol::present::{CompleteKind, CompleteNotifyEvent};
use x11rb::protocol::xproto::ExposeEvent;
pub struct PresentStateShared {
present_notify_requested: Cell<bool>,
poll_requested: Cell<bool>,
}
impl PresentStateShared {
pub(crate) fn request_present_notify_after(
&self, duration: Duration, loop_handle: &LoopHandle<EventLoop>,
) {
if duration.is_zero() || duration.as_millis() < 1 {
self.request_present_notify();
return;
}
let result = loop_handle.insert_source(Timer::from_duration(duration), |_, _, e| {
e.shared().present_state.request_present_notify();
TimeoutAction::Drop
});
if let Err(e) = result {
warn!("{}", e);
self.request_present_notify();
}
}
}
impl PresentStateShared {
pub fn new() -> Self {
Self { present_notify_requested: false.into(), poll_requested: false.into() }
}
pub fn request_present_notify(&self) {
self.present_notify_requested.set(true)
}
}
pub struct PresentState {
draw_now: bool,
last_requested_serial: Option<u32>,
last_received_present: Option<(u32, u64)>,
}
impl PresentState {
pub(crate) fn handle_requests(
&mut self, shared: &WindowShared, handler: &Handler, loop_handle: &LoopHandle<EventLoop>,
) -> Result<(), FatalError> {
let shared_state = &shared.present_state;
let thread_state = &shared.main_thread_shared.present;
if let Some(redraw_after) = thread_state.take_redraw_request() {
shared.request_redraw_after(redraw_after)
}
if self.draw_now {
let _ = thread_state.take_poll_request();
handler.poll();
shared.present_state.present_notify_requested.set(false);
handler.draw()?;
shared_state.poll_requested.set(false);
self.draw_now = false;
shared.connection.conn.flush()?;
} else if thread_state.take_poll_request() || shared_state.poll_requested.take() {
handler.poll();
}
self.handle_present_notify(&shared.present_state, &shared.xcb_window, loop_handle)?;
Ok(())
}
}
impl PresentState {
pub(crate) fn handle_expose_event(
&self, e: ExposeEvent, handler: &Handler, shared: &WindowShared, sizing_state: &SizingState,
) {
if e.count == 0 {
shared.present_state.present_notify_requested.set(true);
}
let current_window_size = sizing_state.non_coalesced_current_size(shared);
let damage_rect = DamageRect::new(&e);
let area = if damage_rect.fully_covers(current_window_size) {
DamageArea::FullWindow
} else {
DamageArea::Rect(damage_rect.into())
};
handler.damage(area);
}
}
impl PresentState {
pub fn handle_window_mapped(
&self, shared: &PresentStateShared, window: &XcbWindow, loop_handle: &LoopHandle<EventLoop>,
) -> Result<(), FatalError> {
if window.present_supported() && window.present_select_input()? {
shared.present_notify_requested.set(true);
} else {
Self::setup_fallback_frame_timer(loop_handle)?;
}
Ok(())
}
}
impl PresentState {
pub fn new() -> Self {
Self { draw_now: false, last_requested_serial: None, last_received_present: None }
}
pub fn handle_present_complete_notify(&mut self, e: CompleteNotifyEvent) {
if e.kind != CompleteKind::NOTIFY_MSC {
return;
}
let Some(last_requested_serial) = self.last_requested_serial else { return };
if last_requested_serial != e.serial {
return;
}
if let Some((last_received_serial, last_received_msc)) = self.last_received_present {
if last_received_serial == e.serial {
return;
}
if e.msc <= last_received_msc {
self.last_received_present = Some((e.serial, e.msc));
return;
}
}
self.last_received_present = Some((e.serial, e.msc));
self.draw_now = true;
}
fn setup_fallback_frame_timer(
loop_handle: &LoopHandle<EventLoop>,
) -> Result<(), calloop::Error> {
const FRAME_INTERVAL: Duration = Duration::from_millis(15);
fn handle_frame(evloop: &mut EventLoop, previous_deadline: Instant) -> TimeoutAction {
evloop.present_state.draw_now = true;
let now = Instant::now();
let Some(next_deadline) = previous_deadline.checked_add(FRAME_INTERVAL) else {
return TimeoutAction::ToDuration(FRAME_INTERVAL);
};
if next_deadline >= now {
return TimeoutAction::ToDuration(FRAME_INTERVAL);
}
TimeoutAction::ToInstant(next_deadline)
}
loop_handle
.insert_source(Timer::from_duration(FRAME_INTERVAL), |i, _, e| handle_frame(e, i))
.map_err(|e| e.error)?;
Ok(())
}
pub fn handle_present_notify(
&mut self, shared: &PresentStateShared, window: &XcbWindow,
loop_handle: &LoopHandle<EventLoop>,
) -> Result<(), FatalError> {
if !shared.present_notify_requested.get() {
return Ok(());
}
if !window.present_supported() {
shared.present_notify_requested.set(false);
return Ok(());
}
let (next_serial, target_msc) =
match (self.last_requested_serial, self.last_received_present) {
(None, None) => (0, 0),
(Some(sent_serial), Some((received_serial, last_msc)))
if sent_serial == received_serial =>
{
(sent_serial.wrapping_add(1), last_msc.wrapping_add(2))
}
_ => {
shared.present_notify_requested.set(false);
return Ok(());
}
};
if window.present_notify(target_msc, next_serial)?.check_is_ok() {
self.last_requested_serial = Some(next_serial);
} else {
self.last_requested_serial = None;
Self::setup_fallback_frame_timer(loop_handle)?;
}
shared.present_notify_requested.set(false);
Ok(())
}
}
pub struct PresentThreadShared {
poll_requested: AtomicBool,
redraw_requested_after: Mutex<Option<RedrawRequested>>,
}
impl PresentThreadShared {
pub(crate) fn new() -> Self {
Self { poll_requested: false.into(), redraw_requested_after: None.into() }
}
pub fn request_redraw_after(&self, duration: Duration) {
let mut guard = self.redraw_requested_after.lock().unwrap_or_else(|g| g.into_inner());
*guard = Some(RedrawRequested::from_duration(duration));
}
fn take_redraw_request(&self) -> Option<Duration> {
let mut guard = self.redraw_requested_after.lock().unwrap_or_else(|g| g.into_inner());
guard.take().map(|w| w.to_duration())
}
pub fn request_poll(&self) {
self.poll_requested.store(true, Ordering::Relaxed);
}
fn take_poll_request(&self) -> bool {
self.poll_requested.swap(false, Ordering::Relaxed)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct DamageRect {
pos: PhysicalPosition<u16>,
size: PhysicalSize<u16>,
}
impl DamageRect {
#[inline]
pub fn new(event: &ExposeEvent) -> Self {
Self {
pos: PhysicalPosition::new(event.x, event.y),
size: PhysicalSize::new(event.width, event.height),
}
}
#[inline]
pub fn position(&self) -> PhysicalPosition<u32> {
PhysicalPosition { x: self.pos.x.into(), y: self.pos.y.into() }
}
#[inline]
pub fn size(&self) -> PhysicalSize<u32> {
PhysicalSize { height: self.size.height.into(), width: self.size.width.into() }
}
pub fn fully_covers(&self, window_size: PhysicalSize<u16>) -> bool {
self.pos.x == 0
&& self.pos.y == 0
&& window_size.width <= self.size.width
&& window_size.height <= self.size.height
}
}