Merge pull request #437 from i509VCB/x11/fix-error-drop
This commit is contained in:
commit
0afebf5fdb
|
@ -73,6 +73,7 @@
|
||||||
|
|
||||||
- EGLBufferReader now checks if buffers are alive before using them.
|
- EGLBufferReader now checks if buffers are alive before using them.
|
||||||
- LibSeat no longer panics on seat disable event.
|
- LibSeat no longer panics on seat disable event.
|
||||||
|
- X11 backend will report an error when trying to present a dmabuf fails.
|
||||||
|
|
||||||
### Anvil
|
### Anvil
|
||||||
|
|
||||||
|
|
|
@ -29,54 +29,23 @@
|
||||||
|
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use super::{Window, X11Error};
|
use super::{PresentError, Window, X11Error};
|
||||||
use drm_fourcc::DrmFourcc;
|
use drm_fourcc::DrmFourcc;
|
||||||
use nix::fcntl;
|
use nix::fcntl;
|
||||||
use x11rb::connection::Connection;
|
use x11rb::{
|
||||||
use x11rb::protocol::dri3::ConnectionExt as _;
|
connection::Connection,
|
||||||
use x11rb::protocol::present::{self, ConnectionExt};
|
protocol::{
|
||||||
use x11rb::protocol::xproto::PixmapWrapper;
|
dri3::ConnectionExt as _,
|
||||||
use x11rb::rust_connection::{ConnectionError, ReplyOrIdError};
|
present::{self, ConnectionExt},
|
||||||
use x11rb::utils::RawFdContainer;
|
xproto::PixmapWrapper,
|
||||||
|
},
|
||||||
|
utils::RawFdContainer,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::backend::allocator::dmabuf::Dmabuf;
|
use crate::backend::allocator::{dmabuf::Dmabuf, Buffer};
|
||||||
use crate::backend::allocator::Buffer;
|
|
||||||
|
|
||||||
// Shm can be easily supported in the future using, xcb_shm_create_pixmap.
|
// Shm can be easily supported in the future using, xcb_shm_create_pixmap.
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
pub enum CreatePixmapError {
|
|
||||||
#[error("An x11 protocol error occured")]
|
|
||||||
Protocol(X11Error),
|
|
||||||
|
|
||||||
#[error("The Dmabuf had too many planes")]
|
|
||||||
TooManyPlanes,
|
|
||||||
|
|
||||||
#[error("Duplicating the file descriptors for the dmabuf handles failed")]
|
|
||||||
DupFailed(String),
|
|
||||||
|
|
||||||
#[error("Buffer had incorrect format, expected: {0}")]
|
|
||||||
IncorrectFormat(DrmFourcc),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<X11Error> for CreatePixmapError {
|
|
||||||
fn from(e: X11Error) -> Self {
|
|
||||||
CreatePixmapError::Protocol(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ReplyOrIdError> for CreatePixmapError {
|
|
||||||
fn from(e: ReplyOrIdError) -> Self {
|
|
||||||
X11Error::from(e).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ConnectionError> for CreatePixmapError {
|
|
||||||
fn from(e: ConnectionError) -> Self {
|
|
||||||
X11Error::from(e).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait PixmapWrapperExt<'c, C>
|
pub trait PixmapWrapperExt<'c, C>
|
||||||
where
|
where
|
||||||
C: Connection,
|
C: Connection,
|
||||||
|
@ -88,7 +57,7 @@ where
|
||||||
connection: &'c C,
|
connection: &'c C,
|
||||||
window: &Window,
|
window: &Window,
|
||||||
dmabuf: &Dmabuf,
|
dmabuf: &Dmabuf,
|
||||||
) -> Result<PixmapWrapper<'c, C>, CreatePixmapError>;
|
) -> Result<PixmapWrapper<'c, C>, X11Error>;
|
||||||
|
|
||||||
/// Presents the pixmap to the window.
|
/// Presents the pixmap to the window.
|
||||||
///
|
///
|
||||||
|
@ -108,9 +77,9 @@ where
|
||||||
connection: &'c C,
|
connection: &'c C,
|
||||||
window: &Window,
|
window: &Window,
|
||||||
dmabuf: &Dmabuf,
|
dmabuf: &Dmabuf,
|
||||||
) -> Result<PixmapWrapper<'c, C>, CreatePixmapError> {
|
) -> Result<PixmapWrapper<'c, C>, X11Error> {
|
||||||
if dmabuf.format().code != window.format() {
|
if dmabuf.format().code != window.format() {
|
||||||
return Err(CreatePixmapError::IncorrectFormat(window.format()));
|
return Err(PresentError::IncorrectFormat(window.format()).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut fds = Vec::new();
|
let mut fds = Vec::new();
|
||||||
|
@ -121,7 +90,7 @@ where
|
||||||
handle,
|
handle,
|
||||||
fcntl::FcntlArg::F_DUPFD_CLOEXEC(3), // Set to 3 so the fd cannot become stdin, stdout or stderr
|
fcntl::FcntlArg::F_DUPFD_CLOEXEC(3), // Set to 3 so the fd cannot become stdin, stdout or stderr
|
||||||
)
|
)
|
||||||
.map_err(|e| CreatePixmapError::DupFailed(e.to_string()))?;
|
.map_err(|e| PresentError::DupFailed(e.to_string()))?;
|
||||||
|
|
||||||
fds.push(RawFdContainer::new(fd))
|
fds.push(RawFdContainer::new(fd))
|
||||||
}
|
}
|
||||||
|
@ -129,7 +98,7 @@ where
|
||||||
// We need dri3 >= 1.2 in order to use the enhanced dri3_pixmap_from_buffers function.
|
// We need dri3 >= 1.2 in order to use the enhanced dri3_pixmap_from_buffers function.
|
||||||
let xid = if window.0.extensions.dri3 >= Some((1, 2)) {
|
let xid = if window.0.extensions.dri3 >= Some((1, 2)) {
|
||||||
if dmabuf.num_planes() > 4 {
|
if dmabuf.num_planes() > 4 {
|
||||||
return Err(CreatePixmapError::TooManyPlanes);
|
return Err(PresentError::TooManyPlanes.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let xid = connection.generate_id()?;
|
let xid = connection.generate_id()?;
|
||||||
|
@ -165,7 +134,7 @@ where
|
||||||
} else {
|
} else {
|
||||||
// Old codepath can only create a pixmap using one plane from a dmabuf.
|
// Old codepath can only create a pixmap using one plane from a dmabuf.
|
||||||
if dmabuf.num_planes() != 1 {
|
if dmabuf.num_planes() != 1 {
|
||||||
return Err(CreatePixmapError::TooManyPlanes);
|
return Err(PresentError::TooManyPlanes.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let xid = connection.generate_id()?;
|
let xid = connection.generate_id()?;
|
||||||
|
|
|
@ -6,24 +6,26 @@ use x11rb::rust_connection::{ConnectError, ConnectionError, ReplyError, ReplyOrI
|
||||||
|
|
||||||
use crate::backend::{allocator::gbm::GbmConvertError, drm::CreateDrmNodeError};
|
use crate::backend::{allocator::gbm::GbmConvertError, drm::CreateDrmNodeError};
|
||||||
|
|
||||||
|
use super::PresentError;
|
||||||
|
|
||||||
/// An error emitted by the X11 backend during setup.
|
/// An error emitted by the X11 backend during setup.
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum X11Error {
|
pub enum X11Error {
|
||||||
/// Connecting to the X server failed.
|
/// Connecting to the X server failed.
|
||||||
#[error("Connecting to the X server failed")]
|
#[error("Connecting to the X server failed")]
|
||||||
ConnectionFailed(ConnectError),
|
ConnectionFailed(#[from] ConnectError),
|
||||||
|
|
||||||
/// A required X11 extension was not present or has the right version.
|
/// A required X11 extension was not present or has the right version.
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
MissingExtension(MissingExtensionError),
|
MissingExtension(#[from] MissingExtensionError),
|
||||||
|
|
||||||
/// Some protocol error occurred during setup.
|
/// Some protocol error occurred during setup.
|
||||||
#[error("Some protocol error occurred during setup")]
|
#[error("Some protocol error occurred during setup")]
|
||||||
Protocol(ReplyOrIdError),
|
Protocol(#[from] ReplyOrIdError),
|
||||||
|
|
||||||
/// Creating the window failed.
|
/// Creating the window failed.
|
||||||
#[error("Creating the window failed")]
|
#[error("Creating the window failed")]
|
||||||
CreateWindow(CreateWindowError),
|
CreateWindow(#[from] CreateWindowError),
|
||||||
|
|
||||||
/// An X11 surface already exists for this window.
|
/// An X11 surface already exists for this window.
|
||||||
#[error("An X11 surface already exists for this window")]
|
#[error("An X11 surface already exists for this window")]
|
||||||
|
@ -42,13 +44,11 @@ pub enum X11Error {
|
||||||
|
|
||||||
/// Failed to allocate buffers needed to present to the window.
|
/// Failed to allocate buffers needed to present to the window.
|
||||||
#[error("Failed to allocate buffers needed to present to the window")]
|
#[error("Failed to allocate buffers needed to present to the window")]
|
||||||
Allocation(AllocateBuffersError),
|
Allocation(#[from] AllocateBuffersError),
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ConnectError> for X11Error {
|
/// Error while presenting to a window.
|
||||||
fn from(err: ConnectError) -> Self {
|
#[error(transparent)]
|
||||||
Self::ConnectionFailed(err)
|
Present(#[from] PresentError),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ReplyError> for X11Error {
|
impl From<ReplyError> for X11Error {
|
||||||
|
@ -63,12 +63,6 @@ impl From<ConnectionError> for X11Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ReplyOrIdError> for X11Error {
|
|
||||||
fn from(err: ReplyOrIdError) -> Self {
|
|
||||||
Self::Protocol(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An error that occurs when a required X11 extension is not present.
|
/// An error that occurs when a required X11 extension is not present.
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum MissingExtensionError {
|
pub enum MissingExtensionError {
|
||||||
|
@ -99,12 +93,6 @@ pub enum MissingExtensionError {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<MissingExtensionError> for X11Error {
|
|
||||||
fn from(err: MissingExtensionError) -> Self {
|
|
||||||
Self::MissingExtension(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An error which may occur when creating an X11 window.
|
/// An error which may occur when creating an X11 window.
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum CreateWindowError {
|
pub enum CreateWindowError {
|
||||||
|
@ -117,12 +105,6 @@ pub enum CreateWindowError {
|
||||||
NoVisual,
|
NoVisual,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<CreateWindowError> for X11Error {
|
|
||||||
fn from(err: CreateWindowError) -> Self {
|
|
||||||
Self::CreateWindow(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An error which may occur when allocating buffers for presentation to the window.
|
/// An error which may occur when allocating buffers for presentation to the window.
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum AllocateBuffersError {
|
pub enum AllocateBuffersError {
|
||||||
|
@ -165,9 +147,3 @@ impl From<CreateDrmNodeError> for AllocateBuffersError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<AllocateBuffersError> for X11Error {
|
|
||||||
fn from(err: AllocateBuffersError) -> Self {
|
|
||||||
Self::Allocation(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -79,15 +79,12 @@ mod error;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod extension;
|
mod extension;
|
||||||
mod input;
|
mod input;
|
||||||
|
mod surface;
|
||||||
mod window_inner;
|
mod window_inner;
|
||||||
|
|
||||||
use self::{buffer::PixmapWrapperExt, window_inner::WindowInner};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::{
|
backend::{
|
||||||
allocator::{
|
allocator::Swapchain,
|
||||||
dmabuf::{AsDmabuf, Dmabuf},
|
|
||||||
Slot, Swapchain,
|
|
||||||
},
|
|
||||||
drm::{node::path_to_type, CreateDrmNodeError, DrmNode, NodeType},
|
drm::{node::path_to_type, CreateDrmNodeError, DrmNode, NodeType},
|
||||||
egl::{native::X11DefaultDisplay, EGLDevice, EGLDisplay, Error as EGLError},
|
egl::{native::X11DefaultDisplay, EGLDevice, EGLDisplay, Error as EGLError},
|
||||||
input::{Axis, ButtonState, InputEvent, KeyState},
|
input::{Axis, ButtonState, InputEvent, KeyState},
|
||||||
|
@ -96,7 +93,6 @@ use crate::{
|
||||||
};
|
};
|
||||||
use calloop::{EventSource, Poll, PostAction, Readiness, Token, TokenFactory};
|
use calloop::{EventSource, Poll, PostAction, Readiness, Token, TokenFactory};
|
||||||
use drm_fourcc::{DrmFourcc, DrmModifier};
|
use drm_fourcc::{DrmFourcc, DrmModifier};
|
||||||
use gbm::BufferObject;
|
|
||||||
use nix::{
|
use nix::{
|
||||||
fcntl::{self, OFlag},
|
fcntl::{self, OFlag},
|
||||||
sys::stat::Mode,
|
sys::stat::Mode,
|
||||||
|
@ -104,12 +100,11 @@ use nix::{
|
||||||
use slog::{error, info, o, Logger};
|
use slog::{error, info, o, Logger};
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
io, mem,
|
io,
|
||||||
os::unix::prelude::AsRawFd,
|
os::unix::prelude::AsRawFd,
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicU32, Ordering},
|
atomic::{AtomicU32, Ordering},
|
||||||
mpsc::{self, Receiver},
|
mpsc, Arc, Mutex, Weak,
|
||||||
Arc, Mutex, MutexGuard, Weak,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use x11rb::{
|
use x11rb::{
|
||||||
|
@ -118,18 +113,17 @@ use x11rb::{
|
||||||
protocol::{
|
protocol::{
|
||||||
self as x11,
|
self as x11,
|
||||||
dri3::ConnectionExt as _,
|
dri3::ConnectionExt as _,
|
||||||
xproto::{
|
xproto::{ColormapAlloc, ConnectionExt, CreateWindowAux, VisualClass, WindowClass, WindowWrapper},
|
||||||
ColormapAlloc, ConnectionExt, CreateWindowAux, PixmapWrapper, VisualClass, WindowClass,
|
|
||||||
WindowWrapper,
|
|
||||||
},
|
|
||||||
ErrorKind,
|
ErrorKind,
|
||||||
},
|
},
|
||||||
rust_connection::{ReplyError, RustConnection},
|
rust_connection::{ReplyError, RustConnection},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use self::{extension::Extensions, window_inner::WindowInner};
|
||||||
|
|
||||||
pub use self::error::*;
|
pub use self::error::*;
|
||||||
use self::extension::Extensions;
|
|
||||||
pub use self::input::*;
|
pub use self::input::*;
|
||||||
|
pub use self::surface::*;
|
||||||
|
|
||||||
/// An event emitted by the X11 backend.
|
/// An event emitted by the X11 backend.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -161,16 +155,6 @@ pub struct X11Backend {
|
||||||
inner: Arc<Mutex<X11Inner>>,
|
inner: Arc<Mutex<X11Inner>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
atom_manager! {
|
|
||||||
pub(crate) Atoms: AtomCollectionCookie {
|
|
||||||
WM_PROTOCOLS,
|
|
||||||
WM_DELETE_WINDOW,
|
|
||||||
_NET_WM_NAME,
|
|
||||||
UTF8_STRING,
|
|
||||||
_SMITHAY_X11_BACKEND_CLOSE,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl X11Backend {
|
impl X11Backend {
|
||||||
/// Initializes the X11 backend by connecting to the X server.
|
/// Initializes the X11 backend by connecting to the X server.
|
||||||
pub fn new<L>(logger: L) -> Result<X11Backend, X11Error>
|
pub fn new<L>(logger: L) -> Result<X11Backend, X11Error>
|
||||||
|
@ -277,19 +261,6 @@ impl X11Backend {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An X11 surface which uses GBM to allocate and present buffers.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct X11Surface {
|
|
||||||
connection: Weak<RustConnection>,
|
|
||||||
window: Weak<WindowInner>,
|
|
||||||
resize: Receiver<Size<u16, Logical>>,
|
|
||||||
swapchain: Swapchain<Arc<Mutex<gbm::Device<DrmNode>>>, BufferObject<()>>,
|
|
||||||
format: DrmFourcc,
|
|
||||||
width: u16,
|
|
||||||
height: u16,
|
|
||||||
buffer: Option<Slot<BufferObject<()>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
enum EGLInitError {
|
enum EGLInitError {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
|
@ -298,81 +269,6 @@ enum EGLInitError {
|
||||||
IO(#[from] io::Error),
|
IO(#[from] io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn egl_init(_: &X11Inner) -> Result<DrmNode, EGLInitError> {
|
|
||||||
let display = EGLDisplay::new(&X11DefaultDisplay, None)?;
|
|
||||||
let device = EGLDevice::device_for_display(&display)?;
|
|
||||||
let path = path_to_type(device.drm_device_path()?, NodeType::Render)?;
|
|
||||||
fcntl::open(&path, OFlag::O_RDWR | OFlag::O_CLOEXEC, Mode::empty())
|
|
||||||
.map_err(Into::<io::Error>::into)
|
|
||||||
.and_then(|fd| {
|
|
||||||
DrmNode::from_fd(fd).map_err(|err| match err {
|
|
||||||
CreateDrmNodeError::Io(err) => err,
|
|
||||||
_ => unreachable!(),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.map_err(EGLInitError::IO)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dri3_init(x11: &X11Inner) -> Result<DrmNode, X11Error> {
|
|
||||||
let connection = &x11.connection;
|
|
||||||
|
|
||||||
// Determine which drm-device the Display is using.
|
|
||||||
let screen = &connection.setup().roots[x11.screen_number];
|
|
||||||
// provider being NONE tells the X server to use the RandR provider.
|
|
||||||
let dri3 = match connection.dri3_open(screen.root, x11rb::NONE)?.reply() {
|
|
||||||
Ok(reply) => reply,
|
|
||||||
Err(err) => {
|
|
||||||
return Err(if let ReplyError::X11Error(ref protocol_error) = err {
|
|
||||||
match protocol_error.error_kind {
|
|
||||||
// Implementation is risen when the renderer is not capable of X server is not capable
|
|
||||||
// of rendering at all.
|
|
||||||
ErrorKind::Implementation => X11Error::CannotDirectRender,
|
|
||||||
// Match may occur when the node cannot be authenticated for the client.
|
|
||||||
ErrorKind::Match => X11Error::CannotDirectRender,
|
|
||||||
_ => err.into(),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err.into()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Take ownership of the container's inner value so we do not need to duplicate the fd.
|
|
||||||
// This is fine because the X server will always open a new file descriptor.
|
|
||||||
let drm_device_fd = dri3.device_fd.into_raw_fd();
|
|
||||||
|
|
||||||
let fd_flags =
|
|
||||||
fcntl::fcntl(drm_device_fd.as_raw_fd(), fcntl::F_GETFD).map_err(AllocateBuffersError::from)?;
|
|
||||||
|
|
||||||
// Enable the close-on-exec flag.
|
|
||||||
fcntl::fcntl(
|
|
||||||
drm_device_fd,
|
|
||||||
fcntl::F_SETFD(fcntl::FdFlag::from_bits_truncate(fd_flags) | fcntl::FdFlag::FD_CLOEXEC),
|
|
||||||
)
|
|
||||||
.map_err(AllocateBuffersError::from)?;
|
|
||||||
let drm_node = DrmNode::from_fd(drm_device_fd).map_err(Into::<AllocateBuffersError>::into)?;
|
|
||||||
|
|
||||||
if drm_node.ty() != NodeType::Render && drm_node.has_render() {
|
|
||||||
// Try to get the render node.
|
|
||||||
if let Some(path) = drm_node.dev_path_with_type(NodeType::Render) {
|
|
||||||
return Ok(fcntl::open(&path, OFlag::O_RDWR | OFlag::O_CLOEXEC, Mode::empty())
|
|
||||||
.map_err(Into::<std::io::Error>::into)
|
|
||||||
.map_err(CreateDrmNodeError::Io)
|
|
||||||
.and_then(DrmNode::from_fd)
|
|
||||||
.unwrap_or_else(|err| {
|
|
||||||
slog::warn!(&x11.log, "Could not create render node from existing DRM node ({}), falling back to primary node", err);
|
|
||||||
drm_node
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
slog::warn!(
|
|
||||||
&x11.log,
|
|
||||||
"DRM Device does not have a render node, falling back to primary node"
|
|
||||||
);
|
|
||||||
Ok(drm_node)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A handle to the X11 backend.
|
/// A handle to the X11 backend.
|
||||||
///
|
///
|
||||||
/// This is the primary object used to interface with the backend.
|
/// This is the primary object used to interface with the backend.
|
||||||
|
@ -482,115 +378,6 @@ impl X11Handle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl X11Surface {
|
|
||||||
/// Returns the window the surface presents to.
|
|
||||||
///
|
|
||||||
/// This will return [`None`] if the window has been destroyed.
|
|
||||||
pub fn window(&self) -> Option<impl AsRef<Window> + '_> {
|
|
||||||
let window = self.window.upgrade().map(Window).map(WindowTemporary);
|
|
||||||
|
|
||||||
struct WindowTemporary(Window);
|
|
||||||
|
|
||||||
impl AsRef<Window> for WindowTemporary {
|
|
||||||
fn as_ref(&self) -> &Window {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a handle to the GBM device used to allocate buffers.
|
|
||||||
pub fn device(&self) -> MutexGuard<'_, gbm::Device<DrmNode>> {
|
|
||||||
self.swapchain.allocator.lock().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the format of the buffers the surface accepts.
|
|
||||||
pub fn format(&self) -> DrmFourcc {
|
|
||||||
self.format
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the next buffer that will be presented to the Window and its age.
|
|
||||||
///
|
|
||||||
/// You may bind this buffer to a renderer to render.
|
|
||||||
/// This function will return the same buffer until [`submit`](Self::submit) is called
|
|
||||||
/// or [`reset_buffers`](Self::reset_buffer) is used to reset the buffers.
|
|
||||||
pub fn buffer(&mut self) -> Result<(Dmabuf, u8), AllocateBuffersError> {
|
|
||||||
if let Some(new_size) = self.resize.try_iter().last() {
|
|
||||||
self.resize(new_size)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.buffer.is_none() {
|
|
||||||
self.buffer = Some(
|
|
||||||
self.swapchain
|
|
||||||
.acquire()
|
|
||||||
.map_err(Into::<AllocateBuffersError>::into)?
|
|
||||||
.ok_or(AllocateBuffersError::NoFreeSlots)?,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let slot = self.buffer.as_ref().unwrap();
|
|
||||||
let age = slot.age();
|
|
||||||
match slot.userdata().get::<Dmabuf>() {
|
|
||||||
Some(dmabuf) => Ok((dmabuf.clone(), age)),
|
|
||||||
None => {
|
|
||||||
let dmabuf = slot.export().map_err(Into::<AllocateBuffersError>::into)?;
|
|
||||||
slot.userdata().insert_if_missing(|| dmabuf.clone());
|
|
||||||
Ok((dmabuf, age))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Consume and submit the buffer to the window.
|
|
||||||
pub fn submit(&mut self) -> Result<(), AllocateBuffersError> {
|
|
||||||
if let Some(connection) = self.connection.upgrade() {
|
|
||||||
// Get a new buffer
|
|
||||||
let mut next = self
|
|
||||||
.swapchain
|
|
||||||
.acquire()
|
|
||||||
.map_err(Into::<AllocateBuffersError>::into)?
|
|
||||||
.ok_or(AllocateBuffersError::NoFreeSlots)?;
|
|
||||||
|
|
||||||
// Swap the buffers
|
|
||||||
if let Some(current) = self.buffer.as_mut() {
|
|
||||||
mem::swap(&mut next, current);
|
|
||||||
}
|
|
||||||
|
|
||||||
let window = self.window().ok_or(AllocateBuffersError::WindowDestroyed)?;
|
|
||||||
|
|
||||||
if let Ok(pixmap) = PixmapWrapper::with_dmabuf(
|
|
||||||
&*connection,
|
|
||||||
window.as_ref(),
|
|
||||||
next.userdata().get::<Dmabuf>().unwrap(),
|
|
||||||
) {
|
|
||||||
// Now present the current buffer
|
|
||||||
let _ = pixmap.present(&*connection, window.as_ref());
|
|
||||||
}
|
|
||||||
self.swapchain.submitted(next);
|
|
||||||
|
|
||||||
// Flush the connection after presenting to the window to ensure we don't run out of buffer space in the X11 connection.
|
|
||||||
let _ = connection.flush();
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resets the internal buffers, e.g. to reset age values
|
|
||||||
pub fn reset_buffers(&mut self) {
|
|
||||||
self.swapchain.reset_buffers();
|
|
||||||
self.buffer = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resize(&mut self, size: Size<u16, Logical>) -> Result<(), AllocateBuffersError> {
|
|
||||||
self.swapchain.resize(size.w as u32, size.h as u32);
|
|
||||||
self.buffer = None;
|
|
||||||
|
|
||||||
self.width = size.w;
|
|
||||||
self.height = size.h;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Builder used to construct a window.
|
/// Builder used to construct a window.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct WindowBuilder<'a> {
|
pub struct WindowBuilder<'a> {
|
||||||
|
@ -755,6 +542,16 @@ impl EventSource for X11Backend {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
atom_manager! {
|
||||||
|
pub(crate) Atoms: AtomCollectionCookie {
|
||||||
|
WM_PROTOCOLS,
|
||||||
|
WM_DELETE_WINDOW,
|
||||||
|
_NET_WM_NAME,
|
||||||
|
UTF8_STRING,
|
||||||
|
_SMITHAY_X11_BACKEND_CLOSE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct X11Inner {
|
pub(crate) struct X11Inner {
|
||||||
log: Logger,
|
log: Logger,
|
||||||
|
@ -1032,3 +829,78 @@ impl X11Inner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn egl_init(_: &X11Inner) -> Result<DrmNode, EGLInitError> {
|
||||||
|
let display = EGLDisplay::new(&X11DefaultDisplay, None)?;
|
||||||
|
let device = EGLDevice::device_for_display(&display)?;
|
||||||
|
let path = path_to_type(device.drm_device_path()?, NodeType::Render)?;
|
||||||
|
fcntl::open(&path, OFlag::O_RDWR | OFlag::O_CLOEXEC, Mode::empty())
|
||||||
|
.map_err(Into::<io::Error>::into)
|
||||||
|
.and_then(|fd| {
|
||||||
|
DrmNode::from_fd(fd).map_err(|err| match err {
|
||||||
|
CreateDrmNodeError::Io(err) => err,
|
||||||
|
_ => unreachable!(),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.map_err(EGLInitError::IO)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dri3_init(x11: &X11Inner) -> Result<DrmNode, X11Error> {
|
||||||
|
let connection = &x11.connection;
|
||||||
|
|
||||||
|
// Determine which drm-device the Display is using.
|
||||||
|
let screen = &connection.setup().roots[x11.screen_number];
|
||||||
|
// provider being NONE tells the X server to use the RandR provider.
|
||||||
|
let dri3 = match connection.dri3_open(screen.root, x11rb::NONE)?.reply() {
|
||||||
|
Ok(reply) => reply,
|
||||||
|
Err(err) => {
|
||||||
|
return Err(if let ReplyError::X11Error(ref protocol_error) = err {
|
||||||
|
match protocol_error.error_kind {
|
||||||
|
// Implementation is risen when the renderer is not capable of X server is not capable
|
||||||
|
// of rendering at all.
|
||||||
|
ErrorKind::Implementation => X11Error::CannotDirectRender,
|
||||||
|
// Match may occur when the node cannot be authenticated for the client.
|
||||||
|
ErrorKind::Match => X11Error::CannotDirectRender,
|
||||||
|
_ => err.into(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err.into()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Take ownership of the container's inner value so we do not need to duplicate the fd.
|
||||||
|
// This is fine because the X server will always open a new file descriptor.
|
||||||
|
let drm_device_fd = dri3.device_fd.into_raw_fd();
|
||||||
|
|
||||||
|
let fd_flags =
|
||||||
|
fcntl::fcntl(drm_device_fd.as_raw_fd(), fcntl::F_GETFD).map_err(AllocateBuffersError::from)?;
|
||||||
|
|
||||||
|
// Enable the close-on-exec flag.
|
||||||
|
fcntl::fcntl(
|
||||||
|
drm_device_fd,
|
||||||
|
fcntl::F_SETFD(fcntl::FdFlag::from_bits_truncate(fd_flags) | fcntl::FdFlag::FD_CLOEXEC),
|
||||||
|
)
|
||||||
|
.map_err(AllocateBuffersError::from)?;
|
||||||
|
let drm_node = DrmNode::from_fd(drm_device_fd).map_err(Into::<AllocateBuffersError>::into)?;
|
||||||
|
|
||||||
|
if drm_node.ty() != NodeType::Render && drm_node.has_render() {
|
||||||
|
// Try to get the render node.
|
||||||
|
if let Some(path) = drm_node.dev_path_with_type(NodeType::Render) {
|
||||||
|
return Ok(fcntl::open(&path, OFlag::O_RDWR | OFlag::O_CLOEXEC, Mode::empty())
|
||||||
|
.map_err(Into::<std::io::Error>::into)
|
||||||
|
.map_err(CreateDrmNodeError::Io)
|
||||||
|
.and_then(DrmNode::from_fd)
|
||||||
|
.unwrap_or_else(|err| {
|
||||||
|
slog::warn!(&x11.log, "Could not create render node from existing DRM node ({}), falling back to primary node", err);
|
||||||
|
drm_node
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
slog::warn!(
|
||||||
|
&x11.log,
|
||||||
|
"DRM Device does not have a render node, falling back to primary node"
|
||||||
|
);
|
||||||
|
Ok(drm_node)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,157 @@
|
||||||
|
use std::{
|
||||||
|
mem,
|
||||||
|
sync::{mpsc::Receiver, Arc, Mutex, MutexGuard, Weak},
|
||||||
|
};
|
||||||
|
|
||||||
|
use drm_fourcc::DrmFourcc;
|
||||||
|
use gbm::BufferObject;
|
||||||
|
use x11rb::{connection::Connection, protocol::xproto::PixmapWrapper, rust_connection::RustConnection};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
backend::{
|
||||||
|
allocator::{
|
||||||
|
dmabuf::{AsDmabuf, Dmabuf},
|
||||||
|
Slot, Swapchain,
|
||||||
|
},
|
||||||
|
drm::DrmNode,
|
||||||
|
x11::{buffer::PixmapWrapperExt, window_inner::WindowInner, AllocateBuffersError, Window},
|
||||||
|
},
|
||||||
|
utils::{Logical, Size},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::X11Error;
|
||||||
|
|
||||||
|
/// An error that may occur when presenting.
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum PresentError {
|
||||||
|
/// The dmabuf being presented has too many planes.
|
||||||
|
#[error("The Dmabuf had too many planes")]
|
||||||
|
TooManyPlanes,
|
||||||
|
|
||||||
|
/// Duplicating the dmabuf handles failed.
|
||||||
|
#[error("Duplicating the file descriptors for the dmabuf handles failed")]
|
||||||
|
DupFailed(String),
|
||||||
|
|
||||||
|
/// The format dmabuf presented does not match the format of the window.
|
||||||
|
#[error("Buffer had incorrect format, expected: {0}")]
|
||||||
|
IncorrectFormat(DrmFourcc),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An X11 surface which uses GBM to allocate and present buffers.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct X11Surface {
|
||||||
|
pub(crate) connection: Weak<RustConnection>,
|
||||||
|
pub(crate) window: Weak<WindowInner>,
|
||||||
|
pub(crate) resize: Receiver<Size<u16, Logical>>,
|
||||||
|
pub(crate) swapchain: Swapchain<Arc<Mutex<gbm::Device<DrmNode>>>, BufferObject<()>>,
|
||||||
|
pub(crate) format: DrmFourcc,
|
||||||
|
pub(crate) width: u16,
|
||||||
|
pub(crate) height: u16,
|
||||||
|
pub(crate) buffer: Option<Slot<BufferObject<()>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl X11Surface {
|
||||||
|
/// Returns the window the surface presents to.
|
||||||
|
///
|
||||||
|
/// This will return [`None`] if the window has been destroyed.
|
||||||
|
pub fn window(&self) -> Option<impl AsRef<Window> + '_> {
|
||||||
|
let window = self.window.upgrade().map(Window).map(WindowTemporary);
|
||||||
|
|
||||||
|
struct WindowTemporary(Window);
|
||||||
|
|
||||||
|
impl AsRef<Window> for WindowTemporary {
|
||||||
|
fn as_ref(&self) -> &Window {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a handle to the GBM device used to allocate buffers.
|
||||||
|
pub fn device(&self) -> MutexGuard<'_, gbm::Device<DrmNode>> {
|
||||||
|
self.swapchain.allocator.lock().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the format of the buffers the surface accepts.
|
||||||
|
pub fn format(&self) -> DrmFourcc {
|
||||||
|
self.format
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the next buffer that will be presented to the Window and its age.
|
||||||
|
///
|
||||||
|
/// You may bind this buffer to a renderer to render.
|
||||||
|
/// This function will return the same buffer until [`submit`](Self::submit) is called
|
||||||
|
/// or [`reset_buffers`](Self::reset_buffer) is used to reset the buffers.
|
||||||
|
pub fn buffer(&mut self) -> Result<(Dmabuf, u8), AllocateBuffersError> {
|
||||||
|
if let Some(new_size) = self.resize.try_iter().last() {
|
||||||
|
self.resize(new_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.buffer.is_none() {
|
||||||
|
self.buffer = Some(
|
||||||
|
self.swapchain
|
||||||
|
.acquire()
|
||||||
|
.map_err(Into::<AllocateBuffersError>::into)?
|
||||||
|
.ok_or(AllocateBuffersError::NoFreeSlots)?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let slot = self.buffer.as_ref().unwrap();
|
||||||
|
let age = slot.age();
|
||||||
|
match slot.userdata().get::<Dmabuf>() {
|
||||||
|
Some(dmabuf) => Ok((dmabuf.clone(), age)),
|
||||||
|
None => {
|
||||||
|
let dmabuf = slot.export().map_err(Into::<AllocateBuffersError>::into)?;
|
||||||
|
slot.userdata().insert_if_missing(|| dmabuf.clone());
|
||||||
|
Ok((dmabuf, age))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consume and submit the buffer to the window.
|
||||||
|
pub fn submit(&mut self) -> Result<(), X11Error> {
|
||||||
|
if let Some(connection) = self.connection.upgrade() {
|
||||||
|
// Get a new buffer
|
||||||
|
let mut next = self
|
||||||
|
.swapchain
|
||||||
|
.acquire()
|
||||||
|
.map_err(Into::<AllocateBuffersError>::into)?
|
||||||
|
.ok_or(AllocateBuffersError::NoFreeSlots)?;
|
||||||
|
|
||||||
|
// Swap the buffers
|
||||||
|
if let Some(current) = self.buffer.as_mut() {
|
||||||
|
mem::swap(&mut next, current);
|
||||||
|
}
|
||||||
|
|
||||||
|
let window = self.window().ok_or(AllocateBuffersError::WindowDestroyed)?;
|
||||||
|
let pixmap = PixmapWrapper::with_dmabuf(
|
||||||
|
&*connection,
|
||||||
|
window.as_ref(),
|
||||||
|
next.userdata().get::<Dmabuf>().unwrap(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Now present the current buffer
|
||||||
|
let _ = pixmap.present(&*connection, window.as_ref())?;
|
||||||
|
self.swapchain.submitted(next);
|
||||||
|
|
||||||
|
// Flush the connection after presenting to the window to ensure we don't run out of buffer space in the X11 connection.
|
||||||
|
let _ = connection.flush();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resets the internal buffers, e.g. to reset age values
|
||||||
|
pub fn reset_buffers(&mut self) {
|
||||||
|
self.swapchain.reset_buffers();
|
||||||
|
self.buffer = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resize(&mut self, size: Size<u16, Logical>) {
|
||||||
|
self.swapchain.resize(size.w as u32, size.h as u32);
|
||||||
|
self.buffer = None;
|
||||||
|
|
||||||
|
self.width = size.w;
|
||||||
|
self.height = size.h;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue