Merge pull request #64 from rust-windowing/display-window-split

Split display/window wrappers
This commit is contained in:
Ian Douglas Scott 2023-01-10 16:05:46 -08:00 committed by GitHub
commit 125ad070c2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 298 additions and 158 deletions

View file

@ -53,7 +53,6 @@ For now, the priority for new platforms is:
Example
==
```rust,no_run
use softbuffer::GraphicsContext;
use winit::event::{Event, WindowEvent};
use winit::event_loop::{ControlFlow, EventLoop};
use winit::window::WindowBuilder;
@ -61,7 +60,8 @@ use winit::window::WindowBuilder;
fn main() {
let event_loop = EventLoop::new();
let window = WindowBuilder::new().build(&event_loop).unwrap();
let mut graphics_context = unsafe { GraphicsContext::new(&window, &window) }.unwrap();
let context = unsafe { softbuffer::Context::new(&window) }.unwrap();
let mut surface = unsafe { softbuffer::Surface::new(&context, &window) }.unwrap();
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
@ -86,7 +86,7 @@ fn main() {
})
.collect::<Vec<_>>();
graphics_context.set_buffer(&buffer, width as u16, height as u16);
surface.set_buffer(&buffer, width as u16, height as u16);
}
Event::WindowEvent {
event: WindowEvent::CloseRequested,

View file

@ -1,7 +1,6 @@
use instant::Instant;
#[cfg(not(target_arch = "wasm32"))]
use rayon::prelude::*;
use softbuffer::GraphicsContext;
use std::f64::consts::PI;
use winit::event::{Event, WindowEvent};
use winit::event_loop::{ControlFlow, EventLoop};
@ -25,7 +24,8 @@ fn main() {
.unwrap();
}
let mut graphics_context = unsafe { GraphicsContext::new(&window, &window) }.unwrap();
let context = unsafe { softbuffer::Context::new(&window) }.unwrap();
let mut surface = unsafe { softbuffer::Surface::new(&context, &window) }.unwrap();
let mut old_size = (0, 0);
let mut frames = pre_render_frames(0, 0);
@ -48,7 +48,7 @@ fn main() {
};
let buffer = &frames[((elapsed * 60.0).round() as usize).clamp(0, 59)];
graphics_context.set_buffer(buffer.as_slice(), width as u16, height as u16);
surface.set_buffer(buffer.as_slice(), width as u16, height as u16);
}
Event::MainEventsCleared => {
window.request_redraw();

View file

@ -1,5 +1,4 @@
use image::GenericImageView;
use softbuffer::GraphicsContext;
use winit::event::{Event, WindowEvent};
use winit::event_loop::{ControlFlow, EventLoop};
use winit::window::WindowBuilder;
@ -38,14 +37,15 @@ fn main() {
.unwrap();
}
let mut graphics_context = unsafe { GraphicsContext::new(&window, &window) }.unwrap();
let context = unsafe { softbuffer::Context::new(&window) }.unwrap();
let mut surface = unsafe { softbuffer::Surface::new(&context, &window) }.unwrap();
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
match event {
Event::RedrawRequested(window_id) if window_id == window.id() => {
graphics_context.set_buffer(&buffer, fruit.width() as u16, fruit.height() as u16);
surface.set_buffer(&buffer, fruit.width() as u16, fruit.height() as u16);
}
Event::WindowEvent {
event: WindowEvent::CloseRequested,

View file

@ -3,7 +3,6 @@
#[cfg(all(feature = "x11", any(target_os = "linux", target_os = "freebsd")))]
mod example {
use raw_window_handle::{RawDisplayHandle, RawWindowHandle, XcbDisplayHandle, XcbWindowHandle};
use softbuffer::GraphicsContext;
use x11rb::{
connection::Connection,
protocol::{
@ -54,13 +53,12 @@ mod example {
// Create a new softbuffer context.
// SAFETY: The display and window handles outlive the context.
let mut context = unsafe {
GraphicsContext::from_raw(
RawWindowHandle::Xcb(window_handle),
RawDisplayHandle::Xcb(display_handle),
)
}
.unwrap();
let context =
unsafe { softbuffer::Context::from_raw(RawDisplayHandle::Xcb(display_handle)) }
.unwrap();
let mut surface =
unsafe { softbuffer::Surface::from_raw(&context, RawWindowHandle::Xcb(window_handle)) }
.unwrap();
// Register an atom for closing the window.
let wm_protocols_atom = conn
@ -104,7 +102,7 @@ mod example {
.collect::<Vec<_>>();
// Draw the buffer.
context.set_buffer(&source, width, height);
surface.set_buffer(&source, width, height);
}
Event::ConfigureNotify(configure_notify) => {
width = configure_notify.width;

View file

@ -1,4 +1,3 @@
use softbuffer::GraphicsContext;
use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent};
use winit::event_loop::{ControlFlow, EventLoop};
use winit::window::WindowBuilder;
@ -41,7 +40,8 @@ fn main() {
.unwrap();
}
let mut graphics_context = unsafe { GraphicsContext::new(&window, &window) }.unwrap();
let context = unsafe { softbuffer::Context::new(&window) }.unwrap();
let mut surface = unsafe { softbuffer::Surface::new(&context, &window) }.unwrap();
let mut buffer = Vec::new();
let mut flag = false;
@ -66,7 +66,7 @@ fn main() {
redraw(&mut buffer, width, height, flag);
// Blit the offscreen buffer to the window's client area
graphics_context.set_buffer(&buffer, width as u16, height as u16);
surface.set_buffer(&buffer, width as u16, height as u16);
}
Event::WindowEvent {

View file

@ -1,4 +1,3 @@
use softbuffer::GraphicsContext;
use winit::event::{Event, WindowEvent};
use winit::event_loop::{ControlFlow, EventLoop};
use winit::window::WindowBuilder;
@ -21,7 +20,8 @@ fn main() {
.unwrap();
}
let mut graphics_context = unsafe { GraphicsContext::new(&window, &window) }.unwrap();
let context = unsafe { softbuffer::Context::new(&window) }.unwrap();
let mut surface = unsafe { softbuffer::Surface::new(&context, &window) }.unwrap();
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
@ -46,7 +46,7 @@ fn main() {
})
.collect::<Vec<_>>();
graphics_context.set_buffer(&buffer, width as u16, height as u16);
surface.set_buffer(&buffer, width as u16, height as u16);
}
Event::WindowEvent {
event: WindowEvent::CloseRequested,

View file

@ -1,4 +1,3 @@
use softbuffer::GraphicsContext;
use winit::event::{Event, WindowEvent};
use winit::event_loop::{ControlFlow, EventLoop};
use winit::window::WindowBuilder;
@ -24,7 +23,8 @@ fn main() {
.unwrap();
}
let mut graphics_context = unsafe { GraphicsContext::new(&window, &window) }.unwrap();
let context = unsafe { softbuffer::Context::new(&window) }.unwrap();
let mut surface = unsafe { softbuffer::Surface::new(&context, &window) }.unwrap();
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
@ -45,7 +45,7 @@ fn main() {
})
.collect::<Vec<_>>();
graphics_context.set_buffer(&buffer, BUFFER_WIDTH as u16, BUFFER_HEIGHT as u16);
surface.set_buffer(&buffer, BUFFER_WIDTH as u16, BUFFER_HEIGHT as u16);
}
Event::WindowEvent {
event: WindowEvent::CloseRequested,

View file

@ -5,14 +5,20 @@ use thiserror::Error;
#[derive(Error, Debug)]
#[non_exhaustive]
pub enum SoftBufferError {
#[error(
"The provided display returned an unsupported platform: {human_readable_display_platform_name}."
)]
UnsupportedDisplayPlatform {
human_readable_display_platform_name: &'static str,
display_handle: RawDisplayHandle,
},
#[error(
"The provided window returned an unsupported platform: {human_readable_window_platform_name}, {human_readable_display_platform_name}."
)]
UnsupportedPlatform {
UnsupportedWindowPlatform {
human_readable_window_platform_name: &'static str,
human_readable_display_platform_name: &'static str,
window_handle: RawWindowHandle,
display_handle: RawDisplayHandle,
},
#[error("The provided window handle is null.")]

View file

@ -21,6 +21,9 @@ mod x11;
mod error;
#[cfg(any(wayland_platform, x11_platform))]
use std::sync::Arc;
pub use error::SoftBufferError;
use raw_window_handle::{
@ -29,12 +32,9 @@ use raw_window_handle::{
/// An instance of this struct contains the platform-specific data that must be managed in order to
/// write to a window on that platform.
pub struct GraphicsContext {
pub struct Context {
/// The inner static dispatch object.
///
/// This is boxed so that `GraphicsContext` is the same size on every platform, which should
/// hopefully prevent surprises.
graphics_context_impl: Box<Dispatch>,
context_impl: ContextDispatch,
}
/// A macro for creating the enum used to statically dispatch to the platform-specific implementation.
@ -42,17 +42,35 @@ macro_rules! make_dispatch {
(
$(
$(#[$attr:meta])*
$name: ident ($inner_ty: ty),
$name: ident ($context_inner: ty, $surface_inner : ty),
)*
) => {
enum Dispatch {
enum ContextDispatch {
$(
$(#[$attr])*
$name($inner_ty),
$name($context_inner),
)*
}
impl Dispatch {
impl ContextDispatch {
fn variant_name(&self) -> &'static str {
match self {
$(
$(#[$attr])*
Self::$name(_) => stringify!($name),
)*
}
}
}
enum SurfaceDispatch {
$(
$(#[$attr])*
$name($surface_inner),
)*
}
impl SurfaceDispatch {
unsafe fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16) {
match self {
$(
@ -67,31 +85,91 @@ macro_rules! make_dispatch {
make_dispatch! {
#[cfg(x11_platform)]
X11(x11::X11Impl),
X11(Arc<x11::X11DisplayImpl>, x11::X11Impl),
#[cfg(wayland_platform)]
Wayland(wayland::WaylandImpl),
Wayland(std::sync::Arc<wayland::WaylandDisplayImpl>, wayland::WaylandImpl),
#[cfg(target_os = "windows")]
Win32(win32::Win32Impl),
Win32((), win32::Win32Impl),
#[cfg(target_os = "macos")]
CG(cg::CGImpl),
CG((), cg::CGImpl),
#[cfg(target_arch = "wasm32")]
Web(web::WebImpl),
Web((), web::WebImpl),
#[cfg(target_os = "redox")]
Orbital(orbital::OrbitalImpl),
Orbital((), orbital::OrbitalImpl),
}
impl GraphicsContext {
impl Context {
/// Creates a new instance of this struct, using the provided display.
///
/// # Safety
///
/// - Ensure that the provided object is valid for the lifetime of the Context
pub unsafe fn new<D: HasRawDisplayHandle>(display: &D) -> Result<Self, SoftBufferError> {
unsafe { Self::from_raw(display.raw_display_handle()) }
}
/// Creates a new instance of this struct, using the provided display handles
///
/// # Safety
///
/// - Ensure that the provided handle is valid for the lifetime of the Context
pub unsafe fn from_raw(raw_display_handle: RawDisplayHandle) -> Result<Self, SoftBufferError> {
let imple: ContextDispatch = match raw_display_handle {
#[cfg(x11_platform)]
RawDisplayHandle::Xlib(xlib_handle) => unsafe {
ContextDispatch::X11(Arc::new(x11::X11DisplayImpl::from_xlib(xlib_handle)?))
},
#[cfg(x11_platform)]
RawDisplayHandle::Xcb(xcb_handle) => unsafe {
ContextDispatch::X11(Arc::new(x11::X11DisplayImpl::from_xcb(xcb_handle)?))
},
#[cfg(wayland_platform)]
RawDisplayHandle::Wayland(wayland_handle) => unsafe {
ContextDispatch::Wayland(Arc::new(wayland::WaylandDisplayImpl::new(
wayland_handle,
)?))
},
#[cfg(target_os = "windows")]
RawDisplayHandle::Windows(_) => ContextDispatch::Win32(()),
#[cfg(target_os = "macos")]
RawDisplayHandle::AppKit(_) => ContextDispatch::CG(()),
#[cfg(target_arch = "wasm32")]
RawDisplayHandle::Web(_) => ContextDispatch::Web(()),
#[cfg(target_os = "redox")]
RawDisplayHandle::Orbital(_) => ContextDispatch::Orbital(()),
unimplemented_display_handle => {
return Err(SoftBufferError::UnsupportedDisplayPlatform {
human_readable_display_platform_name: display_handle_type_name(
&unimplemented_display_handle,
),
display_handle: unimplemented_display_handle,
})
}
};
Ok(Self {
context_impl: imple,
})
}
}
pub struct Surface {
/// This is boxed so that `Surface` is the same size on every platform.
surface_impl: Box<SurfaceDispatch>,
}
impl Surface {
/// Creates a new instance of this struct, using the provided window and display.
///
/// # Safety
///
/// - Ensure that the provided objects are valid to draw a 2D buffer to, and are valid for the
/// lifetime of the GraphicsContext
pub unsafe fn new<W: HasRawWindowHandle, D: HasRawDisplayHandle>(
/// lifetime of the Context
pub unsafe fn new<W: HasRawWindowHandle>(
context: &Context,
window: &W,
display: &D,
) -> Result<Self, SoftBufferError> {
unsafe { Self::from_raw(window.raw_window_handle(), display.raw_display_handle()) }
unsafe { Self::from_raw(context, window.raw_window_handle()) }
}
/// Creates a new instance of this struct, using the provided raw window and display handles
@ -99,63 +177,61 @@ impl GraphicsContext {
/// # Safety
///
/// - Ensure that the provided handles are valid to draw a 2D buffer to, and are valid for the
/// lifetime of the GraphicsContext
/// lifetime of the Context
pub unsafe fn from_raw(
context: &Context,
raw_window_handle: RawWindowHandle,
raw_display_handle: RawDisplayHandle,
) -> Result<Self, SoftBufferError> {
let imple: Dispatch = match (raw_window_handle, raw_display_handle) {
let imple: SurfaceDispatch = match (&context.context_impl, raw_window_handle) {
#[cfg(x11_platform)]
(
ContextDispatch::X11(xcb_display_handle),
RawWindowHandle::Xlib(xlib_window_handle),
RawDisplayHandle::Xlib(xlib_display_handle),
) => Dispatch::X11(unsafe {
x11::X11Impl::from_xlib(xlib_window_handle, xlib_display_handle)?
) => SurfaceDispatch::X11(unsafe {
x11::X11Impl::from_xlib(xlib_window_handle, xcb_display_handle.clone())?
}),
#[cfg(x11_platform)]
(
RawWindowHandle::Xcb(xcb_window_handle),
RawDisplayHandle::Xcb(xcb_display_handle),
) => Dispatch::X11(unsafe {
x11::X11Impl::from_xcb(xcb_window_handle, xcb_display_handle)?
}),
(ContextDispatch::X11(xcb_display_handle), RawWindowHandle::Xcb(xcb_window_handle)) => {
SurfaceDispatch::X11(unsafe {
x11::X11Impl::from_xcb(xcb_window_handle, xcb_display_handle.clone())?
})
}
#[cfg(wayland_platform)]
(
ContextDispatch::Wayland(wayland_display_impl),
RawWindowHandle::Wayland(wayland_window_handle),
RawDisplayHandle::Wayland(wayland_display_handle),
) => Dispatch::Wayland(unsafe {
wayland::WaylandImpl::new(wayland_window_handle, wayland_display_handle)?
) => SurfaceDispatch::Wayland(unsafe {
wayland::WaylandImpl::new(wayland_window_handle, wayland_display_impl.clone())?
}),
#[cfg(target_os = "windows")]
(RawWindowHandle::Win32(win32_handle), _) => {
Dispatch::Win32(unsafe { win32::Win32Impl::new(&win32_handle)? })
(ContextDispatch::Win32(()), RawWindowHandle::Win32(win32_handle)) => {
SurfaceDispatch::Win32(unsafe { win32::Win32Impl::new(&win32_handle)? })
}
#[cfg(target_os = "macos")]
(RawWindowHandle::AppKit(appkit_handle), _) => {
Dispatch::CG(unsafe { cg::CGImpl::new(appkit_handle)? })
(ContextDispatch::CG(()), RawWindowHandle::AppKit(appkit_handle)) => {
SurfaceDispatch::CG(unsafe { cg::CGImpl::new(appkit_handle)? })
}
#[cfg(target_arch = "wasm32")]
(RawWindowHandle::Web(web_handle), _) => Dispatch::Web(web::WebImpl::new(web_handle)?),
#[cfg(target_os = "redox")]
(RawWindowHandle::Orbital(orbital_handle), _) => {
Dispatch::Orbital(orbital::OrbitalImpl::new(orbital_handle)?)
(ContextDispatch::Web(()), RawWindowHandle::Web(web_handle)) => {
SurfaceDispatch::Web(web::WebImpl::new(web_handle)?)
}
(unimplemented_window_handle, unimplemented_display_handle) => {
return Err(SoftBufferError::UnsupportedPlatform {
#[cfg(target_os = "redox")]
(ContextDispatch::Orbital(()), RawWindowHandle::Orbital(orbital_handle)) => {
SurfaceDispatch::Orbital(orbital::OrbitalImpl::new(orbital_handle)?)
}
(unsupported_display_impl, unimplemented_window_handle) => {
return Err(SoftBufferError::UnsupportedWindowPlatform {
human_readable_window_platform_name: window_handle_type_name(
&unimplemented_window_handle,
),
human_readable_display_platform_name: display_handle_type_name(
&unimplemented_display_handle,
),
human_readable_display_platform_name: unsupported_display_impl.variant_name(),
window_handle: unimplemented_window_handle,
display_handle: unimplemented_display_handle,
})
}
};
Ok(Self {
graphics_context_impl: Box::new(imple),
surface_impl: Box::new(imple),
})
}
@ -185,7 +261,7 @@ impl GraphicsContext {
///
/// # Platform dependent behavior
///
/// This section of the documentation details how some platforms may behave when [`set_buffer`](GraphicsContext::set_buffer)
/// This section of the documentation details how some platforms may behave when [`set_buffer`](Surface::set_buffer)
/// is called.
///
/// ## Wayland
@ -203,7 +279,7 @@ impl GraphicsContext {
}
unsafe {
self.graphics_context_impl.set_buffer(buffer, width, height);
self.surface_impl.set_buffer(buffer, width, height);
}
}
}

View file

@ -1,5 +1,6 @@
use crate::{error::unwrap, SoftBufferError};
use raw_window_handle::{WaylandDisplayHandle, WaylandWindowHandle};
use std::sync::{Arc, Mutex};
use wayland_client::{
backend::{Backend, ObjectId},
globals::{registry_queue_init, GlobalListContents},
@ -12,19 +13,15 @@ use buffer::WaylandBuffer;
struct State;
pub struct WaylandImpl {
event_queue: EventQueue<State>,
pub struct WaylandDisplayImpl {
conn: Connection,
event_queue: Mutex<EventQueue<State>>,
qh: QueueHandle<State>,
surface: wl_surface::WlSurface,
shm: wl_shm::WlShm,
buffers: Option<(WaylandBuffer, WaylandBuffer)>,
}
impl WaylandImpl {
pub unsafe fn new(
window_handle: WaylandWindowHandle,
display_handle: WaylandDisplayHandle,
) -> Result<Self, SoftBufferError> {
impl WaylandDisplayImpl {
pub unsafe fn new(display_handle: WaylandDisplayHandle) -> Result<Self, SoftBufferError> {
// SAFETY: Ensured by user
let backend = unsafe { Backend::from_foreign_display(display_handle.display as *mut _) };
let conn = Connection::from_backend(backend);
@ -37,6 +34,27 @@ impl WaylandImpl {
globals.bind(&qh, 1..=1, ()),
"Failed to instantiate Wayland Shm",
)?;
Ok(Self {
conn,
event_queue: Mutex::new(event_queue),
qh,
shm,
})
}
}
pub struct WaylandImpl {
display: Arc<WaylandDisplayImpl>,
surface: wl_surface::WlSurface,
buffers: Option<(WaylandBuffer, WaylandBuffer)>,
}
impl WaylandImpl {
pub unsafe fn new(
window_handle: WaylandWindowHandle,
display: Arc<WaylandDisplayImpl>,
) -> Result<Self, SoftBufferError> {
// SAFETY: Ensured by user
let surface_id = unwrap(
unsafe {
ObjectId::from_ptr(
@ -47,14 +65,12 @@ impl WaylandImpl {
"Failed to create proxy for surface ID.",
)?;
let surface = unwrap(
wl_surface::WlSurface::from_id(&conn, surface_id),
wl_surface::WlSurface::from_id(&display.conn, surface_id),
"Failed to create proxy for surface ID.",
)?;
Ok(Self {
event_queue,
qh,
display,
surface,
shm,
buffers: Default::default(),
})
}
@ -62,23 +78,31 @@ impl WaylandImpl {
fn buffer(&mut self, width: i32, height: i32) -> &WaylandBuffer {
self.buffers = Some(if let Some((front, mut back)) = self.buffers.take() {
// Swap buffers; block if back buffer not released yet
while !back.released() {
self.event_queue.blocking_dispatch(&mut State).unwrap();
if !back.released() {
let mut event_queue = self.display.event_queue.lock().unwrap();
while !back.released() {
event_queue.blocking_dispatch(&mut State).unwrap();
}
}
back.resize(width, height);
(back, front)
} else {
// Allocate front and back buffer
(
WaylandBuffer::new(&self.shm, width, height, &self.qh),
WaylandBuffer::new(&self.shm, width, height, &self.qh),
WaylandBuffer::new(&self.display.shm, width, height, &self.display.qh),
WaylandBuffer::new(&self.display.shm, width, height, &self.display.qh),
)
});
&self.buffers.as_ref().unwrap().0
}
pub(super) unsafe fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16) {
let _ = self.event_queue.dispatch_pending(&mut State);
let _ = self
.display
.event_queue
.lock()
.unwrap()
.dispatch_pending(&mut State);
let surface = self.surface.clone();
let wayland_buffer = self.buffer(width.into(), height.into());
@ -101,7 +125,7 @@ impl WaylandImpl {
}
self.surface.commit();
let _ = self.event_queue.flush();
let _ = self.display.event_queue.lock().unwrap().flush();
}
}

View file

@ -9,23 +9,90 @@ use crate::SoftBufferError;
use nix::libc::{shmat, shmctl, shmdt, shmget, IPC_PRIVATE, IPC_RMID};
use raw_window_handle::{XcbDisplayHandle, XcbWindowHandle, XlibDisplayHandle, XlibWindowHandle};
use std::ptr::{null_mut, NonNull};
use std::{fmt, io};
use std::{fmt, io, sync::Arc};
use x11_dl::xlib::Display;
use x11_dl::xlib_xcb::Xlib_xcb;
use x11rb::connection::{Connection, RequestConnection, SequenceNumber};
use x11rb::connection::{Connection, SequenceNumber};
use x11rb::cookie::Cookie;
use x11rb::errors::{ConnectionError, ReplyError, ReplyOrIdError};
use x11rb::protocol::shm::{self, ConnectionExt as _};
use x11rb::protocol::xproto::{self, ConnectionExt as _};
use x11rb::xcb_ffi::XCBConnection;
/// The handle to an X11 drawing context.
pub struct X11Impl {
pub struct X11DisplayImpl {
/// The handle to the XCB connection.
connection: XCBConnection,
/// SHM extension is available.
is_shm_available: bool,
}
impl X11DisplayImpl {
pub(crate) unsafe fn from_xlib(
display_handle: XlibDisplayHandle,
) -> Result<X11DisplayImpl, SoftBufferError> {
// 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")?;
// Validate the display handle to ensure we can use it.
if display_handle.display.is_null() {
return Err(SoftBufferError::IncompleteDisplayHandle);
}
// 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;
// SAFETY: If the user passed in valid Xlib handles, then these are valid XCB handles.
unsafe { Self::from_xcb(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(
display_handle: XcbDisplayHandle,
) -> Result<Self, SoftBufferError> {
// Check that the handle is valid.
if display_handle.connection.is_null() {
return Err(SoftBufferError::IncompleteDisplayHandle);
}
// 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")?
};
let is_shm_available = is_shm_available(&connection);
Ok(Self {
connection,
is_shm_available,
})
}
}
/// The handle to an X11 drawing context.
pub struct X11Impl {
/// X display this window belongs to.
display: Arc<X11DisplayImpl>,
/// The window to draw to.
window: xproto::Window,
@ -66,34 +133,14 @@ impl X11Impl {
/// The `XlibWindowHandle` and `XlibDisplayHandle` must be valid.
pub unsafe fn from_xlib(
window_handle: XlibWindowHandle,
display_handle: XlibDisplayHandle,
display: Arc<X11DisplayImpl>,
) -> Result<Self, SoftBufferError> {
// 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")?;
// Validate the display handle to ensure we can use it.
if display_handle.display.is_null() {
return Err(SoftBufferError::IncompleteDisplayHandle);
}
// 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) }
unsafe { Self::from_xcb(xcb_window_handle, display) }
}
/// Create a new `X11Impl` from a `XcbWindowHandle` and `XcbDisplayHandle`.
@ -103,41 +150,28 @@ impl X11Impl {
/// The `XcbWindowHandle` and `XcbDisplayHandle` must be valid.
pub(crate) unsafe fn from_xcb(
window_handle: XcbWindowHandle,
display_handle: XcbDisplayHandle,
display: Arc<X11DisplayImpl>,
) -> Result<Self, SoftBufferError> {
// Check that the handles are valid.
if display_handle.connection.is_null() {
return Err(SoftBufferError::IncompleteDisplayHandle);
}
// Check that the handle is valid.
if window_handle.window == 0 {
return Err(SoftBufferError::IncompleteWindowHandle);
}
// 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")?
};
let window = window_handle.window;
// Run in parallel: start getting the window depth and the SHM extension.
let geometry_token = connection
// Run in parallel: start getting the window depth.
let geometry_token = display
.connection
.get_geometry(window)
.swbuf_err("Failed to send geometry request")?;
connection
.prefetch_extension_information(shm::X11_EXTENSION_NAME)
.swbuf_err("Failed to send SHM query request")?;
// Create a new graphics context to draw to.
let gc = connection
let gc = display
.connection
.generate_id()
.swbuf_err("Failed to generate GC ID")?;
connection
display
.connection
.create_gc(
gc,
window,
@ -154,7 +188,7 @@ impl X11Impl {
// See if SHM is available.
let shm_info = {
let present = is_shm_available(&connection);
let present = display.is_shm_available;
if present {
// SHM is available.
@ -168,7 +202,7 @@ impl X11Impl {
};
Ok(Self {
connection,
display,
window,
gc,
depth: geometry_reply.depth,
@ -211,11 +245,11 @@ impl X11Impl {
};
// If the X server is still processing the last image, wait for it to finish.
shm_info.finish_wait(&self.connection)?;
shm_info.finish_wait(&self.display.connection)?;
// Get the SHM segment to use.
let necessary_size = (width as usize) * (height as usize) * 4;
let (segment, segment_id) = shm_info.segment(&self.connection, necessary_size)?;
let (segment, segment_id) = shm_info.segment(&self.display.connection, necessary_size)?;
// Copy the buffer into the segment.
// SAFETY: The buffer is properly sized and we've ensured that the X server isn't reading from it.
@ -224,7 +258,8 @@ impl X11Impl {
}
// Put the image into the window.
self.connection
self.display
.connection
.shm_put_image(
self.window,
self.gc,
@ -245,7 +280,7 @@ impl X11Impl {
.ignore_error();
// Send a short request to act as a notification for when the X server is done processing the image.
shm_info.begin_wait(&self.connection)?;
shm_info.begin_wait(&self.display.connection)?;
Ok(true)
}
@ -257,7 +292,8 @@ impl X11Impl {
width: u16,
height: u16,
) -> Result<(), PushBufferError> {
self.connection
self.display
.connection
.put_image(
xproto::ImageFormat::Z_PIXMAP,
self.window,
@ -426,10 +462,10 @@ impl Drop for X11Impl {
// If we used SHM, make sure it's detached from the server.
if let Some(mut shm) = self.shm.take() {
// If we were in the middle of processing a buffer, wait for it to finish.
shm.finish_wait(&self.connection).ok();
shm.finish_wait(&self.display.connection).ok();
if let Some((segment, seg_id)) = shm.seg.take() {
if let Ok(token) = self.connection.shm_detach(seg_id) {
if let Ok(token) = self.display.connection.shm_detach(seg_id) {
token.ignore_error();
}
@ -439,7 +475,7 @@ impl Drop for X11Impl {
}
// Close the graphics context that we created.
if let Ok(token) = self.connection.free_gc(self.gc) {
if let Ok(token) = self.display.connection.free_gc(self.gc) {
token.ignore_error();
}
}