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
|
|
|
|
|
//! drawn. A more effective implementation would use shared memory instead of the wire. In
|
|
|
|
|
//! addition, we may also want to blit to a pixmap instead of a window.
|
|
|
|
|
|
2022-12-27 12:23:27 -08:00
|
|
|
use crate::SoftBufferError;
|
2022-12-27 09:14:54 -08:00
|
|
|
use raw_window_handle::{XcbDisplayHandle, XcbWindowHandle, XlibDisplayHandle, XlibWindowHandle};
|
|
|
|
|
use std::fmt;
|
2022-01-15 08:17:17 -06:00
|
|
|
|
2022-12-27 09:14:54 -08:00
|
|
|
use x11_dl::xlib::Display;
|
|
|
|
|
use x11_dl::xlib_xcb::Xlib_xcb;
|
2022-12-21 17:26:09 -08:00
|
|
|
|
2022-12-27 09:14:54 -08:00
|
|
|
use x11rb::connection::Connection;
|
|
|
|
|
use x11rb::protocol::xproto::{self, ConnectionExt as _, Gcontext, Window};
|
|
|
|
|
use x11rb::xcb_ffi::XCBConnection;
|
2022-12-21 17:26:09 -08:00
|
|
|
|
2022-12-27 09:14:54 -08:00
|
|
|
/// The handle to an X11 drawing context.
|
|
|
|
|
pub struct X11Impl {
|
|
|
|
|
/// The handle to the XCB connection.
|
|
|
|
|
connection: XCBConnection,
|
2022-12-21 17:26:09 -08:00
|
|
|
|
2022-12-27 09:14:54 -08:00
|
|
|
/// The window to draw to.
|
|
|
|
|
window: Window,
|
2022-12-21 17:26:09 -08:00
|
|
|
|
2022-12-27 09:14:54 -08:00
|
|
|
/// The graphics context to use when drawing.
|
|
|
|
|
gc: 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,
|
2022-01-15 08:17:17 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl X11Impl {
|
2022-12-21 17:26:09 -08:00
|
|
|
/// Create a new `X11Impl` from a `XlibWindowHandle` and `XlibDisplayHandle`.
|
2022-12-22 10:09:47 -08:00
|
|
|
///
|
2022-12-21 17:26:09 -08:00
|
|
|
/// # Safety
|
2022-12-22 10:09:47 -08:00
|
|
|
///
|
2022-12-21 17:26:09 -08:00
|
|
|
/// The `XlibWindowHandle` and `XlibDisplayHandle` must be valid.
|
2022-12-27 09:14:54 -08:00
|
|
|
pub unsafe fn from_xlib(
|
2022-12-21 17:26:09 -08:00
|
|
|
window_handle: XlibWindowHandle,
|
|
|
|
|
display_handle: XlibDisplayHandle,
|
2022-12-27 12:23:27 -08:00
|
|
|
) -> Result<Self, SoftBufferError> {
|
2022-12-27 09:14:54 -08:00
|
|
|
// TODO: We should cache the shared libraries.
|
|
|
|
|
|
|
|
|
|
// Try to open the XlibXCB shared library.
|
|
|
|
|
let lib_xcb = Xlib_xcb::open().swbuf_err("Failed to open XlibXCB shared library")?;
|
2022-12-21 17:26:09 -08:00
|
|
|
|
2022-12-27 09:14:54 -08:00
|
|
|
// Validate the display handle to ensure we can use it.
|
2022-12-21 17:26:09 -08:00
|
|
|
if display_handle.display.is_null() {
|
2022-12-27 12:23:27 -08:00
|
|
|
return Err(SoftBufferError::IncompleteDisplayHandle);
|
2022-12-21 17:26:09 -08:00
|
|
|
}
|
|
|
|
|
|
2022-12-27 09:14:54 -08:00
|
|
|
// Get the underlying XCB connection.
|
|
|
|
|
// SAFETY: The user has asserted that the display handle is valid.
|
|
|
|
|
let connection =
|
|
|
|
|
unsafe { (lib_xcb.XGetXCBConnection)(display_handle.display as *mut Display) };
|
|
|
|
|
|
|
|
|
|
// Construct the equivalent XCB display and window handles.
|
|
|
|
|
let mut xcb_display_handle = XcbDisplayHandle::empty();
|
|
|
|
|
xcb_display_handle.connection = connection;
|
|
|
|
|
xcb_display_handle.screen = display_handle.screen;
|
|
|
|
|
|
|
|
|
|
let mut xcb_window_handle = XcbWindowHandle::empty();
|
|
|
|
|
xcb_window_handle.window = window_handle.window as _;
|
|
|
|
|
xcb_window_handle.visual_id = window_handle.visual_id as _;
|
|
|
|
|
|
|
|
|
|
// SAFETY: If the user passed in valid Xlib handles, then these are valid XCB handles.
|
|
|
|
|
unsafe { Self::from_xcb(xcb_window_handle, xcb_display_handle) }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Create a new `X11Impl` from a `XcbWindowHandle` and `XcbDisplayHandle`.
|
|
|
|
|
///
|
|
|
|
|
/// # Safety
|
|
|
|
|
///
|
|
|
|
|
/// The `XcbWindowHandle` and `XcbDisplayHandle` must be valid.
|
|
|
|
|
pub(crate) unsafe fn from_xcb(
|
|
|
|
|
window_handle: XcbWindowHandle,
|
|
|
|
|
display_handle: XcbDisplayHandle,
|
2022-12-27 12:23:27 -08:00
|
|
|
) -> Result<Self, SoftBufferError> {
|
2022-12-27 09:14:54 -08:00
|
|
|
// Check that the handles are valid.
|
|
|
|
|
if display_handle.connection.is_null() {
|
2022-12-27 12:23:27 -08:00
|
|
|
return Err(SoftBufferError::IncompleteDisplayHandle);
|
2022-12-27 09:14:54 -08:00
|
|
|
}
|
|
|
|
|
|
2022-12-21 18:22:51 -08:00
|
|
|
if window_handle.window == 0 {
|
2022-12-27 12:23:27 -08:00
|
|
|
return Err(SoftBufferError::IncompleteWindowHandle);
|
2022-12-21 17:26:09 -08:00
|
|
|
}
|
|
|
|
|
|
2022-12-27 09:14:54 -08:00
|
|
|
// 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 connection = {
|
|
|
|
|
let result =
|
|
|
|
|
unsafe { XCBConnection::from_raw_xcb_connection(display_handle.connection, false) };
|
|
|
|
|
|
|
|
|
|
result.swbuf_err("Failed to wrap XCB connection")?
|
2022-12-21 17:26:09 -08:00
|
|
|
};
|
|
|
|
|
|
2022-12-27 09:14:54 -08:00
|
|
|
let window = window_handle.window;
|
|
|
|
|
|
|
|
|
|
// Start getting the depth of the window.
|
|
|
|
|
let geometry_token = connection
|
|
|
|
|
.get_geometry(window)
|
|
|
|
|
.swbuf_err("Failed to send geometry request")?;
|
|
|
|
|
|
|
|
|
|
// Create a new graphics context to draw to.
|
|
|
|
|
let gc = connection
|
|
|
|
|
.generate_id()
|
|
|
|
|
.swbuf_err("Failed to generate GC ID")?;
|
|
|
|
|
connection
|
|
|
|
|
.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.
|
|
|
|
|
let geometry_reply = geometry_token
|
|
|
|
|
.reply()
|
|
|
|
|
.swbuf_err("Failed to get geometry reply")?;
|
2022-01-15 08:17:17 -06:00
|
|
|
|
2022-12-21 17:26:09 -08:00
|
|
|
Ok(Self {
|
2022-12-27 09:14:54 -08:00
|
|
|
connection,
|
|
|
|
|
window,
|
2022-12-21 17:26:09 -08:00
|
|
|
gc,
|
2022-12-27 09:14:54 -08:00
|
|
|
depth: geometry_reply.depth,
|
2022-12-21 17:26:09 -08:00
|
|
|
})
|
2022-01-15 08:17:17 -06:00
|
|
|
}
|
|
|
|
|
|
2022-12-22 10:09:47 -08:00
|
|
|
pub(crate) unsafe fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16) {
|
2022-12-27 09:14:54 -08:00
|
|
|
// Draw the image to the buffer.
|
|
|
|
|
let result = self.connection.put_image(
|
|
|
|
|
xproto::ImageFormat::Z_PIXMAP,
|
|
|
|
|
self.window,
|
|
|
|
|
self.gc,
|
|
|
|
|
width,
|
|
|
|
|
height,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
self.depth,
|
|
|
|
|
bytemuck::cast_slice(buffer),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
match result {
|
|
|
|
|
Err(e) => log::error!("Failed to draw image to window: {}", e),
|
|
|
|
|
Ok(token) => token.ignore_error(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-01-15 08:17:17 -06:00
|
|
|
|
2022-12-27 09:14:54 -08:00
|
|
|
impl Drop for X11Impl {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
// Close the graphics context that we created.
|
|
|
|
|
if let Ok(token) = self.connection.free_gc(self.gc) {
|
|
|
|
|
token.ignore_error();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-01-15 08:17:17 -06:00
|
|
|
|
2022-12-27 12:23:27 -08:00
|
|
|
/// Convenient wrapper to cast errors into SoftBufferError.
|
2022-12-27 09:14:54 -08:00
|
|
|
trait ResultExt<T, E> {
|
2022-12-27 12:23:27 -08:00
|
|
|
fn swbuf_err(self, msg: impl Into<String>) -> Result<T, SoftBufferError>;
|
2022-12-27 09:14:54 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<T, E: fmt::Debug + fmt::Display + 'static> ResultExt<T, E> for Result<T, E> {
|
2022-12-27 12:23:27 -08:00
|
|
|
fn swbuf_err(self, msg: impl Into<String>) -> Result<T, SoftBufferError> {
|
2022-12-27 09:14:54 -08:00
|
|
|
self.map_err(|e| {
|
2022-12-27 12:23:27 -08:00
|
|
|
SoftBufferError::PlatformError(Some(msg.into()), Some(Box::new(LibraryError(e))))
|
2022-12-27 09:14:54 -08:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// A wrapper around a library error.
|
|
|
|
|
///
|
|
|
|
|
/// This prevents `x11-dl` and `x11rb` from becoming public dependencies, since users cannot downcast
|
|
|
|
|
/// to this type.
|
|
|
|
|
struct LibraryError<E>(E);
|
|
|
|
|
|
|
|
|
|
impl<E: fmt::Debug> fmt::Debug for LibraryError<E> {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
|
fmt::Debug::fmt(&self.0, f)
|
2022-01-15 08:17:17 -06:00
|
|
|
}
|
2022-01-16 08:59:29 -06:00
|
|
|
}
|
2022-12-27 09:14:54 -08:00
|
|
|
|
|
|
|
|
impl<E: fmt::Display> fmt::Display for LibraryError<E> {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
|
fmt::Display::fmt(&self.0, f)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<E: fmt::Debug + fmt::Display> std::error::Error for LibraryError<E> {}
|