Merge pull request #64 from rust-windowing/display-window-split
Split display/window wrappers
This commit is contained in:
commit
125ad070c2
11 changed files with 298 additions and 158 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
10
src/error.rs
10
src/error.rs
|
|
@ -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.")]
|
||||
|
|
|
|||
180
src/lib.rs
180
src/lib.rs
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
152
src/x11.rs
152
src/x11.rs
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue