2022-12-21 17:26:09 -08:00
|
|
|
//! Implementation of software buffering for X11.
|
2022-12-22 10:09:47 -08:00
|
|
|
//!
|
2022-12-21 17:26:09 -08:00
|
|
|
//! This module converts the input buffer into an XImage and then sends it over the wire to be
|
2023-01-06 03:27:54 +00:00
|
|
|
//! drawn by the X server. The SHM extension is used if available.
|
|
|
|
|
|
|
|
|
|
#![allow(clippy::uninlined_format_args)]
|
2022-12-21 17:26:09 -08:00
|
|
|
|
2023-10-26 19:15:51 -07:00
|
|
|
use crate::error::{InitError, SwResultExt};
|
2023-04-21 10:37:48 -07:00
|
|
|
use crate::{Rect, SoftBufferError};
|
2023-10-26 19:15:51 -07:00
|
|
|
use raw_window_handle::{
|
|
|
|
|
HasDisplayHandle, HasWindowHandle, RawDisplayHandle, RawWindowHandle, XcbDisplayHandle,
|
|
|
|
|
XcbWindowHandle,
|
|
|
|
|
};
|
2023-10-31 07:09:50 -07:00
|
|
|
use rustix::{
|
|
|
|
|
fd::{AsFd, BorrowedFd, OwnedFd},
|
|
|
|
|
mm, shm as posix_shm,
|
|
|
|
|
};
|
2023-10-26 19:15:51 -07:00
|
|
|
|
2023-05-30 15:17:10 -07:00
|
|
|
use std::{
|
2023-12-11 06:54:10 +01:00
|
|
|
collections::HashSet,
|
2023-10-17 19:15:15 -07:00
|
|
|
fmt,
|
|
|
|
|
fs::File,
|
|
|
|
|
io, mem,
|
2023-05-30 15:17:10 -07:00
|
|
|
num::{NonZeroU16, NonZeroU32},
|
2023-10-17 19:15:15 -07:00
|
|
|
ptr::{null_mut, NonNull},
|
2023-05-30 15:17:10 -07:00
|
|
|
rc::Rc,
|
|
|
|
|
slice,
|
|
|
|
|
};
|
2022-01-15 08:17:17 -06:00
|
|
|
|
2023-07-02 15:08:01 -07:00
|
|
|
use as_raw_xcb_connection::AsRawXcbConnection;
|
2023-01-06 13:08:17 -08:00
|
|
|
use x11rb::connection::{Connection, SequenceNumber};
|
2023-01-06 03:27:54 +00:00
|
|
|
use x11rb::cookie::Cookie;
|
|
|
|
|
use x11rb::errors::{ConnectionError, ReplyError, ReplyOrIdError};
|
|
|
|
|
use x11rb::protocol::shm::{self, ConnectionExt as _};
|
2023-12-11 06:54:10 +01:00
|
|
|
use x11rb::protocol::xproto::{self, ConnectionExt as _, ImageOrder, VisualClass, Visualid};
|
2022-12-27 09:14:54 -08:00
|
|
|
use x11rb::xcb_ffi::XCBConnection;
|
2022-12-21 17:26:09 -08:00
|
|
|
|
2023-10-26 19:15:51 -07:00
|
|
|
pub struct X11DisplayImpl<D: ?Sized> {
|
2022-12-27 09:14:54 -08:00
|
|
|
/// The handle to the XCB connection.
|
2023-10-26 19:15:51 -07:00
|
|
|
connection: Option<XCBConnection>,
|
2022-12-21 17:26:09 -08:00
|
|
|
|
2023-01-06 13:08:17 -08:00
|
|
|
/// SHM extension is available.
|
|
|
|
|
is_shm_available: bool,
|
|
|
|
|
|
2023-12-11 06:54:10 +01:00
|
|
|
/// All visuals using softbuffer's pixel representation
|
|
|
|
|
supported_visuals: HashSet<Visualid>,
|
|
|
|
|
|
2023-10-26 19:15:51 -07:00
|
|
|
/// The generic display where the `connection` field comes from.
|
|
|
|
|
///
|
|
|
|
|
/// Without `&mut`, the underlying connection cannot be closed without other unsafe behavior.
|
|
|
|
|
/// With `&mut`, the connection can be dropped without us knowing about it. Therefore, we
|
|
|
|
|
/// cannot provide `&mut` access to this field.
|
|
|
|
|
_display: D,
|
|
|
|
|
}
|
2023-01-06 13:08:17 -08:00
|
|
|
|
2023-10-26 19:15:51 -07:00
|
|
|
impl<D: HasDisplayHandle + ?Sized> X11DisplayImpl<D> {
|
|
|
|
|
/// Create a new `X11DisplayImpl`.
|
|
|
|
|
pub(crate) fn new(display: D) -> Result<Self, InitError<D>>
|
|
|
|
|
where
|
|
|
|
|
D: Sized,
|
|
|
|
|
{
|
|
|
|
|
// Get the underlying libxcb handle.
|
|
|
|
|
let raw = display.display_handle()?.as_raw();
|
|
|
|
|
let xcb_handle = match raw {
|
|
|
|
|
RawDisplayHandle::Xcb(xcb_handle) => xcb_handle,
|
|
|
|
|
RawDisplayHandle::Xlib(xlib) => {
|
|
|
|
|
// Convert to an XCB handle.
|
2023-11-01 22:26:10 -07:00
|
|
|
let connection = xlib.display.map(|display| {
|
|
|
|
|
// Get the underlying XCB connection.
|
|
|
|
|
// SAFETY: The user has asserted that the display handle is valid.
|
|
|
|
|
unsafe {
|
|
|
|
|
let display = tiny_xlib::Display::from_ptr(display.as_ptr());
|
|
|
|
|
NonNull::new_unchecked(display.as_raw_xcb_connection()).cast()
|
|
|
|
|
}
|
|
|
|
|
});
|
2023-10-26 19:15:51 -07:00
|
|
|
|
|
|
|
|
// Construct the equivalent XCB display and window handles.
|
2023-11-01 22:26:10 -07:00
|
|
|
XcbDisplayHandle::new(connection, xlib.screen)
|
2023-10-26 19:15:51 -07:00
|
|
|
}
|
|
|
|
|
_ => return Err(InitError::Unsupported(display)),
|
2023-07-02 15:08:01 -07:00
|
|
|
};
|
2023-01-06 13:08:17 -08:00
|
|
|
|
2023-10-26 19:15:51 -07:00
|
|
|
// Validate the display handle to ensure we can use it.
|
|
|
|
|
let connection = match xcb_handle.connection {
|
2023-11-01 22:26:10 -07:00
|
|
|
Some(connection) => {
|
|
|
|
|
// Wrap the display handle in an x11rb connection.
|
|
|
|
|
// SAFETY: We don't own the connection, so don't drop it. We also assert that the connection is valid.
|
|
|
|
|
let result =
|
|
|
|
|
unsafe { XCBConnection::from_raw_xcb_connection(connection.as_ptr(), false) };
|
2023-01-06 13:08:17 -08:00
|
|
|
|
2023-11-01 22:26:10 -07:00
|
|
|
result.swbuf_err("Failed to wrap XCB connection")?
|
|
|
|
|
}
|
|
|
|
|
None => {
|
|
|
|
|
// The user didn't provide an XCB connection, so create our own.
|
|
|
|
|
log::info!("no XCB connection provided by the user, so spawning our own");
|
|
|
|
|
XCBConnection::connect(None)
|
|
|
|
|
.swbuf_err("Failed to spawn XCB connection")?
|
|
|
|
|
.0
|
|
|
|
|
}
|
2023-01-06 13:08:17 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let is_shm_available = is_shm_available(&connection);
|
2023-04-06 00:30:59 -07:00
|
|
|
if !is_shm_available {
|
|
|
|
|
log::warn!("SHM extension is not available. Performance may be poor.");
|
|
|
|
|
}
|
2023-01-06 13:08:17 -08:00
|
|
|
|
2023-12-11 06:54:10 +01:00
|
|
|
let supported_visuals = supported_visuals(&connection);
|
|
|
|
|
|
2023-01-06 13:08:17 -08:00
|
|
|
Ok(Self {
|
2023-10-26 19:15:51 -07:00
|
|
|
connection: Some(connection),
|
2023-01-06 13:08:17 -08:00
|
|
|
is_shm_available,
|
2023-12-11 06:54:10 +01:00
|
|
|
supported_visuals,
|
2023-10-26 19:15:51 -07:00
|
|
|
_display: display,
|
2023-01-06 13:08:17 -08:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-26 19:15:51 -07:00
|
|
|
impl<D: ?Sized> X11DisplayImpl<D> {
|
|
|
|
|
fn connection(&self) -> &XCBConnection {
|
|
|
|
|
self.connection
|
|
|
|
|
.as_ref()
|
|
|
|
|
.expect("X11DisplayImpl::connection() called after X11DisplayImpl::drop()")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-06 13:08:17 -08:00
|
|
|
/// The handle to an X11 drawing context.
|
2023-10-26 19:15:51 -07:00
|
|
|
pub struct X11Impl<D: ?Sized, W: ?Sized> {
|
2023-01-06 13:08:17 -08:00
|
|
|
/// X display this window belongs to.
|
2023-10-26 19:15:51 -07:00
|
|
|
display: Rc<X11DisplayImpl<D>>,
|
2023-01-06 13:08:17 -08:00
|
|
|
|
2022-12-27 09:14:54 -08:00
|
|
|
/// The window to draw to.
|
2023-01-06 03:27:54 +00:00
|
|
|
window: xproto::Window,
|
2022-12-21 17:26:09 -08:00
|
|
|
|
2022-12-27 09:14:54 -08:00
|
|
|
/// The graphics context to use when drawing.
|
2023-01-06 03:27:54 +00:00
|
|
|
gc: xproto::Gcontext,
|
2022-12-21 17:26:09 -08:00
|
|
|
|
|
|
|
|
/// The depth (bits per pixel) of the drawing context.
|
2022-12-27 09:14:54 -08:00
|
|
|
depth: u8,
|
2023-01-06 03:27:54 +00:00
|
|
|
|
2023-06-01 20:09:30 -07:00
|
|
|
/// The visual ID of the drawing context.
|
|
|
|
|
visual_id: u32,
|
|
|
|
|
|
2023-04-06 00:30:59 -07:00
|
|
|
/// The buffer we draw to.
|
|
|
|
|
buffer: Buffer,
|
|
|
|
|
|
2023-04-20 18:52:34 -07:00
|
|
|
/// Buffer has been presented.
|
|
|
|
|
buffer_presented: bool,
|
|
|
|
|
|
2023-05-30 15:17:10 -07:00
|
|
|
/// The current buffer width/height.
|
|
|
|
|
size: Option<(NonZeroU16, NonZeroU16)>,
|
2023-10-26 19:15:51 -07:00
|
|
|
|
|
|
|
|
/// Keep the window alive.
|
|
|
|
|
_window_handle: W,
|
2023-01-06 03:27:54 +00:00
|
|
|
}
|
|
|
|
|
|
2023-04-06 00:30:59 -07:00
|
|
|
/// The buffer that is being drawn to.
|
|
|
|
|
enum Buffer {
|
|
|
|
|
/// A buffer implemented using shared memory to prevent unnecessary copying.
|
|
|
|
|
Shm(ShmBuffer),
|
|
|
|
|
|
|
|
|
|
/// A normal buffer that we send over the wire.
|
|
|
|
|
Wire(Vec<u32>),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct ShmBuffer {
|
2023-01-06 03:27:54 +00:00
|
|
|
/// The shared memory segment, paired with its ID.
|
|
|
|
|
seg: Option<(ShmSegment, shm::Seg)>,
|
|
|
|
|
|
|
|
|
|
/// A cookie indicating that the shared memory segment is ready to be used.
|
|
|
|
|
///
|
|
|
|
|
/// We can't soundly read from or write to the SHM segment until the X server is done processing the
|
|
|
|
|
/// `shm::PutImage` request. However, the X server handles requests in order, which means that, if
|
|
|
|
|
/// we send a very small request after the `shm::PutImage` request, then the X server will have to
|
|
|
|
|
/// process that request before it can process the `shm::PutImage` request. Therefore, we can use
|
|
|
|
|
/// the reply to that small request to determine when the `shm::PutImage` request is done.
|
|
|
|
|
///
|
|
|
|
|
/// In this case, we use `GetInputFocus` since it is a very small request.
|
|
|
|
|
///
|
|
|
|
|
/// We store the sequence number instead of the `Cookie` since we cannot hold a self-referential
|
|
|
|
|
/// reference to the `connection` field.
|
|
|
|
|
done_processing: Option<SequenceNumber>,
|
2022-01-15 08:17:17 -06:00
|
|
|
}
|
|
|
|
|
|
2023-10-26 19:15:51 -07:00
|
|
|
impl<D: HasDisplayHandle + ?Sized, W: HasWindowHandle> X11Impl<D, W> {
|
|
|
|
|
/// Create a new `X11Impl` from a `HasWindowHandle`.
|
|
|
|
|
pub(crate) fn new(window_src: W, display: Rc<X11DisplayImpl<D>>) -> Result<Self, InitError<W>> {
|
|
|
|
|
// Get the underlying raw window handle.
|
|
|
|
|
let raw = window_src.window_handle()?.as_raw();
|
|
|
|
|
let window_handle = match raw {
|
|
|
|
|
RawWindowHandle::Xcb(xcb) => xcb,
|
|
|
|
|
RawWindowHandle::Xlib(xlib) => {
|
|
|
|
|
let window = match NonZeroU32::new(xlib.window as u32) {
|
|
|
|
|
Some(window) => window,
|
|
|
|
|
None => return Err(SoftBufferError::IncompleteWindowHandle.into()),
|
|
|
|
|
};
|
|
|
|
|
let mut xcb_window_handle = XcbWindowHandle::new(window);
|
|
|
|
|
xcb_window_handle.visual_id = NonZeroU32::new(xlib.visual_id as u32);
|
|
|
|
|
xcb_window_handle
|
|
|
|
|
}
|
|
|
|
|
_ => {
|
|
|
|
|
return Err(InitError::Unsupported(window_src));
|
|
|
|
|
}
|
|
|
|
|
};
|
2022-12-21 17:26:09 -08:00
|
|
|
|
2023-10-26 19:15:51 -07:00
|
|
|
log::trace!("new: window_handle={:X}", window_handle.window);
|
|
|
|
|
let window = window_handle.window.get();
|
2022-12-27 09:14:54 -08:00
|
|
|
|
2023-06-01 20:09:30 -07:00
|
|
|
// Run in parallel: start getting the window depth and (if necessary) visual.
|
|
|
|
|
let display2 = display.clone();
|
|
|
|
|
let tokens = {
|
|
|
|
|
let geometry_token = display2
|
2023-10-26 19:15:51 -07:00
|
|
|
.connection()
|
2023-06-01 20:09:30 -07:00
|
|
|
.get_geometry(window)
|
|
|
|
|
.swbuf_err("Failed to send geometry request")?;
|
2023-10-26 19:15:51 -07:00
|
|
|
let window_attrs_token = if window_handle.visual_id.is_none() {
|
2023-06-01 20:09:30 -07:00
|
|
|
Some(
|
|
|
|
|
display2
|
2023-10-26 19:15:51 -07:00
|
|
|
.connection()
|
2023-06-01 20:09:30 -07:00
|
|
|
.get_window_attributes(window)
|
|
|
|
|
.swbuf_err("Failed to send window attributes request")?,
|
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
(geometry_token, window_attrs_token)
|
|
|
|
|
};
|
2022-12-27 09:14:54 -08:00
|
|
|
|
|
|
|
|
// Create a new graphics context to draw to.
|
2023-01-06 13:08:17 -08:00
|
|
|
let gc = display
|
2023-10-26 19:15:51 -07:00
|
|
|
.connection()
|
2022-12-27 09:14:54 -08:00
|
|
|
.generate_id()
|
|
|
|
|
.swbuf_err("Failed to generate GC ID")?;
|
2023-01-06 13:08:17 -08:00
|
|
|
display
|
2023-10-26 19:15:51 -07:00
|
|
|
.connection()
|
2022-12-27 09:14:54 -08:00
|
|
|
.create_gc(
|
|
|
|
|
gc,
|
|
|
|
|
window,
|
|
|
|
|
&xproto::CreateGCAux::new().graphics_exposures(0),
|
|
|
|
|
)
|
|
|
|
|
.swbuf_err("Failed to send GC creation request")?
|
|
|
|
|
.check()
|
|
|
|
|
.swbuf_err("Failed to create GC")?;
|
|
|
|
|
|
|
|
|
|
// Finish getting the depth of the window.
|
2023-06-01 20:09:30 -07:00
|
|
|
let (geometry_reply, visual_id) = {
|
|
|
|
|
let (geometry_token, window_attrs_token) = tokens;
|
|
|
|
|
let geometry_reply = geometry_token
|
|
|
|
|
.reply()
|
|
|
|
|
.swbuf_err("Failed to get geometry reply")?;
|
|
|
|
|
let visual_id = match window_attrs_token {
|
2023-10-26 19:15:51 -07:00
|
|
|
None => window_handle.visual_id.unwrap().get(),
|
2023-06-01 20:09:30 -07:00
|
|
|
Some(window_attrs) => {
|
|
|
|
|
window_attrs
|
|
|
|
|
.reply()
|
|
|
|
|
.swbuf_err("Failed to get window attributes reply")?
|
|
|
|
|
.visual
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
(geometry_reply, visual_id)
|
|
|
|
|
};
|
2022-01-15 08:17:17 -06:00
|
|
|
|
2023-12-11 06:54:10 +01:00
|
|
|
if !display.supported_visuals.contains(&visual_id) {
|
|
|
|
|
return Err(SoftBufferError::PlatformError(
|
|
|
|
|
Some(format!(
|
|
|
|
|
"Visual 0x{visual_id:x} does not use softbuffer's pixel format and is unsupported"
|
|
|
|
|
)),
|
|
|
|
|
None,
|
|
|
|
|
)
|
|
|
|
|
.into());
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-06 03:27:54 +00:00
|
|
|
// See if SHM is available.
|
2023-04-06 00:30:59 -07:00
|
|
|
let buffer = if display.is_shm_available {
|
|
|
|
|
// SHM is available.
|
|
|
|
|
Buffer::Shm(ShmBuffer {
|
|
|
|
|
seg: None,
|
|
|
|
|
done_processing: None,
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
// SHM is not available.
|
|
|
|
|
Buffer::Wire(Vec::new())
|
2023-01-06 03:27:54 +00:00
|
|
|
};
|
|
|
|
|
|
2022-12-21 17:26:09 -08:00
|
|
|
Ok(Self {
|
2023-01-06 13:08:17 -08:00
|
|
|
display,
|
2022-12-27 09:14:54 -08:00
|
|
|
window,
|
2022-12-21 17:26:09 -08:00
|
|
|
gc,
|
2022-12-27 09:14:54 -08:00
|
|
|
depth: geometry_reply.depth,
|
2023-06-01 20:09:30 -07:00
|
|
|
visual_id,
|
2023-04-06 00:30:59 -07:00
|
|
|
buffer,
|
2023-04-20 18:52:34 -07:00
|
|
|
buffer_presented: false,
|
2023-05-30 15:17:10 -07:00
|
|
|
size: None,
|
2023-10-26 19:15:51 -07:00
|
|
|
_window_handle: window_src,
|
2022-12-21 17:26:09 -08:00
|
|
|
})
|
2022-01-15 08:17:17 -06:00
|
|
|
}
|
|
|
|
|
|
2023-04-06 00:30:59 -07:00
|
|
|
/// Resize the internal buffer to the given width and height.
|
|
|
|
|
pub(crate) fn resize(
|
|
|
|
|
&mut self,
|
|
|
|
|
width: NonZeroU32,
|
|
|
|
|
height: NonZeroU32,
|
|
|
|
|
) -> Result<(), SoftBufferError> {
|
|
|
|
|
log::trace!(
|
|
|
|
|
"resize: window={:X}, size={}x{}",
|
|
|
|
|
self.window,
|
|
|
|
|
width,
|
|
|
|
|
height
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Width and height should fit in u16.
|
2023-05-30 15:17:10 -07:00
|
|
|
let width: NonZeroU16 = width
|
2023-04-06 00:30:59 -07:00
|
|
|
.try_into()
|
|
|
|
|
.or(Err(SoftBufferError::SizeOutOfRange { width, height }))?;
|
2023-05-30 15:17:10 -07:00
|
|
|
let height: NonZeroU16 = height.try_into().or(Err(SoftBufferError::SizeOutOfRange {
|
|
|
|
|
width: width.into(),
|
|
|
|
|
height,
|
|
|
|
|
}))?;
|
2023-01-06 03:27:54 +00:00
|
|
|
|
2023-05-30 15:17:10 -07:00
|
|
|
if self.size != Some((width, height)) {
|
2023-04-20 18:52:34 -07:00
|
|
|
self.buffer_presented = false;
|
2023-04-06 00:30:59 -07:00
|
|
|
self.buffer
|
2023-10-26 19:15:51 -07:00
|
|
|
.resize(self.display.connection(), width.get(), height.get())
|
2023-04-06 00:30:59 -07:00
|
|
|
.swbuf_err("Failed to resize X11 buffer")?;
|
|
|
|
|
|
|
|
|
|
// We successfully resized the buffer.
|
2023-05-30 15:17:10 -07:00
|
|
|
self.size = Some((width, height));
|
2023-01-06 03:27:54 +00:00
|
|
|
}
|
2023-04-06 00:30:59 -07:00
|
|
|
|
|
|
|
|
Ok(())
|
2023-01-06 03:27:54 +00:00
|
|
|
}
|
|
|
|
|
|
2023-04-06 00:30:59 -07:00
|
|
|
/// Get a mutable reference to the buffer.
|
2023-10-26 19:15:51 -07:00
|
|
|
pub(crate) fn buffer_mut(&mut self) -> Result<BufferImpl<'_, D, W>, SoftBufferError> {
|
2023-04-06 00:30:59 -07:00
|
|
|
log::trace!("buffer_mut: window={:X}", self.window);
|
2023-01-06 03:27:54 +00:00
|
|
|
|
2023-04-07 11:54:50 -07:00
|
|
|
// Finish waiting on the previous `shm::PutImage` request, if any.
|
2023-10-26 19:15:51 -07:00
|
|
|
self.buffer.finish_wait(self.display.connection())?;
|
2023-01-06 03:27:54 +00:00
|
|
|
|
2023-04-07 11:54:50 -07:00
|
|
|
// We can now safely call `buffer_mut` on the buffer.
|
|
|
|
|
Ok(BufferImpl(self))
|
2023-04-06 00:30:59 -07:00
|
|
|
}
|
2023-06-01 20:09:30 -07:00
|
|
|
|
|
|
|
|
/// Fetch the buffer from the window.
|
|
|
|
|
pub fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
|
|
|
|
|
log::trace!("fetch: window={:X}", self.window);
|
|
|
|
|
|
2023-06-02 15:03:10 -07:00
|
|
|
let (width, height) = self
|
|
|
|
|
.size
|
|
|
|
|
.expect("Must set size of surface before calling `fetch()`");
|
|
|
|
|
|
2023-06-01 20:09:30 -07:00
|
|
|
// TODO: Is it worth it to do SHM here? Probably not.
|
|
|
|
|
let reply = self
|
|
|
|
|
.display
|
2023-10-26 19:15:51 -07:00
|
|
|
.connection()
|
2023-06-01 20:09:30 -07:00
|
|
|
.get_image(
|
|
|
|
|
xproto::ImageFormat::Z_PIXMAP,
|
|
|
|
|
self.window,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
2023-06-02 15:03:10 -07:00
|
|
|
width.get(),
|
|
|
|
|
height.get(),
|
2023-06-01 20:09:30 -07:00
|
|
|
u32::MAX,
|
|
|
|
|
)
|
|
|
|
|
.swbuf_err("Failed to send image fetching request")?
|
|
|
|
|
.reply()
|
|
|
|
|
.swbuf_err("Failed to fetch image from window")?;
|
|
|
|
|
|
|
|
|
|
if reply.depth == self.depth && reply.visual == self.visual_id {
|
|
|
|
|
let mut out = vec![0u32; reply.data.len() / 4];
|
|
|
|
|
bytemuck::cast_slice_mut::<u32, u8>(&mut out).copy_from_slice(&reply.data);
|
|
|
|
|
Ok(out)
|
|
|
|
|
} else {
|
|
|
|
|
Err(SoftBufferError::PlatformError(
|
|
|
|
|
Some("Mismatch between reply and window data".into()),
|
|
|
|
|
None,
|
|
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-04-06 00:30:59 -07:00
|
|
|
}
|
2022-12-27 09:14:54 -08:00
|
|
|
|
2023-10-26 19:15:51 -07:00
|
|
|
pub struct BufferImpl<'a, D: ?Sized, W: ?Sized>(&'a mut X11Impl<D, W>);
|
2023-01-06 03:27:54 +00:00
|
|
|
|
2023-10-26 19:15:51 -07:00
|
|
|
impl<'a, D: HasDisplayHandle + ?Sized, W: HasWindowHandle + ?Sized> BufferImpl<'a, D, W> {
|
2023-04-06 00:30:59 -07:00
|
|
|
#[inline]
|
|
|
|
|
pub fn pixels(&self) -> &[u32] {
|
2023-04-07 11:54:50 -07:00
|
|
|
// SAFETY: We called `finish_wait` on the buffer, so it is safe to call `buffer()`.
|
|
|
|
|
unsafe { self.0.buffer.buffer() }
|
2023-04-06 00:30:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn pixels_mut(&mut self) -> &mut [u32] {
|
2023-04-07 11:54:50 -07:00
|
|
|
// SAFETY: We called `finish_wait` on the buffer, so it is safe to call `buffer_mut`.
|
|
|
|
|
unsafe { self.0.buffer.buffer_mut() }
|
2023-04-06 00:30:59 -07:00
|
|
|
}
|
|
|
|
|
|
2023-04-20 18:52:34 -07:00
|
|
|
pub fn age(&self) -> u8 {
|
|
|
|
|
if self.0.buffer_presented {
|
|
|
|
|
1
|
|
|
|
|
} else {
|
|
|
|
|
0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-06 00:30:59 -07:00
|
|
|
/// Push the buffer to the window.
|
2023-04-21 10:37:48 -07:00
|
|
|
pub fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError> {
|
2023-04-07 11:54:50 -07:00
|
|
|
let imp = self.0;
|
2023-04-06 00:30:59 -07:00
|
|
|
|
2023-05-30 15:17:10 -07:00
|
|
|
let (surface_width, surface_height) = imp
|
|
|
|
|
.size
|
|
|
|
|
.expect("Must set size of surface before calling `present_with_damage()`");
|
|
|
|
|
|
2023-04-06 00:30:59 -07:00
|
|
|
log::trace!("present: window={:X}", imp.window);
|
|
|
|
|
|
2023-04-20 18:52:34 -07:00
|
|
|
match imp.buffer {
|
2023-04-06 00:30:59 -07:00
|
|
|
Buffer::Wire(ref wire) => {
|
|
|
|
|
// This is a suboptimal strategy, raise a stink in the debug logs.
|
|
|
|
|
log::debug!("Falling back to non-SHM method for window drawing.");
|
|
|
|
|
|
|
|
|
|
imp.display
|
2023-10-26 19:15:51 -07:00
|
|
|
.connection()
|
2023-04-06 00:30:59 -07:00
|
|
|
.put_image(
|
|
|
|
|
xproto::ImageFormat::Z_PIXMAP,
|
|
|
|
|
imp.window,
|
|
|
|
|
imp.gc,
|
2023-05-30 15:17:10 -07:00
|
|
|
surface_width.get(),
|
|
|
|
|
surface_height.get(),
|
2023-04-06 00:30:59 -07:00
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
imp.depth,
|
|
|
|
|
bytemuck::cast_slice(wire),
|
|
|
|
|
)
|
|
|
|
|
.map(|c| c.ignore_error())
|
|
|
|
|
.push_err()
|
2023-05-30 15:17:10 -07:00
|
|
|
.swbuf_err("Failed to draw image to window")?;
|
2023-04-06 00:30:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Buffer::Shm(ref mut shm) => {
|
|
|
|
|
// If the X server is still processing the last image, wait for it to finish.
|
2023-04-07 11:54:50 -07:00
|
|
|
// SAFETY: We know that we called finish_wait() before this.
|
|
|
|
|
// Put the image into the window.
|
|
|
|
|
if let Some((_, segment_id)) = shm.seg {
|
2023-04-21 10:37:48 -07:00
|
|
|
damage
|
|
|
|
|
.iter()
|
2023-05-30 15:17:10 -07:00
|
|
|
.try_for_each(|rect| {
|
|
|
|
|
let (src_x, src_y, dst_x, dst_y, width, height) = (|| {
|
|
|
|
|
Some((
|
|
|
|
|
u16::try_from(rect.x).ok()?,
|
|
|
|
|
u16::try_from(rect.y).ok()?,
|
|
|
|
|
i16::try_from(rect.x).ok()?,
|
|
|
|
|
i16::try_from(rect.y).ok()?,
|
|
|
|
|
u16::try_from(rect.width.get()).ok()?,
|
|
|
|
|
u16::try_from(rect.height.get()).ok()?,
|
|
|
|
|
))
|
|
|
|
|
})(
|
|
|
|
|
)
|
|
|
|
|
.ok_or(SoftBufferError::DamageOutOfRange { rect: *rect })?;
|
|
|
|
|
imp.display
|
2023-10-26 19:15:51 -07:00
|
|
|
.connection()
|
2023-05-30 15:17:10 -07:00
|
|
|
.shm_put_image(
|
|
|
|
|
imp.window,
|
|
|
|
|
imp.gc,
|
|
|
|
|
surface_width.get(),
|
|
|
|
|
surface_height.get(),
|
|
|
|
|
src_x,
|
|
|
|
|
src_y,
|
|
|
|
|
width,
|
|
|
|
|
height,
|
|
|
|
|
dst_x,
|
|
|
|
|
dst_y,
|
|
|
|
|
imp.depth,
|
|
|
|
|
xproto::ImageFormat::Z_PIXMAP.into(),
|
|
|
|
|
false,
|
|
|
|
|
segment_id,
|
|
|
|
|
0,
|
|
|
|
|
)
|
|
|
|
|
.push_err()
|
|
|
|
|
.map(|c| c.ignore_error())
|
|
|
|
|
.swbuf_err("Failed to draw image to window")
|
|
|
|
|
})
|
2023-04-07 11:54:50 -07:00
|
|
|
.and_then(|()| {
|
|
|
|
|
// Send a short request to act as a notification for when the X server is done processing the image.
|
2023-10-26 19:15:51 -07:00
|
|
|
shm.begin_wait(imp.display.connection())
|
2023-05-30 15:17:10 -07:00
|
|
|
.swbuf_err("Failed to draw image to window")
|
|
|
|
|
})?;
|
2023-04-07 11:54:50 -07:00
|
|
|
}
|
2023-04-06 00:30:59 -07:00
|
|
|
}
|
2023-04-20 18:52:34 -07:00
|
|
|
}
|
2023-04-06 00:30:59 -07:00
|
|
|
|
2023-04-20 18:52:34 -07:00
|
|
|
imp.buffer_presented = true;
|
|
|
|
|
|
|
|
|
|
Ok(())
|
2023-01-06 03:27:54 +00:00
|
|
|
}
|
2023-04-21 10:37:48 -07:00
|
|
|
|
|
|
|
|
pub fn present(self) -> Result<(), SoftBufferError> {
|
2023-05-30 15:17:10 -07:00
|
|
|
let (width, height) = self
|
|
|
|
|
.0
|
|
|
|
|
.size
|
|
|
|
|
.expect("Must set size of surface before calling `present()`");
|
2023-04-21 10:37:48 -07:00
|
|
|
self.present_with_damage(&[Rect {
|
|
|
|
|
x: 0,
|
|
|
|
|
y: 0,
|
2023-05-30 15:17:10 -07:00
|
|
|
width: width.into(),
|
|
|
|
|
height: height.into(),
|
2023-04-21 10:37:48 -07:00
|
|
|
}])
|
|
|
|
|
}
|
2023-04-06 00:30:59 -07:00
|
|
|
}
|
2023-01-06 03:27:54 +00:00
|
|
|
|
2023-04-06 00:30:59 -07:00
|
|
|
impl Buffer {
|
|
|
|
|
/// Resize the buffer to the given size.
|
|
|
|
|
fn resize(
|
2023-01-06 03:27:54 +00:00
|
|
|
&mut self,
|
2023-04-06 00:30:59 -07:00
|
|
|
conn: &impl Connection,
|
2023-01-06 03:27:54 +00:00
|
|
|
width: u16,
|
|
|
|
|
height: u16,
|
|
|
|
|
) -> Result<(), PushBufferError> {
|
2023-04-06 00:30:59 -07:00
|
|
|
match self {
|
|
|
|
|
Buffer::Shm(ref mut shm) => shm.alloc_segment(conn, total_len(width, height)),
|
|
|
|
|
Buffer::Wire(wire) => {
|
2023-09-06 17:05:17 +02:00
|
|
|
wire.resize(total_len(width, height) / 4, 0);
|
2023-04-06 00:30:59 -07:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-01-06 03:27:54 +00:00
|
|
|
|
2023-04-07 11:54:50 -07:00
|
|
|
/// Finish waiting for an ongoing `shm::PutImage` request, if there is one.
|
|
|
|
|
fn finish_wait(&mut self, conn: &impl Connection) -> Result<(), SoftBufferError> {
|
|
|
|
|
if let Buffer::Shm(ref mut shm) = self {
|
|
|
|
|
shm.finish_wait(conn)
|
|
|
|
|
.swbuf_err("Failed to wait for X11 buffer")?;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Get a reference to the buffer.
|
|
|
|
|
///
|
|
|
|
|
/// # Safety
|
|
|
|
|
///
|
|
|
|
|
/// `finish_wait()` must be called in between `shm::PutImage` requests and this function.
|
|
|
|
|
#[inline]
|
|
|
|
|
unsafe fn buffer(&self) -> &[u32] {
|
|
|
|
|
match self {
|
|
|
|
|
Buffer::Shm(ref shm) => unsafe { shm.as_ref() },
|
|
|
|
|
Buffer::Wire(wire) => wire,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-06 00:30:59 -07:00
|
|
|
/// Get a mutable reference to the buffer.
|
2023-04-07 11:54:50 -07:00
|
|
|
///
|
|
|
|
|
/// # Safety
|
|
|
|
|
///
|
|
|
|
|
/// `finish_wait()` must be called in between `shm::PutImage` requests and this function.
|
|
|
|
|
#[inline]
|
|
|
|
|
unsafe fn buffer_mut(&mut self) -> &mut [u32] {
|
2023-04-06 00:30:59 -07:00
|
|
|
match self {
|
2023-04-07 11:54:50 -07:00
|
|
|
Buffer::Shm(ref mut shm) => unsafe { shm.as_mut() },
|
|
|
|
|
Buffer::Wire(wire) => wire,
|
2023-04-06 00:30:59 -07:00
|
|
|
}
|
2023-01-06 03:27:54 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-06 00:30:59 -07:00
|
|
|
impl ShmBuffer {
|
2023-01-06 03:27:54 +00:00
|
|
|
/// Allocate a new `ShmSegment` of the given size.
|
2023-04-06 00:30:59 -07:00
|
|
|
fn alloc_segment(
|
2023-01-06 03:27:54 +00:00
|
|
|
&mut self,
|
|
|
|
|
conn: &impl Connection,
|
2023-04-24 19:37:41 -03:00
|
|
|
buffer_size: usize,
|
2023-04-06 00:30:59 -07:00
|
|
|
) -> Result<(), PushBufferError> {
|
2023-01-06 03:27:54 +00:00
|
|
|
// Round the size up to the next power of two to prevent frequent reallocations.
|
2023-04-24 19:37:41 -03:00
|
|
|
let size = buffer_size.next_power_of_two();
|
2023-01-06 03:27:54 +00:00
|
|
|
|
|
|
|
|
// Get the size of the segment currently in use.
|
|
|
|
|
let needs_realloc = match self.seg {
|
|
|
|
|
Some((ref seg, _)) => seg.size() < size,
|
|
|
|
|
None => true,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Reallocate if necessary.
|
|
|
|
|
if needs_realloc {
|
2023-04-24 19:37:41 -03:00
|
|
|
let new_seg = ShmSegment::new(size, buffer_size)?;
|
2023-01-06 03:27:54 +00:00
|
|
|
self.associate(conn, new_seg)?;
|
2023-04-24 19:37:41 -03:00
|
|
|
} else if let Some((ref mut seg, _)) = self.seg {
|
|
|
|
|
seg.set_buffer_size(buffer_size);
|
2023-01-06 03:27:54 +00:00
|
|
|
}
|
|
|
|
|
|
2023-04-06 00:30:59 -07:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-07 11:54:50 -07:00
|
|
|
/// Get the SHM buffer as a reference.
|
|
|
|
|
///
|
|
|
|
|
/// # Safety
|
|
|
|
|
///
|
|
|
|
|
/// `finish_wait()` must be called before this function is.
|
|
|
|
|
#[inline]
|
|
|
|
|
unsafe fn as_ref(&self) -> &[u32] {
|
|
|
|
|
match self.seg.as_ref() {
|
|
|
|
|
Some((seg, _)) => {
|
2023-04-24 19:37:41 -03:00
|
|
|
let buffer_size = seg.buffer_size();
|
|
|
|
|
|
2023-04-07 11:54:50 -07:00
|
|
|
// SAFETY: No other code should be able to access the segment.
|
2023-04-24 19:37:41 -03:00
|
|
|
bytemuck::cast_slice(unsafe { &seg.as_ref()[..buffer_size] })
|
2023-04-07 11:54:50 -07:00
|
|
|
}
|
|
|
|
|
None => {
|
|
|
|
|
// Nothing has been allocated yet.
|
|
|
|
|
&[]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-04-06 00:30:59 -07:00
|
|
|
|
2023-04-07 11:54:50 -07:00
|
|
|
/// Get the SHM buffer as a mutable reference.
|
|
|
|
|
///
|
|
|
|
|
/// # Safety
|
|
|
|
|
///
|
|
|
|
|
/// `finish_wait()` must be called before this function is.
|
|
|
|
|
#[inline]
|
|
|
|
|
unsafe fn as_mut(&mut self) -> &mut [u32] {
|
2023-04-06 00:30:59 -07:00
|
|
|
match self.seg.as_mut() {
|
|
|
|
|
Some((seg, _)) => {
|
2023-04-24 19:37:41 -03:00
|
|
|
let buffer_size = seg.buffer_size();
|
|
|
|
|
|
2023-04-06 00:30:59 -07:00
|
|
|
// SAFETY: No other code should be able to access the segment.
|
2023-04-24 19:37:41 -03:00
|
|
|
bytemuck::cast_slice_mut(unsafe { &mut seg.as_mut()[..buffer_size] })
|
2023-04-06 00:30:59 -07:00
|
|
|
}
|
|
|
|
|
None => {
|
|
|
|
|
// Nothing has been allocated yet.
|
2023-04-07 11:54:50 -07:00
|
|
|
&mut []
|
2023-04-06 00:30:59 -07:00
|
|
|
}
|
|
|
|
|
}
|
2023-01-06 03:27:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Associate an SHM segment with the server.
|
|
|
|
|
fn associate(
|
|
|
|
|
&mut self,
|
|
|
|
|
conn: &impl Connection,
|
|
|
|
|
seg: ShmSegment,
|
|
|
|
|
) -> Result<(), PushBufferError> {
|
|
|
|
|
// Register the guard.
|
|
|
|
|
let new_id = conn.generate_id()?;
|
2023-10-31 07:09:50 -07:00
|
|
|
conn.shm_attach_fd(new_id, seg.as_fd().try_clone_to_owned().unwrap(), true)?
|
2023-10-17 19:15:15 -07:00
|
|
|
.ignore_error();
|
2023-01-06 03:27:54 +00:00
|
|
|
|
|
|
|
|
// Take out the old one and detach it.
|
|
|
|
|
if let Some((old_seg, old_id)) = self.seg.replace((seg, new_id)) {
|
2023-04-06 00:30:59 -07:00
|
|
|
// Wait for the old segment to finish processing.
|
|
|
|
|
self.finish_wait(conn)?;
|
|
|
|
|
|
2023-01-06 03:27:54 +00:00
|
|
|
conn.shm_detach(old_id)?.ignore_error();
|
|
|
|
|
|
|
|
|
|
// Drop the old segment.
|
|
|
|
|
drop(old_seg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Begin waiting for the SHM processing to finish.
|
|
|
|
|
fn begin_wait(&mut self, c: &impl Connection) -> Result<(), PushBufferError> {
|
|
|
|
|
let cookie = c.get_input_focus()?.sequence_number();
|
|
|
|
|
let old_cookie = self.done_processing.replace(cookie);
|
|
|
|
|
debug_assert!(old_cookie.is_none());
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Wait for the SHM processing to finish.
|
|
|
|
|
fn finish_wait(&mut self, c: &impl Connection) -> Result<(), PushBufferError> {
|
|
|
|
|
if let Some(done_processing) = self.done_processing.take() {
|
|
|
|
|
// Cast to a cookie and wait on it.
|
|
|
|
|
let cookie = Cookie::<_, xproto::GetInputFocusReply>::new(c, done_processing);
|
|
|
|
|
cookie.reply()?;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct ShmSegment {
|
2023-10-17 19:15:15 -07:00
|
|
|
id: File,
|
2023-01-06 03:27:54 +00:00
|
|
|
ptr: NonNull<i8>,
|
|
|
|
|
size: usize,
|
2023-04-24 19:37:41 -03:00
|
|
|
buffer_size: usize,
|
2023-01-06 03:27:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ShmSegment {
|
|
|
|
|
/// Create a new `ShmSegment` with the given size.
|
2023-04-24 19:37:41 -03:00
|
|
|
fn new(size: usize, buffer_size: usize) -> io::Result<Self> {
|
2023-04-24 20:31:39 -03:00
|
|
|
assert!(size >= buffer_size);
|
|
|
|
|
|
2023-10-17 19:15:15 -07:00
|
|
|
// Create a shared memory segment.
|
|
|
|
|
let id = File::from(create_shm_id()?);
|
2023-01-06 03:27:54 +00:00
|
|
|
|
2023-10-17 19:15:15 -07:00
|
|
|
// Set its length.
|
|
|
|
|
id.set_len(size as u64)?;
|
2023-01-06 03:27:54 +00:00
|
|
|
|
2023-10-17 19:15:15 -07:00
|
|
|
// Map the shared memory to our file descriptor space.
|
|
|
|
|
let ptr = unsafe {
|
|
|
|
|
let ptr = mm::mmap(
|
|
|
|
|
null_mut(),
|
2023-04-24 19:37:41 -03:00
|
|
|
size,
|
2023-10-17 19:15:15 -07:00
|
|
|
mm::ProtFlags::READ | mm::ProtFlags::WRITE,
|
|
|
|
|
mm::MapFlags::SHARED,
|
|
|
|
|
&id,
|
|
|
|
|
0,
|
|
|
|
|
)?;
|
|
|
|
|
|
|
|
|
|
match NonNull::new(ptr.cast()) {
|
|
|
|
|
Some(ptr) => ptr,
|
|
|
|
|
None => {
|
|
|
|
|
return Err(io::Error::new(
|
|
|
|
|
io::ErrorKind::Other,
|
|
|
|
|
"unexpected null when mapping SHM segment",
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Ok(Self {
|
|
|
|
|
id,
|
|
|
|
|
ptr,
|
|
|
|
|
size,
|
|
|
|
|
buffer_size,
|
|
|
|
|
})
|
2023-01-06 03:27:54 +00:00
|
|
|
}
|
|
|
|
|
|
2023-04-07 11:54:50 -07:00
|
|
|
/// Get this shared memory segment as a reference.
|
|
|
|
|
///
|
|
|
|
|
/// # Safety
|
|
|
|
|
///
|
|
|
|
|
/// One must ensure that no other processes are writing to this memory.
|
|
|
|
|
unsafe fn as_ref(&self) -> &[i8] {
|
|
|
|
|
unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.size) }
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-06 00:30:59 -07:00
|
|
|
/// Get this shared memory segment as a mutable reference.
|
2023-01-06 03:27:54 +00:00
|
|
|
///
|
|
|
|
|
/// # Safety
|
|
|
|
|
///
|
2023-04-06 00:30:59 -07:00
|
|
|
/// One must ensure that no other processes are reading from or writing to this memory.
|
|
|
|
|
unsafe fn as_mut(&mut self) -> &mut [i8] {
|
2023-04-07 11:54:50 -07:00
|
|
|
unsafe { slice::from_raw_parts_mut(self.ptr.as_ptr(), self.size) }
|
2023-01-06 03:27:54 +00:00
|
|
|
}
|
|
|
|
|
|
2023-04-24 19:37:41 -03:00
|
|
|
/// Set the size of the buffer for this shared memory segment.
|
|
|
|
|
fn set_buffer_size(&mut self, buffer_size: usize) {
|
2023-04-24 20:31:39 -03:00
|
|
|
assert!(self.size >= buffer_size);
|
2023-04-24 19:37:41 -03:00
|
|
|
self.buffer_size = buffer_size
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Get the size of the buffer for this shared memory segment.
|
|
|
|
|
fn buffer_size(&self) -> usize {
|
|
|
|
|
self.buffer_size
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-06 03:27:54 +00:00
|
|
|
/// Get the size of this shared memory segment.
|
|
|
|
|
fn size(&self) -> usize {
|
|
|
|
|
self.size
|
|
|
|
|
}
|
2023-10-17 19:15:15 -07:00
|
|
|
}
|
2023-01-06 03:27:54 +00:00
|
|
|
|
2023-10-31 07:09:50 -07:00
|
|
|
impl AsFd for ShmSegment {
|
|
|
|
|
fn as_fd(&self) -> BorrowedFd<'_> {
|
|
|
|
|
self.id.as_fd()
|
2023-01-06 03:27:54 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Drop for ShmSegment {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
unsafe {
|
2023-10-17 19:15:15 -07:00
|
|
|
// Unmap the shared memory segment.
|
|
|
|
|
mm::munmap(self.ptr.as_ptr().cast(), self.size).ok();
|
2022-12-27 09:14:54 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-01-15 08:17:17 -06:00
|
|
|
|
2023-10-26 19:15:51 -07:00
|
|
|
impl<D: ?Sized> Drop for X11DisplayImpl<D> {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
// Make sure that the x11rb connection is dropped before its source is.
|
|
|
|
|
self.connection = None;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<D: ?Sized, W: ?Sized> Drop for X11Impl<D, W> {
|
2022-12-27 09:14:54 -08:00
|
|
|
fn drop(&mut self) {
|
2023-01-06 03:27:54 +00:00
|
|
|
// If we used SHM, make sure it's detached from the server.
|
2023-04-06 00:30:59 -07:00
|
|
|
if let Buffer::Shm(mut shm) = mem::replace(&mut self.buffer, Buffer::Wire(Vec::new())) {
|
2023-01-06 03:27:54 +00:00
|
|
|
// If we were in the middle of processing a buffer, wait for it to finish.
|
2023-10-26 19:15:51 -07:00
|
|
|
shm.finish_wait(self.display.connection()).ok();
|
2023-01-06 03:27:54 +00:00
|
|
|
|
|
|
|
|
if let Some((segment, seg_id)) = shm.seg.take() {
|
2023-10-26 19:15:51 -07:00
|
|
|
if let Ok(token) = self.display.connection().shm_detach(seg_id) {
|
2023-01-06 03:27:54 +00:00
|
|
|
token.ignore_error();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Drop the segment.
|
|
|
|
|
drop(segment);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-27 09:14:54 -08:00
|
|
|
// Close the graphics context that we created.
|
2023-10-26 19:15:51 -07:00
|
|
|
if let Ok(token) = self.display.connection().free_gc(self.gc) {
|
2022-12-27 09:14:54 -08:00
|
|
|
token.ignore_error();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-01-15 08:17:17 -06:00
|
|
|
|
2023-10-17 19:15:15 -07:00
|
|
|
/// Create a shared memory identifier.
|
2023-10-31 07:09:50 -07:00
|
|
|
fn create_shm_id() -> io::Result<OwnedFd> {
|
2023-10-17 19:15:15 -07:00
|
|
|
use posix_shm::{Mode, ShmOFlags};
|
|
|
|
|
|
|
|
|
|
let mut rng = fastrand::Rng::new();
|
|
|
|
|
let mut name = String::with_capacity(23);
|
|
|
|
|
|
|
|
|
|
// Only try four times; the chances of a collision on this space is astronomically low, so if
|
|
|
|
|
// we miss four times in a row we're probably under attack.
|
|
|
|
|
for i in 0..4 {
|
|
|
|
|
name.clear();
|
|
|
|
|
name.push_str("softbuffer-x11-");
|
|
|
|
|
name.extend(std::iter::repeat_with(|| rng.alphanumeric()).take(7));
|
|
|
|
|
|
|
|
|
|
// Try to create the shared memory segment.
|
|
|
|
|
match posix_shm::shm_open(
|
|
|
|
|
&name,
|
|
|
|
|
ShmOFlags::RDWR | ShmOFlags::CREATE | ShmOFlags::EXCL,
|
|
|
|
|
Mode::RWXU,
|
|
|
|
|
) {
|
|
|
|
|
Ok(id) => {
|
|
|
|
|
posix_shm::shm_unlink(&name).ok();
|
|
|
|
|
return Ok(id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Err(rustix::io::Errno::EXIST) => {
|
|
|
|
|
log::warn!("x11: SHM ID collision at {} on try number {}", name, i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Err(e) => return Err(e.into()),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Err(io::Error::new(
|
|
|
|
|
io::ErrorKind::Other,
|
|
|
|
|
"failed to generate a non-existent SHM name",
|
|
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-06 03:27:54 +00:00
|
|
|
/// Test to see if SHM is available.
|
|
|
|
|
fn is_shm_available(c: &impl Connection) -> bool {
|
|
|
|
|
// Create a small SHM segment.
|
2023-04-24 19:37:41 -03:00
|
|
|
let seg = match ShmSegment::new(0x1000, 0x1000) {
|
2023-01-06 03:27:54 +00:00
|
|
|
Ok(seg) => seg,
|
|
|
|
|
Err(_) => return false,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Attach and detach it.
|
|
|
|
|
let seg_id = match c.generate_id() {
|
|
|
|
|
Ok(id) => id,
|
|
|
|
|
Err(_) => return false,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let (attach, detach) = {
|
2023-10-31 07:09:50 -07:00
|
|
|
let attach = c.shm_attach_fd(seg_id, seg.as_fd().try_clone_to_owned().unwrap(), false);
|
2023-01-06 03:27:54 +00:00
|
|
|
let detach = c.shm_detach(seg_id);
|
|
|
|
|
|
|
|
|
|
match (attach, detach) {
|
|
|
|
|
(Ok(attach), Ok(detach)) => (attach, detach),
|
|
|
|
|
_ => return false,
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Check the replies.
|
|
|
|
|
matches!((attach.check(), detach.check()), (Ok(()), Ok(())))
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-11 06:54:10 +01:00
|
|
|
/// Collect all visuals that use softbuffer's pixel format
|
|
|
|
|
fn supported_visuals(c: &impl Connection) -> HashSet<Visualid> {
|
|
|
|
|
// Check that depth 24 uses 32 bits per pixels
|
|
|
|
|
if !c
|
|
|
|
|
.setup()
|
|
|
|
|
.pixmap_formats
|
|
|
|
|
.iter()
|
|
|
|
|
.any(|f| f.depth == 24 && f.bits_per_pixel == 32)
|
|
|
|
|
{
|
|
|
|
|
log::warn!("X11 server does not have a depth 24 format with 32 bits per pixel");
|
|
|
|
|
return HashSet::new();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// How does the server represent red, green, blue components of a pixel?
|
|
|
|
|
#[cfg(target_endian = "little")]
|
|
|
|
|
let own_byte_order = ImageOrder::LSB_FIRST;
|
|
|
|
|
#[cfg(target_endian = "big")]
|
|
|
|
|
let own_byte_order = ImageOrder::MSB_FIRST;
|
|
|
|
|
let expected_masks = if c.setup().image_byte_order == own_byte_order {
|
|
|
|
|
(0xff0000, 0xff00, 0xff)
|
|
|
|
|
} else {
|
|
|
|
|
// This is the byte-swapped version of our wished-for format
|
|
|
|
|
(0xff00, 0xff0000, 0xff000000)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
c.setup()
|
|
|
|
|
.roots
|
|
|
|
|
.iter()
|
|
|
|
|
.flat_map(|screen| {
|
|
|
|
|
screen
|
|
|
|
|
.allowed_depths
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|depth| depth.depth == 24)
|
|
|
|
|
.flat_map(|depth| {
|
|
|
|
|
depth
|
|
|
|
|
.visuals
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|visual| {
|
|
|
|
|
// Ignore grayscale or indexes / color palette visuals
|
|
|
|
|
visual.class == VisualClass::TRUE_COLOR
|
|
|
|
|
|| visual.class == VisualClass::DIRECT_COLOR
|
|
|
|
|
})
|
|
|
|
|
.filter(|visual| {
|
|
|
|
|
// Colors must be laid out as softbuffer expects
|
|
|
|
|
expected_masks == (visual.red_mask, visual.green_mask, visual.blue_mask)
|
|
|
|
|
})
|
|
|
|
|
.map(|visual| visual.visual_id)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
.collect()
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-06 03:27:54 +00:00
|
|
|
/// An error that can occur when pushing a buffer to the window.
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
enum PushBufferError {
|
|
|
|
|
/// We encountered an X11 error.
|
|
|
|
|
X11(ReplyError),
|
|
|
|
|
|
|
|
|
|
/// We exhausted the XID space.
|
|
|
|
|
XidExhausted,
|
|
|
|
|
|
|
|
|
|
/// A system error occurred while creating the shared memory segment.
|
|
|
|
|
System(io::Error),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl fmt::Display for PushBufferError {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
|
match self {
|
|
|
|
|
Self::X11(e) => write!(f, "X11 error: {}", e),
|
|
|
|
|
Self::XidExhausted => write!(f, "XID space exhausted"),
|
|
|
|
|
Self::System(e) => write!(f, "System error: {}", e),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl std::error::Error for PushBufferError {}
|
|
|
|
|
|
|
|
|
|
impl From<ConnectionError> for PushBufferError {
|
|
|
|
|
fn from(e: ConnectionError) -> Self {
|
|
|
|
|
Self::X11(ReplyError::ConnectionError(e))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<ReplyError> for PushBufferError {
|
|
|
|
|
fn from(e: ReplyError) -> Self {
|
|
|
|
|
Self::X11(e)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<ReplyOrIdError> for PushBufferError {
|
|
|
|
|
fn from(e: ReplyOrIdError) -> Self {
|
|
|
|
|
match e {
|
|
|
|
|
ReplyOrIdError::ConnectionError(e) => Self::X11(ReplyError::ConnectionError(e)),
|
|
|
|
|
ReplyOrIdError::X11Error(e) => Self::X11(ReplyError::X11Error(e)),
|
|
|
|
|
ReplyOrIdError::IdsExhausted => Self::XidExhausted,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<io::Error> for PushBufferError {
|
|
|
|
|
fn from(e: io::Error) -> Self {
|
|
|
|
|
Self::System(e)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-06 00:30:59 -07:00
|
|
|
/// Convenient wrapper to cast errors into PushBufferError.
|
|
|
|
|
trait PushResultExt<T, E> {
|
|
|
|
|
fn push_err(self) -> Result<T, PushBufferError>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<T, E: Into<PushBufferError>> PushResultExt<T, E> for Result<T, E> {
|
|
|
|
|
fn push_err(self) -> Result<T, PushBufferError> {
|
|
|
|
|
self.map_err(Into::into)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Get the length that a slice needs to be to hold a buffer of the given dimensions.
|
|
|
|
|
#[inline(always)]
|
|
|
|
|
fn total_len(width: u16, height: u16) -> usize {
|
|
|
|
|
let width: usize = width.into();
|
|
|
|
|
let height: usize = height.into();
|
|
|
|
|
|
|
|
|
|
width
|
|
|
|
|
.checked_mul(height)
|
|
|
|
|
.and_then(|len| len.checked_mul(4))
|
|
|
|
|
.unwrap_or_else(|| panic!("Dimensions are too large: ({} x {})", width, height))
|
|
|
|
|
}
|