The glory of error-chain

This commit is contained in:
Drakulix 2017-09-18 16:58:20 +02:00
parent a04bfcdd11
commit fa6742fb5f
7 changed files with 380 additions and 421 deletions

View File

@ -22,6 +22,7 @@ clippy = { version = "*", optional = true }
rental = "0.4.11" rental = "0.4.11"
wayland-protocols = { version = "0.10.1", features = ["unstable_protocols", "server"] } wayland-protocols = { version = "0.10.1", features = ["unstable_protocols", "server"] }
image = "0.15.0" image = "0.15.0"
error-chain = "0.11.0"
[build-dependencies] [build-dependencies]
gl_generator = "0.5" gl_generator = "0.5"

View File

@ -1,5 +1,5 @@
use super::{CrtcError, DrmError, ModeError};
use super::devices; use super::devices;
use super::error::*;
use backend::graphics::GraphicsBackend; use backend::graphics::GraphicsBackend;
use backend::graphics::egl::{EGLGraphicsBackend, EGLSurface, PixelFormat, SwapBuffersError}; use backend::graphics::egl::{EGLGraphicsBackend, EGLSurface, PixelFormat, SwapBuffersError};
use drm::buffer::Buffer; use drm::buffer::Buffer;
@ -94,7 +94,7 @@ pub(crate) struct DrmBackendInternal {
impl DrmBackendInternal { impl DrmBackendInternal {
pub(crate) fn new<I, L>(context: Rc<devices::Context>, crtc: crtc::Handle, mode: Mode, connectors: I, pub(crate) fn new<I, L>(context: Rc<devices::Context>, crtc: crtc::Handle, mode: Mode, connectors: I,
own_id: usize, logger: L) own_id: usize, logger: L)
-> Result<DrmBackendInternal, DrmError> -> Result<DrmBackendInternal>
where where
I: Into<Vec<connector::Handle>>, I: Into<Vec<connector::Handle>>,
L: Into<Option<::slog::Logger>>, L: Into<Option<::slog::Logger>>,
@ -107,11 +107,12 @@ impl DrmBackendInternal {
// check the connectors, if they suite the mode // check the connectors, if they suite the mode
for connector in connectors.iter() { for connector in connectors.iter() {
if !connector::Info::load_from_device(context.head().head(), *connector)? if !connector::Info::load_from_device(context.head().head(), *connector)
.chain_err(|| ErrorKind::DrmDev(format!("{:?}", context.head().head())))?
.modes() .modes()
.contains(&mode) .contains(&mode)
{ {
return Err(DrmError::Mode(ModeError::ModeNotSuitable)); bail!(ErrorKind::ModeNotSuitable(mode))
} }
} }
@ -122,23 +123,31 @@ impl DrmBackendInternal {
Ok(GbmTypes { Ok(GbmTypes {
cursor: { cursor: {
// Create an unused cursor buffer (we don't want an Option here) // Create an unused cursor buffer (we don't want an Option here)
context.devices.gbm.create_buffer_object( context
1, .devices
1, .gbm
GbmFormat::ARGB8888, .create_buffer_object(
&[BufferObjectFlags::Cursor, BufferObjectFlags::Write], 1,
)? 1,
GbmFormat::ARGB8888,
&[BufferObjectFlags::Cursor, BufferObjectFlags::Write],
)
.chain_err(|| ErrorKind::GbmInitFailed)?
}, },
surface: Surface::try_new( surface: Surface::try_new(
{ {
debug!(log, "Creating GbmSurface"); debug!(log, "Creating GbmSurface");
// create a gbm surface // create a gbm surface
Box::new(context.devices.gbm.create_surface( Box::new(context
w as u32, .devices
h as u32, .gbm
GbmFormat::XRGB8888, .create_surface(
&[BufferObjectFlags::Scanout, BufferObjectFlags::Rendering], w as u32,
)?) h as u32,
GbmFormat::XRGB8888,
&[BufferObjectFlags::Scanout, BufferObjectFlags::Rendering],
)
.chain_err(|| ErrorKind::GbmInitFailed)?)
}, },
|surface| { |surface| {
// create an egl surface from the gbm one // create an egl surface from the gbm one
@ -147,15 +156,24 @@ impl DrmBackendInternal {
// make it active for the first `crtc::set` // make it active for the first `crtc::set`
// (which is needed before the first page_flip) // (which is needed before the first page_flip)
unsafe { egl_surface.make_current()? }; unsafe {
egl_surface.swap_buffers()?; egl_surface
.make_current()
.chain_err(|| ErrorKind::FailedToSwap)?
};
egl_surface
.swap_buffers()
.chain_err(|| ErrorKind::FailedToSwap)?;
// init the first screen // init the first screen
// (must be done before calling page_flip for the first time) // (must be done before calling page_flip for the first time)
let mut front_bo = surface.lock_front_buffer()?; let mut front_bo = surface
.lock_front_buffer()
.chain_err(|| ErrorKind::FailedToSwap)?;
debug!(log, "FrontBuffer color format: {:?}", front_bo.format()); debug!(log, "FrontBuffer color format: {:?}", front_bo.format());
// we need a framebuffer per front buffer // we need a framebuffer per front buffer
let fb = framebuffer::create(context.devices.drm, &*front_bo)?; let fb = framebuffer::create(context.devices.drm, &*front_bo)
.chain_err(|| ErrorKind::DrmDev(format!("{:?}", context.devices.drm)))?;
debug!(log, "Initialize screen"); debug!(log, "Initialize screen");
crtc::set( crtc::set(
@ -165,6 +183,8 @@ impl DrmBackendInternal {
&connectors, &connectors,
(0, 0), (0, 0),
Some(mode), Some(mode),
).chain_err(
|| ErrorKind::DrmDev(format!("{:?}", context.devices.drm)),
)?; )?;
front_bo.set_userdata(fb); front_bo.set_userdata(fb);
@ -176,7 +196,7 @@ impl DrmBackendInternal {
}, },
}) })
}, },
).map_err(DrmError::from)?, ).map_err(Error::from)?,
}) })
})?, })?,
crtc, crtc,
@ -216,10 +236,15 @@ impl DrmBackend {
/// # Errors /// # Errors
/// ///
/// Errors if the new connector does not support the currently set `Mode` /// Errors if the new connector does not support the currently set `Mode`
pub fn add_connector(&mut self, connector: connector::Handle) -> Result<(), DrmError> { pub fn add_connector(&mut self, connector: connector::Handle) -> Result<()> {
let info = let info =
connector::Info::load_from_device(self.0.borrow().graphics.head().head().head(), connector) connector::Info::load_from_device(self.0.borrow().graphics.head().head().head(), connector)
.map_err(|err| ModeError::FailedToLoad(err))?; .chain_err(|| {
ErrorKind::DrmDev(format!(
"{:?}",
self.0.borrow().graphics.head().head().head()
))
})?;
// check if the connector can handle the current mode // check if the connector can handle the current mode
let mut internal = self.0.borrow_mut(); let mut internal = self.0.borrow_mut();
@ -229,16 +254,21 @@ impl DrmBackend {
.iter() .iter()
.map(|encoder| { .map(|encoder| {
encoder::Info::load_from_device(self.0.borrow().graphics.head().head().head(), *encoder) encoder::Info::load_from_device(self.0.borrow().graphics.head().head().head(), *encoder)
.map_err(DrmError::from) .chain_err(|| {
ErrorKind::DrmDev(format!(
"{:?}",
self.0.borrow().graphics.head().head().head()
))
})
}) })
.collect::<Result<Vec<encoder::Info>, DrmError>>()?; .collect::<Result<Vec<encoder::Info>>>()?;
// and if any encoder supports the selected crtc // and if any encoder supports the selected crtc
if !encoders if !encoders
.iter() .iter()
.any(|encoder| encoder.supports_crtc(self.0.borrow().crtc)) .any(|encoder| encoder.supports_crtc(self.0.borrow().crtc))
{ {
return Err(DrmError::Crtc(CrtcError::NoSuitableEncoder)); bail!(ErrorKind::NoSuitableEncoder(info, self.0.borrow().crtc));
} }
info!( info!(
@ -249,7 +279,7 @@ impl DrmBackend {
internal.connectors.push(connector); internal.connectors.push(connector);
Ok(()) Ok(())
} else { } else {
Err(DrmError::Mode(ModeError::ModeNotSuitable)) bail!(ErrorKind::ModeNotSuitable(self.0.borrow().mode))
} }
} }
@ -270,7 +300,7 @@ impl DrmBackend {
info.connector_type() info.connector_type()
); );
} else { } else {
info!(self.0.borrow().logger, "Removeing unknown connector"); info!(self.0.borrow().logger, "Removing unknown connector");
} }
self.0.borrow_mut().connectors.retain(|x| *x != connector); self.0.borrow_mut().connectors.retain(|x| *x != connector);
@ -283,14 +313,20 @@ impl DrmBackend {
/// This will fail if not all set connectors support the new `Mode`. /// This will fail if not all set connectors support the new `Mode`.
/// Several internal resources will need to be recreated to fit the new `Mode`. /// Several internal resources will need to be recreated to fit the new `Mode`.
/// Other errors might occur. /// Other errors might occur.
pub fn use_mode(&mut self, mode: Mode) -> Result<(), DrmError> { pub fn use_mode(&mut self, mode: Mode) -> Result<()> {
// check the connectors // check the connectors
for connector in self.0.borrow().connectors.iter() { for connector in self.0.borrow().connectors.iter() {
if !connector::Info::load_from_device(self.0.borrow().graphics.head().head().head(), *connector)? if !connector::Info::load_from_device(self.0.borrow().graphics.head().head().head(), *connector)
.chain_err(|| {
ErrorKind::DrmDev(format!(
"{:?}",
self.0.borrow().graphics.head().head().head()
))
})?
.modes() .modes()
.contains(&mode) .contains(&mode)
{ {
return Err(DrmError::Mode(ModeError::ModeNotSuitable)); bail!(ErrorKind::ModeNotSuitable(mode));
} }
} }
@ -303,61 +339,76 @@ impl DrmBackend {
let (w, h) = mode.size(); let (w, h) = mode.size();
internal internal.graphics.rent_all_mut(|graphics| -> Result<()> {
.graphics // Recreate the surface and the related resources to match the new
.rent_all_mut(|graphics| -> Result<(), DrmError> { // resolution.
// Recreate the surface and the related resources to match the new debug!(logger, "Reinitializing surface for new mode: {}:{}", w, h);
// resolution. graphics.gbm.surface = Surface::try_new(
debug!(logger, "Reinitializing surface for new mode: {}:{}", w, h); {
graphics.gbm.surface = Surface::try_new( // create a new gbm surface
{ debug!(logger, "Creating GbmSurface");
// create a new gbm surface Box::new(graphics
debug!(logger, "Creating GbmSurface"); .context
Box::new(graphics.context.devices.gbm.create_surface( .devices
.gbm
.create_surface(
w as u32, w as u32,
h as u32, h as u32,
GbmFormat::XRGB8888, GbmFormat::XRGB8888,
&[BufferObjectFlags::Scanout, BufferObjectFlags::Rendering], &[BufferObjectFlags::Scanout, BufferObjectFlags::Rendering],
)?) )
}, .chain_err(|| ErrorKind::GbmInitFailed)?)
|surface| { },
// create an egl surface from the gbm one |surface| {
debug!(logger, "Creating EGLSurface"); // create an egl surface from the gbm one
let egl_surface = graphics.context.egl.create_surface(&surface)?; debug!(logger, "Creating EGLSurface");
let egl_surface = graphics.context.egl.create_surface(&surface)?;
// make it active for the first `crtc::set` // make it active for the first `crtc::set`
// (which is needed before the first page_flip) // (which is needed before the first page_flip)
unsafe { egl_surface.make_current()? }; unsafe {
egl_surface.swap_buffers()?; egl_surface
.make_current()
.chain_err(|| ErrorKind::FailedToSwap)?
};
egl_surface
.swap_buffers()
.chain_err(|| ErrorKind::FailedToSwap)?;
let mut front_bo = surface.lock_front_buffer()?; let mut front_bo = surface
debug!(logger, "FrontBuffer color format: {:?}", front_bo.format()); .lock_front_buffer()
// we need a framebuffer per front_buffer .chain_err(|| ErrorKind::FailedToSwap)?;
let fb = framebuffer::create(graphics.context.devices.drm, &*front_bo)?; debug!(logger, "FrontBuffer color format: {:?}", front_bo.format());
// we need a framebuffer per front_buffer
let fb = framebuffer::create(graphics.context.devices.drm, &*front_bo).chain_err(|| {
ErrorKind::DrmDev(format!("{:?}", graphics.context.devices.drm))
})?;
debug!(logger, "Initialize screen"); debug!(logger, "Initialize screen");
crtc::set( crtc::set(
graphics.context.devices.drm, graphics.context.devices.drm,
crtc, crtc,
fb.handle(), fb.handle(),
&connectors, &connectors,
(0, 0), (0, 0),
Some(mode), Some(mode),
)?; ).chain_err(|| {
front_bo.set_userdata(fb); ErrorKind::DrmDev(format!("{:?}", graphics.context.devices.drm))
})?;
front_bo.set_userdata(fb);
Ok(EGL { Ok(EGL {
surface: egl_surface, surface: egl_surface,
buffers: GbmBuffers { buffers: GbmBuffers {
front_buffer: Cell::new(front_bo), front_buffer: Cell::new(front_bo),
next_buffer: Cell::new(None), next_buffer: Cell::new(None),
}, },
}) })
}, },
)?; )?;
Ok(()) Ok(())
})?; })?;
info!(logger, "Setting new mode: {:?}", mode.name()); info!(logger, "Setting new mode: {:?}", mode.name());
internal.mode = mode; internal.mode = mode;
@ -375,34 +426,46 @@ impl DrmBackend {
impl GraphicsBackend for DrmBackend { impl GraphicsBackend for DrmBackend {
type CursorFormat = ImageBuffer<Rgba<u8>, Vec<u8>>; type CursorFormat = ImageBuffer<Rgba<u8>, Vec<u8>>;
type Error = DrmError; type Error = Error;
fn set_cursor_position(&self, x: u32, y: u32) -> Result<(), DrmError> { fn set_cursor_position(&self, x: u32, y: u32) -> Result<()> {
trace!(self.0.borrow().logger, "Move the cursor to {},{}", x, y); trace!(self.0.borrow().logger, "Move the cursor to {},{}", x, y);
crtc::move_cursor( crtc::move_cursor(
self.0.borrow().graphics.head().head().head(), self.0.borrow().graphics.head().head().head(),
self.0.borrow().crtc, self.0.borrow().crtc,
(x as i32, y as i32), (x as i32, y as i32),
).map_err(DrmError::from) ).chain_err(|| {
ErrorKind::DrmDev(format!(
"{:?}",
self.0.borrow().graphics.head().head().head()
))
})
} }
fn set_cursor_representation(&self, buffer: ImageBuffer<Rgba<u8>, Vec<u8>>, hotspot: (u32, u32)) fn set_cursor_representation(&self, buffer: ImageBuffer<Rgba<u8>, Vec<u8>>, hotspot: (u32, u32))
-> Result<(), DrmError> { -> Result<()> {
let (w, h) = buffer.dimensions(); let (w, h) = buffer.dimensions();
debug!(self.0.borrow().logger, "Importing cursor"); debug!(self.0.borrow().logger, "Importing cursor");
/// import the cursor into a buffer we can render /// import the cursor into a buffer we can render
self.0 self.0
.borrow_mut() .borrow_mut()
.graphics .graphics
.rent_all_mut(|graphics| -> Result<(), DrmError> { .rent_all_mut(|graphics| -> Result<()> {
graphics.gbm.cursor = { graphics.gbm.cursor = {
let mut cursor = graphics.context.devices.gbm.create_buffer_object( let mut cursor = graphics
w, .context
h, .devices
GbmFormat::ARGB8888, .gbm
&[BufferObjectFlags::Cursor, BufferObjectFlags::Write], .create_buffer_object(
)?; w,
cursor.write(&*buffer.into_raw())?; h,
GbmFormat::ARGB8888,
&[BufferObjectFlags::Cursor, BufferObjectFlags::Write],
)
.chain_err(|| ErrorKind::GbmInitFailed)?;
cursor
.write(&*buffer.into_raw())
.chain_err(|| ErrorKind::GbmInitFailed)?;
cursor cursor
}; };
Ok(()) Ok(())
@ -429,7 +492,12 @@ impl GraphicsBackend for DrmBackend {
.graphics .graphics
.rent(|gbm| Buffer::handle(&gbm.cursor)), .rent(|gbm| Buffer::handle(&gbm.cursor)),
(w, h), (w, h),
).map_err(DrmError::from) ).chain_err(|| {
ErrorKind::DrmDev(format!(
"{:?}",
self.0.borrow().graphics.head().head().head()
))
})
} else { } else {
Ok(()) Ok(())
} }
@ -437,7 +505,7 @@ impl GraphicsBackend for DrmBackend {
} }
impl EGLGraphicsBackend for DrmBackend { impl EGLGraphicsBackend for DrmBackend {
fn swap_buffers(&self) -> Result<(), SwapBuffersError> { fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
self.0.borrow().graphics.rent_all(|graphics| { self.0.borrow().graphics.rent_all(|graphics| {
// We cannot call lock_front_buffer anymore without releasing the previous buffer, which will happen when the page flip is done // We cannot call lock_front_buffer anymore without releasing the previous buffer, which will happen when the page flip is done
if graphics.gbm.surface.rent(|egl| { if graphics.gbm.surface.rent(|egl| {
@ -503,7 +571,7 @@ impl EGLGraphicsBackend for DrmBackend {
.rent(|context| context.is_current()) .rent(|context| context.is_current())
} }
unsafe fn make_current(&self) -> Result<(), SwapBuffersError> { unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
self.0 self.0
.borrow() .borrow()
.graphics .graphics

View File

@ -1,110 +1,52 @@
use backend::graphics::egl::{CreationError, SwapBuffersError}; //!
use drm::result::Error as DrmError; //! Errors thrown by the `DrmDevice` and `DrmBackend`
use gbm::FrontBufferError; //!
use backend::graphics::egl;
use drm::control::{connector, crtc, Mode};
use rental::TryNewError; use rental::TryNewError;
use std::error::{self, Error as ErrorTrait}; error_chain! {
use std::fmt; errors {
use std::io::Error as IoError; #[doc = "The `DrmDevice` encountered an access error"]
DrmDev(dev: String) {
description("The drm device encountered an access error"),
display("The drm device ({:?}) encountered an access error", dev),
}
/// Error summing up error types related to all underlying libraries #[doc = "Creation of gbm resource failed"]
/// involved in creating the a `DrmDevice`/`DrmBackend` GbmInitFailed {
#[derive(Debug)] description("Creation of gbm resource failed"),
pub enum Error { display("Creation of gbm resource failed"),
/// The `DrmDevice` has encountered an error on an ioctl }
Drm(DrmError),
/// The `EGLContext` could not be created
EGLCreation(CreationError),
/// Swapping Buffers via EGL was not possible
EGLSwap(SwapBuffersError),
/// Locking the front buffer of the underlying `GbmSurface` failed
Gbm(FrontBufferError),
/// A generic IO-Error happened accessing the underlying devices
Io(IoError),
/// Selected an invalid Mode
Mode(ModeError),
/// Error related to the selected crtc
Crtc(CrtcError),
}
impl fmt::Display for Error { #[doc = "Swapping front buffers failed"]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { FailedToSwap {
write!( description("Swapping front buffers failed"),
f, display("Swapping front buffers failed"),
"DrmBackend error: {}", }
match self {
&Error::Drm(ref x) => x as &error::Error,
&Error::EGLCreation(ref x) => x as &error::Error,
&Error::EGLSwap(ref x) => x as &error::Error,
&Error::Gbm(ref x) => x as &error::Error,
&Error::Io(ref x) => x as &error::Error,
&Error::Mode(ref x) => x as &error::Error,
&Error::Crtc(ref x) => x as &error::Error,
}
)
}
}
impl error::Error for Error { #[doc = "mode is not compatible with all given connectors"]
fn description(&self) -> &str { ModeNotSuitable(mode: Mode) {
"DrmBackend error" description("Mode is not compatible with all given connectors"),
} display("Mode ({:?}) is not compatible with all given connectors", mode),
}
fn cause(&self) -> Option<&error::Error> { #[doc = "The given crtc is already in use by another backend"]
match self { CrtcAlreadyInUse(crtc: crtc::Handle) {
&Error::Drm(ref x) => Some(x as &error::Error), description("The given crtc is already in use by another backend"),
&Error::EGLCreation(ref x) => Some(x as &error::Error), display("The given crtc ({:?}) is already in use by another backend", crtc),
&Error::EGLSwap(ref x) => Some(x as &error::Error), }
&Error::Gbm(ref x) => Some(x as &error::Error),
&Error::Io(ref x) => Some(x as &error::Error), #[doc = "No encoder was found for a given connector on the set crtc"]
&Error::Mode(ref x) => Some(x as &error::Error), NoSuitableEncoder(connector: connector::Info, crtc: crtc::Handle) {
&Error::Crtc(ref x) => Some(x as &error::Error), description("No encoder found for given connector on set crtc"),
display("No encoder found for the given connector '{:?}' on the set crtc ({:?})", connector.connector_type(), crtc),
} }
} }
}
impl From<DrmError> for Error { links {
fn from(err: DrmError) -> Error { EGL(egl::Error, egl::ErrorKind) #[doc = "EGL error"];
Error::Drm(err)
}
}
impl From<CreationError> for Error {
fn from(err: CreationError) -> Error {
Error::EGLCreation(err)
}
}
impl From<SwapBuffersError> for Error {
fn from(err: SwapBuffersError) -> Error {
Error::EGLSwap(err)
}
}
impl From<FrontBufferError> for Error {
fn from(err: FrontBufferError) -> Error {
Error::Gbm(err)
}
}
impl From<IoError> for Error {
fn from(err: IoError) -> Error {
Error::Io(err)
}
}
impl From<ModeError> for Error {
fn from(err: ModeError) -> Error {
match err {
err @ ModeError::ModeNotSuitable => Error::Mode(err),
ModeError::FailedToLoad(err) => Error::Drm(err),
}
}
}
impl From<CrtcError> for Error {
fn from(err: CrtcError) -> Error {
Error::Crtc(err)
} }
} }
@ -113,72 +55,3 @@ impl<H> From<TryNewError<Error, H>> for Error {
err.0 err.0
} }
} }
/// Error when trying to select an invalid mode
#[derive(Debug)]
pub enum ModeError {
/// `Mode` is not compatible with all given connectors
ModeNotSuitable,
/// Failed to load `Mode` information
FailedToLoad(DrmError),
}
impl fmt::Display for ModeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.description())?;
if let Some(cause) = self.cause() {
write!(f, "\tCause: {}", cause)?;
}
Ok(())
}
}
impl error::Error for ModeError {
fn description(&self) -> &str {
match self {
&ModeError::ModeNotSuitable => "Mode does not match all attached connectors",
&ModeError::FailedToLoad(_) => "Failed to load mode information from device",
}
}
fn cause(&self) -> Option<&error::Error> {
match self {
&ModeError::FailedToLoad(ref err) => Some(err as &error::Error),
_ => None,
}
}
}
/// Errors related to the selected crtc
#[derive(Debug)]
pub enum CrtcError {
/// Selected crtc is already in use by another `DrmBackend`
AlreadyInUse,
/// For the selected crtc no encoder exists that supports all connectors
NoSuitableEncoder,
}
impl fmt::Display for CrtcError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.description())?;
if let Some(cause) = self.cause() {
write!(f, "\tCause: {}", cause)?;
}
Ok(())
}
}
impl error::Error for CrtcError {
fn description(&self) -> &str {
match self {
&CrtcError::AlreadyInUse => "Crtc is already in use by another DrmBackend",
&CrtcError::NoSuitableEncoder => "Crtc has no supported encoder that can drive all connectors",
}
}
fn cause(&self) -> Option<&error::Error> {
match self {
_ => None,
}
}
}

View File

@ -205,11 +205,11 @@ use wayland_server::EventLoopHandle;
use wayland_server::sources::{FdEventSourceHandler, FdInterest}; use wayland_server::sources::{FdEventSourceHandler, FdInterest};
mod backend; mod backend;
mod error; pub mod error;
pub use self::backend::{DrmBackend, Id}; pub use self::backend::{DrmBackend, Id};
use self::backend::DrmBackendInternal; use self::backend::DrmBackendInternal;
pub use self::error::{CrtcError, Error as DrmError, ModeError}; use self::error::*;
/// Internal struct as required by the drm crate /// Internal struct as required by the drm crate
#[derive(Debug)] #[derive(Debug)]
@ -275,7 +275,7 @@ impl<H: DrmHandler + 'static> DrmDevice<H> {
/// # Safety /// # Safety
/// The file descriptor might not be valid and needs to be owned by smithay, /// The file descriptor might not be valid and needs to be owned by smithay,
/// make sure not to share it. Otherwise undefined behavior might occur. /// make sure not to share it. Otherwise undefined behavior might occur.
pub unsafe fn new_from_fd<L>(fd: RawFd, logger: L) -> Result<Self, DrmError> pub unsafe fn new_from_fd<L>(fd: RawFd, logger: L) -> Result<Self>
where where
L: Into<Option<::slog::Logger>>, L: Into<Option<::slog::Logger>>,
{ {
@ -299,8 +299,7 @@ impl<H: DrmHandler + 'static> DrmDevice<H> {
/// # Safety /// # Safety
/// The file descriptor might not be valid and needs to be owned by smithay, /// The file descriptor might not be valid and needs to be owned by smithay,
/// make sure not to share it. Otherwise undefined behavior might occur. /// make sure not to share it. Otherwise undefined behavior might occur.
pub unsafe fn new_from_fd_with_gl_attr<L>(fd: RawFd, attributes: GlAttributes, logger: L) pub unsafe fn new_from_fd_with_gl_attr<L>(fd: RawFd, attributes: GlAttributes, logger: L) -> Result<Self>
-> Result<Self, DrmError>
where where
L: Into<Option<::slog::Logger>>, L: Into<Option<::slog::Logger>>,
{ {
@ -311,7 +310,7 @@ impl<H: DrmHandler + 'static> DrmDevice<H> {
/// ///
/// Returns an error if the file is no valid drm node or context creation was not /// Returns an error if the file is no valid drm node or context creation was not
/// successful. /// successful.
pub fn new_from_file<L>(file: File, logger: L) -> Result<Self, DrmError> pub fn new_from_file<L>(file: File, logger: L) -> Result<Self>
where where
L: Into<Option<::slog::Logger>>, L: Into<Option<::slog::Logger>>,
{ {
@ -331,15 +330,14 @@ impl<H: DrmHandler + 'static> DrmDevice<H> {
/// ///
/// Returns an error if the file is no valid drm node or context creation was not /// Returns an error if the file is no valid drm node or context creation was not
/// successful. /// successful.
pub fn new_from_file_with_gl_attr<L>(file: File, attributes: GlAttributes, logger: L) pub fn new_from_file_with_gl_attr<L>(file: File, attributes: GlAttributes, logger: L) -> Result<Self>
-> Result<Self, DrmError>
where where
L: Into<Option<::slog::Logger>>, L: Into<Option<::slog::Logger>>,
{ {
DrmDevice::new(DrmDev::new_from_file(file), attributes, logger) DrmDevice::new(DrmDev::new_from_file(file), attributes, logger)
} }
fn new<L>(drm: DrmDev, attributes: GlAttributes, logger: L) -> Result<Self, DrmError> fn new<L>(drm: DrmDev, attributes: GlAttributes, logger: L) -> Result<Self>
where where
L: Into<Option<::slog::Logger>>, L: Into<Option<::slog::Logger>>,
{ {
@ -365,7 +363,7 @@ impl<H: DrmHandler + 'static> DrmDevice<H> {
context: Rc::new(Context::try_new( context: Rc::new(Context::try_new(
Box::new(Devices::try_new(Box::new(drm), |drm| { Box::new(Devices::try_new(Box::new(drm), |drm| {
debug!(log, "Creating gbm device"); debug!(log, "Creating gbm device");
GbmDevice::new_from_drm::<DrmDevice<H>>(drm).map_err(DrmError::from) GbmDevice::new_from_drm::<DrmDevice<H>>(drm).chain_err(|| ErrorKind::GbmInitFailed)
})?), })?),
|devices| { |devices| {
debug!(log, "Creating egl context from gbm device"); debug!(log, "Creating egl context from gbm device");
@ -379,7 +377,7 @@ impl<H: DrmHandler + 'static> DrmDevice<H> {
..Default::default() ..Default::default()
}, },
log.clone(), log.clone(),
).map_err(DrmError::from) ).map_err(Error::from)
}, },
)?), )?),
backends: Vec::new(), backends: Vec::new(),
@ -393,15 +391,14 @@ impl<H: DrmHandler + 'static> DrmDevice<H> {
/// ///
/// Errors if initialization fails or the mode is not available on all given /// Errors if initialization fails or the mode is not available on all given
/// connectors. /// connectors.
pub fn create_backend<I>(&mut self, crtc: crtc::Handle, mode: Mode, connectors: I) pub fn create_backend<I>(&mut self, crtc: crtc::Handle, mode: Mode, connectors: I) -> Result<DrmBackend>
-> Result<DrmBackend, DrmError>
where where
I: Into<Vec<connector::Handle>>, I: Into<Vec<connector::Handle>>,
{ {
for backend in self.backends.iter() { for backend in self.backends.iter() {
if let Some(backend) = backend.upgrade() { if let Some(backend) = backend.upgrade() {
if backend.borrow().is_crtc(crtc) { if backend.borrow().is_crtc(crtc) {
return Err(DrmError::Crtc(CrtcError::AlreadyInUse)); bail!(ErrorKind::CrtcAlreadyInUse(crtc))
} }
} }
} }
@ -411,21 +408,25 @@ impl<H: DrmHandler + 'static> DrmDevice<H> {
// check if we have an encoder for every connector // check if we have an encoder for every connector
for connector in connectors.iter() { for connector in connectors.iter() {
let con_info = connector::Info::load_from_device(self.context.head().head(), *connector)?; let con_info = connector::Info::load_from_device(self.context.head().head(), *connector)
.chain_err(|| {
ErrorKind::DrmDev(format!("{:?}", self.context.head().head()))
})?;
// then check for every connector which encoders it does support // then check for every connector which encoders it does support
let encoders = con_info let encoders = con_info
.encoders() .encoders()
.iter() .iter()
.map(|encoder| { .map(|encoder| {
encoder::Info::load_from_device(self.context.head().head(), *encoder) encoder::Info::load_from_device(self.context.head().head(), *encoder).chain_err(|| {
.map_err(DrmError::from) ErrorKind::DrmDev(format!("{:?}", self.context.head().head()))
})
}) })
.collect::<Result<Vec<encoder::Info>, DrmError>>()?; .collect::<Result<Vec<encoder::Info>>>()?;
// and if any encoder supports the selected crtc // and if any encoder supports the selected crtc
if !encoders.iter().any(|encoder| encoder.supports_crtc(crtc)) { if !encoders.iter().any(|encoder| encoder.supports_crtc(crtc)) {
return Err(DrmError::Crtc(CrtcError::NoSuitableEncoder)); bail!(ErrorKind::NoSuitableEncoder(con_info, crtc))
} }
} }

View File

@ -10,11 +10,11 @@ use super::GraphicsBackend;
use gbm::{AsRaw, Device as GbmDevice, Surface as GbmSurface}; use gbm::{AsRaw, Device as GbmDevice, Surface as GbmSurface};
use libloading::Library; use libloading::Library;
use nix::{c_int, c_void}; use nix::{c_int, c_void};
use rental::TryNewError;
use slog; use slog;
use std::error::{self, Error}; use std::error;
use std::ffi::{CStr, CString}; use std::ffi::{CStr, CString};
use std::fmt; use std::fmt;
use std::io;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::mem; use std::mem;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
@ -74,7 +74,7 @@ pub enum NativeSurfacePtr {
} }
/// Enumerates all supported backends /// Enumerates all supported backends
#[derive(Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum NativeType { pub enum NativeType {
/// X11 window & surface /// X11 window & surface
X11, X11,
@ -82,66 +82,77 @@ pub enum NativeType {
Wayland, Wayland,
/// Gbm surface /// Gbm surface
Gbm, Gbm,
/// Unknown
Unknown,
} }
/// Error that can happen while creating an `EGLContext` or `EGLSurface` error_chain! {
#[derive(Debug)] errors {
pub enum CreationError { #[doc = "The requested OpenGL version is not supported"]
/// I/O error from the underlying system OpenGlVersionNotSupported(version: (u8, u8)) {
IoError(io::Error), description("The requested OpenGL version is not supported."),
/// Operating System error display("The requested OpenGL version {:?} is not supported.", version),
OsError(String), }
/// The requested OpenGl version is not supported by the graphics system
OpenGlVersionNotSupported,
/// There is no pixel format available that fulfills all requirements
NoAvailablePixelFormat,
/// Surface creation from an unsupport combination
///
/// E.g creating a surface from an X11 window on a context created from a wayland display
NonMatchingSurfaceType,
/// Context creation is not supported on this system
NotSupported,
}
impl From<io::Error> for CreationError { #[doc = "The EGL implementation does not support creating OpenGL ES contexts"]
fn from(err: io::Error) -> Self { OpenGlesNotSupported {
CreationError::IoError(err) description("The EGL implementation does not support creating OpenGL ES contexts")
}
#[doc = "No available pixel format matched the criteria"]
NoAvailablePixelFormat {
description("No available pixel format matched the criteria.")
}
#[doc = "Surface type does not match the context type"]
NonMatchingSurfaceType(context: NativeType, surface: NativeType) {
description("Surface type does not match the context type."),
display("Surface type '{:?}' does not match the context type '{:?}'.", surface, context),
}
#[doc = "Context creation is not supported on the current window system"]
NotSupported {
description("Context creation is not supported on the current window system.")
}
#[doc = "Loading libEGL failed"]
LoadingEGLFailed {
description("Loading libEGL failed"),
}
#[doc = "EGL was unable to optain a valid EGL Display"]
DisplayNotSupported {
description("EGL was unable to optain a valid EGL Display")
}
#[doc = "eglInitialize returned an error"]
InitFailed {
description("Failed to initialize EGL")
}
#[doc = "Failed to configure the EGL context"]
ConfigFailed {
description("Failed to configure the EGL context")
}
#[doc = "Context creation failed as one or more requirements could not be met. Try removing some gl attributes or pixel format requirements"]
CreationFailed {
description("Context creation failed as one or more requirements could not be met. Try removing some gl attributes or pixel format requirements")
}
#[doc = "eglCreateWindowSurface failed"]
SurfaceCreationFailed {
description("Failed to create a new EGLSurface")
}
#[doc = "The reason of failure could not be determined"]
Unknown(err_no: u32)
} }
} }
impl fmt::Display for CreationError { impl<H> From<TryNewError<Error, H>> for Error {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn from(err: TryNewError<Error, H>) -> Error {
formatter.write_str(self.description())?; err.0
if let Some(err) = error::Error::cause(self) {
write!(formatter, ": {}", err)?;
}
Ok(())
}
}
impl error::Error for CreationError {
fn description(&self) -> &str {
match *self {
CreationError::IoError(ref err) => err.description(),
CreationError::OsError(ref text) => text,
CreationError::OpenGlVersionNotSupported => {
"The requested OpenGL version is not \
supported."
}
CreationError::NoAvailablePixelFormat => {
"Couldn't find any pixel format that matches \
the criterias."
}
CreationError::NonMatchingSurfaceType => "Surface type does not match the context type.",
CreationError::NotSupported => "Context creation is not supported on the current window system",
}
}
fn cause(&self) -> Option<&error::Error> {
match *self {
CreationError::IoError(ref err) => Some(err),
_ => None,
}
} }
} }
@ -155,20 +166,22 @@ pub unsafe trait NativeSurface {
type Keep: 'static; type Keep: 'static;
/// Return a surface for the given type if possible /// Return a surface for the given type if possible
fn surface(&self, backend: NativeType) -> Result<(NativeSurfacePtr, Self::Keep), CreationError>; fn surface(&self, backend: NativeType) -> Result<(NativeSurfacePtr, Self::Keep)>;
} }
#[cfg(feature = "backend_winit")] #[cfg(feature = "backend_winit")]
unsafe impl NativeSurface for WinitWindow { unsafe impl NativeSurface for WinitWindow {
type Keep = Option<wegl::WlEglSurface>; type Keep = Option<wegl::WlEglSurface>;
fn surface(&self, backend_type: NativeType) fn surface(&self, backend_type: NativeType) -> Result<(NativeSurfacePtr, Option<wegl::WlEglSurface>)> {
-> Result<(NativeSurfacePtr, Option<wegl::WlEglSurface>), CreationError> {
match backend_type { match backend_type {
NativeType::X11 => if let Some(window) = self.get_xlib_window() { NativeType::X11 => if let Some(window) = self.get_xlib_window() {
Ok((NativeSurfacePtr::X11(window), None)) Ok((NativeSurfacePtr::X11(window), None))
} else { } else {
return Err(CreationError::NonMatchingSurfaceType); bail!(ErrorKind::NonMatchingSurfaceType(
NativeType::Wayland,
NativeType::X11
));
}, },
NativeType::Wayland => if let Some(surface) = self.get_wayland_surface() { NativeType::Wayland => if let Some(surface) = self.get_wayland_surface() {
let (w, h) = self.get_inner_size().unwrap(); let (w, h) = self.get_inner_size().unwrap();
@ -179,9 +192,12 @@ unsafe impl NativeSurface for WinitWindow {
Some(egl_surface), Some(egl_surface),
)) ))
} else { } else {
return Err(CreationError::NonMatchingSurfaceType); bail!(ErrorKind::NonMatchingSurfaceType(
NativeType::X11,
NativeType::Wayland
));
}, },
_ => Err(CreationError::NonMatchingSurfaceType), x => bail!(ErrorKind::NonMatchingSurfaceType(NativeType::Unknown, x)),
} }
} }
} }
@ -190,18 +206,18 @@ unsafe impl NativeSurface for WinitWindow {
unsafe impl<'a, T: 'static> NativeSurface for GbmSurface<'a, T> { unsafe impl<'a, T: 'static> NativeSurface for GbmSurface<'a, T> {
type Keep = (); type Keep = ();
fn surface(&self, backend: NativeType) -> Result<(NativeSurfacePtr, Self::Keep), CreationError> { fn surface(&self, backend: NativeType) -> Result<(NativeSurfacePtr, Self::Keep)> {
match backend { match backend {
NativeType::Gbm => Ok((NativeSurfacePtr::Gbm(self.as_raw() as *const _), ())), NativeType::Gbm => Ok((NativeSurfacePtr::Gbm(self.as_raw() as *const _), ())),
_ => Err(CreationError::NonMatchingSurfaceType), x => bail!(ErrorKind::NonMatchingSurfaceType(NativeType::Gbm, x)),
} }
} }
} }
unsafe impl NativeSurface for () { unsafe impl NativeSurface for () {
type Keep = (); type Keep = ();
fn surface(&self, _backend: NativeType) -> Result<(NativeSurfacePtr, ()), CreationError> { fn surface(&self, _backend: NativeType) -> Result<(NativeSurfacePtr, ())> {
Err(CreationError::NotSupported) bail!(ErrorKind::NotSupported)
} }
} }
@ -225,7 +241,7 @@ impl<'a> EGLContext<'a, ()> {
#[cfg(feature = "backend_winit")] #[cfg(feature = "backend_winit")]
pub fn new_from_winit<L>(window: &'a WinitWindow, attributes: GlAttributes, pub fn new_from_winit<L>(window: &'a WinitWindow, attributes: GlAttributes,
reqs: PixelFormatRequirements, logger: L) reqs: PixelFormatRequirements, logger: L)
-> Result<EGLContext<'a, WinitWindow>, CreationError> -> Result<EGLContext<'a, WinitWindow>>
where where
L: Into<Option<::slog::Logger>>, L: Into<Option<::slog::Logger>>,
{ {
@ -242,7 +258,7 @@ impl<'a> EGLContext<'a, ()> {
NativeDisplayPtr::Wayland(display) NativeDisplayPtr::Wayland(display)
} else { } else {
error!(log, "Window is backed by an unsupported graphics framework"); error!(log, "Window is backed by an unsupported graphics framework");
return Err(CreationError::NotSupported); bail!(ErrorKind::NotSupported)
}, },
attributes, attributes,
reqs, reqs,
@ -255,7 +271,7 @@ impl<'a> EGLContext<'a, ()> {
#[cfg(feature = "backend_drm")] #[cfg(feature = "backend_drm")]
pub fn new_from_gbm<L, U: 'static>(gbm: &'a GbmDevice<'a>, attributes: GlAttributes, pub fn new_from_gbm<L, U: 'static>(gbm: &'a GbmDevice<'a>, attributes: GlAttributes,
reqs: PixelFormatRequirements, logger: L) reqs: PixelFormatRequirements, logger: L)
-> Result<EGLContext<'a, GbmSurface<'a, U>>, CreationError> -> Result<EGLContext<'a, GbmSurface<'a, U>>>
where where
L: Into<Option<::slog::Logger>>, L: Into<Option<::slog::Logger>>,
{ {
@ -275,7 +291,7 @@ impl<'a> EGLContext<'a, ()> {
impl<'a, T: NativeSurface> EGLContext<'a, T> { impl<'a, T: NativeSurface> EGLContext<'a, T> {
unsafe fn new(native: NativeDisplayPtr, mut attributes: GlAttributes, reqs: PixelFormatRequirements, unsafe fn new(native: NativeDisplayPtr, mut attributes: GlAttributes, reqs: PixelFormatRequirements,
log: ::slog::Logger) log: ::slog::Logger)
-> Result<EGLContext<'a, T>, CreationError> -> Result<EGLContext<'a, T>>
where where
T: NativeSurface, T: NativeSurface,
{ {
@ -297,12 +313,12 @@ impl<'a, T: NativeSurface> EGLContext<'a, T> {
} }
} }
} }
Some((1, _)) => { Some((1, x)) => {
error!( error!(
log, log,
"OpenGLES 1.* is not supported by the EGL renderer backend" "OpenGLES 1.* is not supported by the EGL renderer backend"
); );
return Err(CreationError::OpenGlVersionNotSupported); bail!(ErrorKind::OpenGlVersionNotSupported((1, x)));
} }
Some(version) => { Some(version) => {
error!( error!(
@ -310,12 +326,12 @@ impl<'a, T: NativeSurface> EGLContext<'a, T> {
"OpenGLES {:?} is unknown and not supported by the EGL renderer backend", "OpenGLES {:?} is unknown and not supported by the EGL renderer backend",
version version
); );
return Err(CreationError::OpenGlVersionNotSupported); bail!(ErrorKind::OpenGlVersionNotSupported(version));
} }
}; };
trace!(log, "Loading libEGL"); trace!(log, "Loading libEGL");
let lib = Library::new("libEGL.so.1")?; let lib = Library::new("libEGL.so.1").chain_err(|| ErrorKind::LoadingEGLFailed)?;
let egl = ffi::egl::Egl::load_with(|sym| { let egl = ffi::egl::Egl::load_with(|sym| {
let name = CString::new(sym).unwrap(); let name = CString::new(sym).unwrap();
let symbol = lib.get::<*mut c_void>(name.as_bytes()); let symbol = lib.get::<*mut c_void>(name.as_bytes());
@ -418,6 +434,7 @@ impl<'a, T: NativeSurface> EGLContext<'a, T> {
if display == ffi::egl::NO_DISPLAY { if display == ffi::egl::NO_DISPLAY {
error!(log, "EGL Display is not valid"); error!(log, "EGL Display is not valid");
bail!(ErrorKind::DisplayNotSupported);
} }
let egl_version = { let egl_version = {
@ -425,7 +442,7 @@ impl<'a, T: NativeSurface> EGLContext<'a, T> {
let mut minor: ffi::egl::types::EGLint = mem::uninitialized(); let mut minor: ffi::egl::types::EGLint = mem::uninitialized();
if egl.Initialize(display, &mut major, &mut minor) == 0 { if egl.Initialize(display, &mut major, &mut minor) == 0 {
return Err(CreationError::OsError(String::from("eglInitialize failed"))); bail!(ErrorKind::InitFailed);
} }
info!(log, "EGL Initialized"); info!(log, "EGL Initialized");
@ -451,7 +468,7 @@ impl<'a, T: NativeSurface> EGLContext<'a, T> {
log, log,
"OpenGLES not supported by the underlying EGL implementation" "OpenGLES not supported by the underlying EGL implementation"
); );
return Err(CreationError::OpenGlVersionNotSupported); bail!(ErrorKind::OpenGlesNotSupported);
} }
let descriptor = { let descriptor = {
@ -477,7 +494,7 @@ impl<'a, T: NativeSurface> EGLContext<'a, T> {
log, log,
"OpenglES 3.* is not supported on EGL Versions lower then 1.3" "OpenglES 3.* is not supported on EGL Versions lower then 1.3"
); );
return Err(CreationError::NoAvailablePixelFormat); bail!(ErrorKind::NoAvailablePixelFormat);
} }
trace!(log, "Setting RENDERABLE_TYPE to OPENGL_ES3"); trace!(log, "Setting RENDERABLE_TYPE to OPENGL_ES3");
out.push(ffi::egl::RENDERABLE_TYPE as c_int); out.push(ffi::egl::RENDERABLE_TYPE as c_int);
@ -492,7 +509,7 @@ impl<'a, T: NativeSurface> EGLContext<'a, T> {
log, log,
"OpenglES 2.* is not supported on EGL Versions lower then 1.3" "OpenglES 2.* is not supported on EGL Versions lower then 1.3"
); );
return Err(CreationError::NoAvailablePixelFormat); bail!(ErrorKind::NoAvailablePixelFormat);
} }
trace!(log, "Setting RENDERABLE_TYPE to OPENGL_ES2"); trace!(log, "Setting RENDERABLE_TYPE to OPENGL_ES2");
out.push(ffi::egl::RENDERABLE_TYPE as c_int); out.push(ffi::egl::RENDERABLE_TYPE as c_int);
@ -561,7 +578,7 @@ impl<'a, T: NativeSurface> EGLContext<'a, T> {
if reqs.stereoscopy { if reqs.stereoscopy {
error!(log, "Stereoscopy is currently unsupported (sorry!)"); error!(log, "Stereoscopy is currently unsupported (sorry!)");
return Err(CreationError::NoAvailablePixelFormat); bail!(ErrorKind::NoAvailablePixelFormat);
} }
out.push(ffi::egl::NONE as c_int); out.push(ffi::egl::NONE as c_int);
@ -579,13 +596,11 @@ impl<'a, T: NativeSurface> EGLContext<'a, T> {
&mut num_configs, &mut num_configs,
) == 0 ) == 0
{ {
return Err(CreationError::OsError( bail!(ErrorKind::ConfigFailed);
String::from("eglChooseConfig failed"),
));
} }
if num_configs == 0 { if num_configs == 0 {
error!(log, "No matching color format found"); error!(log, "No matching color format found");
return Err(CreationError::NoAvailablePixelFormat); bail!(ErrorKind::NoAvailablePixelFormat);
} }
// analyzing each config // analyzing each config
@ -596,7 +611,7 @@ impl<'a, T: NativeSurface> EGLContext<'a, T> {
let res = $egl.GetConfigAttrib($display, $config, let res = $egl.GetConfigAttrib($display, $config,
$attr as ffi::egl::types::EGLint, &mut value); $attr as ffi::egl::types::EGLint, &mut value);
if res == 0 { if res == 0 {
return Err(CreationError::OsError(String::from("eglGetConfigAttrib failed"))); bail!(ErrorKind::ConfigFailed);
} }
value value
} }
@ -654,14 +669,8 @@ impl<'a, T: NativeSurface> EGLContext<'a, T> {
if context.is_null() { if context.is_null() {
match egl.GetError() as u32 { match egl.GetError() as u32 {
ffi::egl::BAD_ATTRIBUTE => { ffi::egl::BAD_ATTRIBUTE => bail!(ErrorKind::CreationFailed),
error!( err_no => bail!(ErrorKind::Unknown(err_no)),
log,
"Context creation failed as one or more requirements could not be met. Try removing some gl attributes or pixel format requirements"
);
return Err(CreationError::OpenGlVersionNotSupported);
}
e => panic!("eglCreateContext failed: 0x{:x}", e),
} }
} }
debug!(log, "EGL context successfully created"); debug!(log, "EGL context successfully created");
@ -709,7 +718,7 @@ impl<'a, T: NativeSurface> EGLContext<'a, T> {
} }
/// Creates a surface bound to the given egl context for rendering /// Creates a surface bound to the given egl context for rendering
pub fn create_surface<'b>(&'a self, native: &'b T) -> Result<EGLSurface<'a, 'b, T>, CreationError> { pub fn create_surface<'b>(&'a self, native: &'b T) -> Result<EGLSurface<'a, 'b, T>> {
trace!(self.logger, "Creating EGL window surface..."); trace!(self.logger, "Creating EGL window surface...");
let (surface, keep) = native.surface(self.backend_type)?; let (surface, keep) = native.surface(self.backend_type)?;
@ -728,9 +737,7 @@ impl<'a, T: NativeSurface> EGLContext<'a, T> {
}; };
if egl_surface.is_null() { if egl_surface.is_null() {
return Err(CreationError::OsError( bail!(ErrorKind::SurfaceCreationFailed);
String::from("eglCreateWindowSurface failed"),
));
} }
debug!(self.logger, "EGL surface successfully created"); debug!(self.logger, "EGL surface successfully created");
@ -801,7 +808,7 @@ impl<'a, 'b, T: NativeSurface> DerefMut for EGLSurface<'a, 'b, T> {
impl<'context, 'surface, T: NativeSurface> EGLSurface<'context, 'surface, T> { impl<'context, 'surface, T: NativeSurface> EGLSurface<'context, 'surface, T> {
/// Swaps buffers at the end of a frame. /// Swaps buffers at the end of a frame.
pub fn swap_buffers(&self) -> Result<(), SwapBuffersError> { pub fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
let ret = unsafe { let ret = unsafe {
self.context self.context
.egl .egl
@ -824,7 +831,7 @@ impl<'context, 'surface, T: NativeSurface> EGLSurface<'context, 'surface, T> {
/// ///
/// This function is marked unsafe, because the context cannot be made current /// This function is marked unsafe, because the context cannot be made current
/// on multiple threads. /// on multiple threads.
pub unsafe fn make_current(&self) -> Result<(), SwapBuffersError> { pub unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
let ret = self.context.egl.MakeCurrent( let ret = self.context.egl.MakeCurrent(
self.context.display as *const _, self.context.display as *const _,
self.surface as *const _, self.surface as *const _,
@ -879,7 +886,8 @@ pub enum SwapBuffersError {
} }
impl fmt::Display for SwapBuffersError { impl fmt::Display for SwapBuffersError {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, formatter: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
use std::error::Error;
write!(formatter, "{}", self.description()) write!(formatter, "{}", self.description())
} }
} }
@ -983,7 +991,7 @@ pub struct PixelFormat {
/// and can be used to render upon /// and can be used to render upon
pub trait EGLGraphicsBackend: GraphicsBackend { pub trait EGLGraphicsBackend: GraphicsBackend {
/// Swaps buffers at the end of a frame. /// Swaps buffers at the end of a frame.
fn swap_buffers(&self) -> Result<(), SwapBuffersError>; fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError>;
/// Returns the address of an OpenGL function. /// Returns the address of an OpenGL function.
/// ///
@ -1006,7 +1014,7 @@ pub trait EGLGraphicsBackend: GraphicsBackend {
/// ///
/// This function is marked unsafe, because the context cannot be made current /// This function is marked unsafe, because the context cannot be made current
/// on multiple threads. /// on multiple threads.
unsafe fn make_current(&self) -> Result<(), SwapBuffersError>; unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError>;
/// Returns the pixel format of the main framebuffer of the context. /// Returns the pixel format of the main framebuffer of the context.
fn get_pixel_format(&self) -> PixelFormat; fn get_pixel_format(&self) -> PixelFormat;

View File

@ -2,21 +2,20 @@
use backend::{SeatInternal, TouchSlotInternal}; use backend::{SeatInternal, TouchSlotInternal};
use backend::graphics::GraphicsBackend; use backend::graphics::GraphicsBackend;
use backend::graphics::egl::{CreationError, EGLContext, EGLGraphicsBackend, GlAttributes, PixelFormat, use backend::graphics::egl::{self, EGLContext, EGLGraphicsBackend, GlAttributes, PixelFormat,
PixelFormatRequirements, SwapBuffersError}; PixelFormatRequirements, SwapBuffersError};
use backend::input::{Axis, AxisSource, Event as BackendEvent, InputBackend, InputHandler, KeyState, use backend::input::{Axis, AxisSource, Event as BackendEvent, InputBackend, InputHandler, KeyState,
KeyboardKeyEvent, MouseButton, MouseButtonState, PointerAxisEvent, PointerButtonEvent, KeyboardKeyEvent, MouseButton, MouseButtonState, PointerAxisEvent, PointerButtonEvent,
PointerMotionAbsoluteEvent, Seat, SeatCapabilities, TouchCancelEvent, TouchDownEvent, PointerMotionAbsoluteEvent, Seat, SeatCapabilities, TouchCancelEvent, TouchDownEvent,
TouchMotionEvent, TouchSlot, TouchUpEvent, UnusedEvent}; TouchMotionEvent, TouchSlot, TouchUpEvent, UnusedEvent};
use nix::c_void; use nix::c_void;
use rental::TryNewError;
use std::cmp; use std::cmp;
use std::error::Error; use std::error;
use std::fmt; use std::fmt;
use std::rc::Rc; use std::rc::Rc;
use winit::{CreationError as WinitCreationError, ElementState, Event, EventsLoop, KeyboardInput, use winit::{ElementState, Event, EventsLoop, KeyboardInput, MouseButton as WinitMouseButton, MouseCursor,
MouseButton as WinitMouseButton, MouseCursor, MouseScrollDelta, Touch, TouchPhase, MouseScrollDelta, Touch, TouchPhase, WindowBuilder, WindowEvent};
WindowBuilder, WindowEvent};
rental! { rental! {
mod rental { mod rental {
@ -40,6 +39,24 @@ rental! {
use self::rental::{Window, EGL}; use self::rental::{Window, EGL};
error_chain! {
errors {
#[doc = "Failed to initialize a window"]
InitFailed {
description("Failed to initialize a window")
}
}
links {
EGL(egl::Error, egl::ErrorKind) #[doc = "EGL error"];
}
}
impl<H> From<TryNewError<Error, H>> for Error {
fn from(err: TryNewError<Error, H>) -> Error {
err.0
}
}
/// Window with an active EGL Context created by `winit`. Implements the /// Window with an active EGL Context created by `winit`. Implements the
/// `EGLGraphicsBackend` graphics backend trait /// `EGLGraphicsBackend` graphics backend trait
pub struct WinitGraphicsBackend { pub struct WinitGraphicsBackend {
@ -64,7 +81,7 @@ pub struct WinitInputBackend {
/// Create a new `WinitGraphicsBackend`, which implements the `EGLGraphicsBackend` /// Create a new `WinitGraphicsBackend`, which implements the `EGLGraphicsBackend`
/// graphics backend trait and a corresponding `WinitInputBackend`, which implements /// graphics backend trait and a corresponding `WinitInputBackend`, which implements
/// the `InputBackend` trait /// the `InputBackend` trait
pub fn init<L>(logger: L) -> Result<(WinitGraphicsBackend, WinitInputBackend), CreationError> pub fn init<L>(logger: L) -> Result<(WinitGraphicsBackend, WinitInputBackend)>
where where
L: Into<Option<::slog::Logger>>, L: Into<Option<::slog::Logger>>,
{ {
@ -81,7 +98,7 @@ where
/// graphics backend trait, from a given `WindowBuilder` struct and a corresponding /// graphics backend trait, from a given `WindowBuilder` struct and a corresponding
/// `WinitInputBackend`, which implements the `InputBackend` trait /// `WinitInputBackend`, which implements the `InputBackend` trait
pub fn init_from_builder<L>(builder: WindowBuilder, logger: L) pub fn init_from_builder<L>(builder: WindowBuilder, logger: L)
-> Result<(WinitGraphicsBackend, WinitInputBackend), CreationError> -> Result<(WinitGraphicsBackend, WinitInputBackend)>
where where
L: Into<Option<::slog::Logger>>, L: Into<Option<::slog::Logger>>,
{ {
@ -101,9 +118,8 @@ where
/// graphics backend trait, from a given `WindowBuilder` struct, as well as given /// graphics backend trait, from a given `WindowBuilder` struct, as well as given
/// `GlAttributes` for further customization of the rendering pipeline and a /// `GlAttributes` for further customization of the rendering pipeline and a
/// corresponding `WinitInputBackend`, which implements the `InputBackend` trait. /// corresponding `WinitInputBackend`, which implements the `InputBackend` trait.
pub fn init_from_builder_with_gl_attr<L>( pub fn init_from_builder_with_gl_attr<L>(builder: WindowBuilder, attributes: GlAttributes, logger: L)
builder: WindowBuilder, attributes: GlAttributes, logger: L) -> Result<(WinitGraphicsBackend, WinitInputBackend)>
-> Result<(WinitGraphicsBackend, WinitInputBackend), CreationError>
where where
L: Into<Option<::slog::Logger>>, L: Into<Option<::slog::Logger>>,
{ {
@ -111,11 +127,13 @@ where
info!(log, "Initializing a winit backend"); info!(log, "Initializing a winit backend");
let events_loop = EventsLoop::new(); let events_loop = EventsLoop::new();
let winit_window = builder.build(&events_loop)?; let winit_window = builder
.build(&events_loop)
.chain_err(|| ErrorKind::InitFailed)?;
debug!(log, "Window created"); debug!(log, "Window created");
let window = match Window::try_new(Box::new(winit_window), |window| { let window = Rc::new(Window::try_new(Box::new(winit_window), |window| {
match EGL::try_new( EGL::try_new(
Box::new(match EGLContext::new_from_winit( Box::new(match EGLContext::new_from_winit(
&*window, &*window,
attributes, attributes,
@ -128,20 +146,12 @@ where
log.clone(), log.clone(),
) { ) {
Ok(context) => context, Ok(context) => context,
Err(err) => { Err(err) => bail!(err),
error!(log, "EGLContext creation failed:\n {}", err);
return Err(err);
}
}), }),
|context| context.create_surface(window), |context| context.create_surface(window),
) { ).map_err(egl::Error::from)
Ok(x) => Ok(x), .map_err(Error::from)
Err(::rental::TryNewError(err, _)) => return Err(err), })?);
}
}) {
Ok(x) => Rc::new(x),
Err(::rental::TryNewError(err, _)) => return Err(err),
};
Ok(( Ok((
WinitGraphicsBackend { WinitGraphicsBackend {
@ -172,12 +182,13 @@ impl GraphicsBackend for WinitGraphicsBackend {
type CursorFormat = MouseCursor; type CursorFormat = MouseCursor;
type Error = (); type Error = ();
fn set_cursor_position(&self, x: u32, y: u32) -> Result<(), ()> { fn set_cursor_position(&self, x: u32, y: u32) -> ::std::result::Result<(), ()> {
debug!(self.logger, "Setting cursor position to {:?}", (x, y)); debug!(self.logger, "Setting cursor position to {:?}", (x, y));
self.window.head().set_cursor_position(x as i32, y as i32) self.window.head().set_cursor_position(x as i32, y as i32)
} }
fn set_cursor_representation(&self, cursor: Self::CursorFormat, _hotspot: (u32, u32)) -> Result<(), ()> { fn set_cursor_representation(&self, cursor: Self::CursorFormat, _hotspot: (u32, u32))
-> ::std::result::Result<(), ()> {
// Cannot log this one, as `CursorFormat` is not `Debug` and should not be // Cannot log this one, as `CursorFormat` is not `Debug` and should not be
debug!(self.logger, "Changing cursor representation"); debug!(self.logger, "Changing cursor representation");
self.window.head().set_cursor(cursor); self.window.head().set_cursor(cursor);
@ -186,7 +197,7 @@ impl GraphicsBackend for WinitGraphicsBackend {
} }
impl EGLGraphicsBackend for WinitGraphicsBackend { impl EGLGraphicsBackend for WinitGraphicsBackend {
fn swap_buffers(&self) -> Result<(), SwapBuffersError> { fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
trace!(self.logger, "Swapping buffers"); trace!(self.logger, "Swapping buffers");
self.window self.window
.rent(|egl| egl.rent(|surface| surface.swap_buffers())) .rent(|egl| egl.rent(|surface| surface.swap_buffers()))
@ -208,7 +219,7 @@ impl EGLGraphicsBackend for WinitGraphicsBackend {
self.window.rent(|egl| egl.head().is_current()) self.window.rent(|egl| egl.head().is_current())
} }
unsafe fn make_current(&self) -> Result<(), SwapBuffersError> { unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
debug!(self.logger, "Setting EGL context to be the current context"); debug!(self.logger, "Setting EGL context to be the current context");
self.window self.window
.rent(|egl| egl.rent(|surface| surface.make_current())) .rent(|egl| egl.rent(|surface| surface.make_current()))
@ -228,7 +239,7 @@ pub enum WinitInputError {
WindowClosed, WindowClosed,
} }
impl Error for WinitInputError { impl error::Error for WinitInputError {
fn description(&self) -> &str { fn description(&self) -> &str {
match *self { match *self {
WinitInputError::WindowClosed => "Glutin Window was closed", WinitInputError::WindowClosed => "Glutin Window was closed",
@ -238,6 +249,7 @@ impl Error for WinitInputError {
impl fmt::Display for WinitInputError { impl fmt::Display for WinitInputError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use std::error::Error;
write!(f, "{}", self.description()) write!(f, "{}", self.description())
} }
} }
@ -576,7 +588,7 @@ impl InputBackend for WinitInputBackend {
/// ///
/// The linked `WinitGraphicsBackend` will error with a lost Context and should /// The linked `WinitGraphicsBackend` will error with a lost Context and should
/// not be used anymore as well. /// not be used anymore as well.
fn dispatch_new_events(&mut self) -> Result<(), WinitInputError> { fn dispatch_new_events(&mut self) -> ::std::result::Result<(), WinitInputError> {
let mut closed = false; let mut closed = false;
{ {
@ -831,12 +843,3 @@ impl From<ElementState> for MouseButtonState {
} }
} }
} }
impl From<WinitCreationError> for CreationError {
fn from(error: WinitCreationError) -> Self {
match error {
WinitCreationError::OsError(x) => CreationError::OsError(x),
WinitCreationError::NotSupported => CreationError::NotSupported,
}
}
}

View File

@ -6,6 +6,8 @@
#![cfg_attr(feature = "clippy", feature(plugin))] #![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))] #![cfg_attr(feature = "clippy", plugin(clippy))]
// `error_chain!` can recurse deeply
#![recursion_limit = "1024"]
extern crate image; extern crate image;
extern crate nix; extern crate nix;
@ -36,6 +38,9 @@ extern crate glium;
extern crate slog; extern crate slog;
extern crate slog_stdlog; extern crate slog_stdlog;
#[macro_use]
extern crate error_chain;
pub mod backend; pub mod backend;
pub mod compositor; pub mod compositor;
pub mod shm; pub mod shm;