Store DrmBackend in EventLoop state

This commit is contained in:
Drakulix 2017-09-20 19:48:58 +02:00
parent 0758ec98ba
commit 518f7dbdfc
7 changed files with 228 additions and 303 deletions

View File

@ -40,7 +40,4 @@ backend_libinput = ["input"]
renderer_glium = ["glium"]
[replace]
"wayland-server:0.9.9" = { git = "https://github.com/Drakulix/wayland-rs", branch = "raw_handler_access"}
"wayland-protocols:0.9.9" = { git = "https://github.com/Drakulix/wayland-rs", branch = "raw_handler_access"}
"wayland-client:0.9.9" = { git = "https://github.com/Drakulix/wayland-rs", branch = "raw_handler_access"}
"drm:0.2.1" = { git = "https://github.com/Drakulix/drm-rs", branch = "future" }

View File

@ -19,9 +19,8 @@ use drm::control::encoder::Info as EncoderInfo;
use glium::Surface;
use helpers::{shell_implementation, surface_implementation, GliumDrawer, Roles, SurfaceData};
use slog::{Drain, Logger};
use smithay::backend::drm::{drm_device_bind, DrmBackend, DrmDevice, DrmHandler, Id};
use smithay::backend::drm::{drm_device_bind, DrmBackend, DrmDevice, DrmHandler};
use smithay::backend::graphics::egl::EGLGraphicsBackend;
use smithay::backend::graphics::glium::{GliumGraphicsBackend, IntoGlium};
use smithay::compositor::{compositor_init, CompositorToken, SubsurfaceRole, TraversalAction};
use smithay::compositor::roles::Role;
use smithay::shell::{shell_init, ShellState};
@ -48,7 +47,7 @@ fn main() {
let mut options = OpenOptions::new();
options.read(true);
options.write(true);
let mut device =
let mut device: DrmDevice<GliumDrawer<DrmBackend>> =
DrmDevice::new_from_file(options.clone().open("/dev/dri/card0").unwrap(), log.clone()).unwrap();
// Get a set of all modesetting resource handles (excluding planes):
@ -80,8 +79,8 @@ fn main() {
let mode = connector_info.modes()[0]; // Use first mode (usually highest resoltion, but in reality you should filter and sort and check and match with other connectors, if you use more then one.)
// Initialize the hardware backend
let renderer = device
.create_backend(crtc, mode, vec![connector_info.handle()])
let renderer_token = device
.create_backend(&mut event_loop, crtc, mode, vec![connector_info.handle()])
.unwrap();
/*
@ -104,10 +103,12 @@ fn main() {
/*
* Initialize glium
*/
let drawer = GliumDrawer::new(renderer.into_glium());
let mut frame = drawer.draw();
frame.clear_color(0.8, 0.8, 0.9, 1.0);
frame.finish().unwrap();
{
let drawer = event_loop.state().get(&renderer_token);
let mut frame = drawer.draw();
frame.clear_color(0.8, 0.8, 0.9, 1.0);
frame.finish().unwrap();
}
/*
* Add a listening socket:
@ -122,7 +123,6 @@ fn main() {
&mut event_loop,
device,
DrmHandlerImpl {
drawer,
shell_state_token,
compositor_token,
logger: log,
@ -133,20 +133,21 @@ fn main() {
}
pub struct DrmHandlerImpl {
drawer: GliumDrawer<GliumGraphicsBackend<DrmBackend>>,
shell_state_token: StateToken<ShellState<SurfaceData, Roles, (), ()>>,
compositor_token: CompositorToken<SurfaceData, Roles, ()>,
logger: ::slog::Logger,
}
impl DrmHandler for DrmHandlerImpl {
fn ready(&mut self, evlh: &mut EventLoopHandle, _id: Id, _frame: u32, _duration: Duration) {
let mut frame = self.drawer.draw();
impl DrmHandler<GliumDrawer<DrmBackend>> for DrmHandlerImpl {
fn ready(&mut self, evlh: &mut EventLoopHandle, _device: &mut DrmDevice<GliumDrawer<DrmBackend>>,
backend: &StateToken<GliumDrawer<DrmBackend>>, _frame: u32, _duration: Duration) {
let state = evlh.state();
let drawer = state.get(backend);
let mut frame = drawer.draw();
frame.clear_color(0.8, 0.8, 0.9, 1.0);
// redraw the frame, in a simple but inneficient way
{
let screen_dimensions = self.drawer.get_framebuffer_dimensions();
let state = evlh.state();
let screen_dimensions = drawer.get_framebuffer_dimensions();
for toplevel_surface in state.get(&self.shell_state_token).toplevel_surfaces() {
if let Some(wl_surface) = toplevel_surface.get_surface() {
// this surface is a root of a subsurface tree that needs to be drawn
@ -163,13 +164,7 @@ impl DrmHandler for DrmHandlerImpl {
x += subdata.x;
y += subdata.y;
}
self.drawer.render(
&mut frame,
contents,
(w, h),
(x, y),
screen_dimensions,
);
drawer.render(&mut frame, contents, (w, h), (x, y), screen_dimensions);
TraversalAction::DoChildren((x, y))
} else {
// we are not display, so our children are neither
@ -184,7 +179,8 @@ impl DrmHandler for DrmHandlerImpl {
frame.finish().unwrap();
}
fn error(&mut self, _evlh: &mut EventLoopHandle, error: IoError) {
fn error(&mut self, _evlh: &mut EventLoopHandle, _device: &mut DrmDevice<GliumDrawer<DrmBackend>>,
error: IoError) {
panic!("{:?}", error);
}
}

View File

@ -1,7 +1,8 @@
use glium;
use glium::Surface;
use glium::{Frame, Surface};
use glium::index::PrimitiveType;
use smithay::backend::graphics::egl::EGLGraphicsBackend;
use smithay::backend::graphics::glium::GliumGraphicsBackend;
use std::ops::Deref;
#[derive(Copy, Clone)]
@ -12,23 +13,25 @@ struct Vertex {
implement_vertex!(Vertex, position, tex_coords);
pub struct GliumDrawer<F> {
display: F,
pub struct GliumDrawer<F: EGLGraphicsBackend + 'static> {
display: GliumGraphicsBackend<F>,
vertex_buffer: glium::VertexBuffer<Vertex>,
index_buffer: glium::IndexBuffer<u16>,
program: glium::Program,
}
impl<F> Deref for GliumDrawer<F> {
impl<F: EGLGraphicsBackend + 'static> Deref for GliumDrawer<F> {
type Target = F;
fn deref(&self) -> &F {
&self.display
&*self.display
}
}
impl<F: glium::backend::Facade> GliumDrawer<F> {
pub fn new(display: F) -> GliumDrawer<F> {
impl<T: Into<GliumGraphicsBackend<T>> + EGLGraphicsBackend + 'static> From<T> for GliumDrawer<T> {
fn from(backend: T) -> GliumDrawer<T> {
let display = backend.into();
// building the vertex buffer, which contains all the vertices that we will draw
let vertex_buffer = glium::VertexBuffer::new(
&display,
@ -93,7 +96,9 @@ impl<F: glium::backend::Facade> GliumDrawer<F> {
program,
}
}
}
impl<F: EGLGraphicsBackend + 'static> GliumDrawer<F> {
pub fn render(&self, target: &mut glium::Frame, contents: &[u8], surface_dimensions: (u32, u32),
surface_location: (i32, i32), screen_size: (u32, u32)) {
let image = glium::texture::RawImage2d {
@ -130,4 +135,9 @@ impl<F: glium::backend::Facade> GliumDrawer<F> {
)
.unwrap();
}
#[inline]
pub fn draw(&self) -> Frame {
self.display.draw()
}
}

View File

@ -7,7 +7,6 @@ extern crate slog_async;
extern crate slog_term;
#[macro_use(define_roles)]
extern crate smithay;
extern crate wayland_protocols;
extern crate wayland_server;
mod helpers;
@ -16,8 +15,6 @@ use glium::Surface;
use helpers::{shell_implementation, surface_implementation, GliumDrawer};
use slog::{Drain, Logger};
use smithay::backend::graphics::egl::EGLGraphicsBackend;
use smithay::backend::graphics::glium::IntoGlium;
use smithay::backend::input::InputBackend;
use smithay::backend::winit;
use smithay::compositor::{compositor_init, SubsurfaceRole, TraversalAction};
@ -57,7 +54,7 @@ fn main() {
/*
* Initialize glium
*/
let drawer = GliumDrawer::new(renderer.into_glium());
let drawer = GliumDrawer::from(renderer);
/*
* Add a listening socket:

View File

@ -8,18 +8,9 @@ use drm::control::ResourceInfo;
use gbm::{BufferObject, BufferObjectFlags, Format as GbmFormat, Surface as GbmSurface, SurfaceBufferHandle};
use image::{ImageBuffer, Rgba};
use nix::c_void;
use std::cell::{Cell, RefCell};
use std::cell::Cell;
use std::rc::Rc;
/// Backend based on a `DrmDevice` and a given crtc
pub struct DrmBackend(Rc<RefCell<DrmBackendInternal>>);
impl DrmBackend {
pub(crate) fn new(drm: Rc<RefCell<DrmBackendInternal>>) -> DrmBackend {
DrmBackend(drm)
}
}
/*
Dependency graph
- drm
@ -32,7 +23,7 @@ impl DrmBackend {
*/
pub(crate) struct GbmTypes<'dev, 'context> {
cursor: BufferObject<'dev, ()>,
cursor: Cell<BufferObject<'dev, ()>>,
surface: Surface<'context>,
}
@ -70,60 +61,32 @@ rental! {
}
use self::graphics::{Graphics, Surface};
/// Id of a `DrmBackend` related to its `DrmDevice`.
///
/// Used to track which `DrmBackend` finished page-flipping
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Id(usize);
impl Id {
pub(crate) fn raw(&self) -> usize {
self.0
}
}
pub(crate) struct DrmBackendInternal {
/// Backend based on a `DrmDevice` and a given crtc
pub struct DrmBackend {
graphics: Graphics,
crtc: crtc::Handle,
mode: Mode,
connectors: Vec<connector::Handle>,
own_id: Id,
logger: ::slog::Logger,
}
impl DrmBackendInternal {
pub(crate) fn new<I, L>(context: Rc<devices::Context>, crtc: crtc::Handle, mode: Mode, connectors: I,
own_id: usize, logger: L)
-> Result<DrmBackendInternal>
where
I: Into<Vec<connector::Handle>>,
L: Into<Option<::slog::Logger>>,
{
impl DrmBackend {
pub(crate) fn new(context: Rc<devices::Context>, crtc: crtc::Handle, mode: Mode,
connectors: Vec<connector::Handle>, logger: ::slog::Logger)
-> Result<DrmBackend> {
// logger already initialized by the DrmDevice
let log = ::slog_or_stdlog(logger);
info!(log, "Initializing DrmBackend");
let connectors = connectors.into();
// check the connectors, if they suite the mode
for connector in connectors.iter() {
if !connector::Info::load_from_device(context.head().head(), *connector)
.chain_err(|| ErrorKind::DrmDev(format!("{:?}", context.head().head())))?
.modes()
.contains(&mode)
{
bail!(ErrorKind::ModeNotSuitable(mode))
}
}
let (w, h) = mode.size();
Ok(DrmBackendInternal {
Ok(DrmBackend {
graphics: Graphics::try_new(context, |context| {
Ok(GbmTypes {
cursor: {
// Create an unused cursor buffer (we don't want an Option here)
context
Cell::new(context
.devices
.gbm
.create_buffer_object(
@ -132,7 +95,7 @@ impl DrmBackendInternal {
GbmFormat::ARGB8888,
&[BufferObjectFlags::Cursor, BufferObjectFlags::Write],
)
.chain_err(|| ErrorKind::GbmInitFailed)?
.chain_err(|| ErrorKind::GbmInitFailed)?)
},
surface: Surface::try_new(
{
@ -202,7 +165,6 @@ impl DrmBackendInternal {
crtc,
mode,
connectors,
own_id: Id(own_id),
logger: log.clone(),
})
}
@ -225,85 +187,67 @@ impl DrmBackendInternal {
});
}
pub(crate) fn is_crtc(&self, crtc: crtc::Handle) -> bool {
crtc == self.crtc
}
}
impl DrmBackend {
/// Add a connector to backend
///
/// # Errors
///
/// Errors if the new connector does not support the currently set `Mode`
pub fn add_connector(&mut self, connector: connector::Handle) -> Result<()> {
let info =
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()
))
})?;
let info = connector::Info::load_from_device(self.graphics.head().head().head(), connector)
.chain_err(|| {
ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head()))
})?;
// check if the connector can handle the current mode
let mut internal = self.0.borrow_mut();
if info.modes().contains(&internal.mode) {
if info.modes().contains(&self.mode) {
// check if there is a valid encoder
let encoders = info.encoders()
.iter()
.map(|encoder| {
encoder::Info::load_from_device(self.0.borrow().graphics.head().head().head(), *encoder)
.chain_err(|| {
ErrorKind::DrmDev(format!(
"{:?}",
self.0.borrow().graphics.head().head().head()
))
})
encoder::Info::load_from_device(self.graphics.head().head().head(), *encoder).chain_err(
|| ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head())),
)
})
.collect::<Result<Vec<encoder::Info>>>()?;
// and if any encoder supports the selected crtc
if !encoders
.iter()
.any(|encoder| encoder.supports_crtc(self.0.borrow().crtc))
.any(|encoder| encoder.supports_crtc(self.crtc))
{
bail!(ErrorKind::NoSuitableEncoder(info, self.0.borrow().crtc));
bail!(ErrorKind::NoSuitableEncoder(info, self.crtc));
}
info!(
self.0.borrow().logger,
self.logger,
"Adding new connector: {:?}",
info.connector_type()
);
internal.connectors.push(connector);
self.connectors.push(connector);
Ok(())
} else {
bail!(ErrorKind::ModeNotSuitable(self.0.borrow().mode))
bail!(ErrorKind::ModeNotSuitable(self.mode))
}
}
/// Returns a copy of the currently set connectors
pub fn used_connectors(&self) -> Vec<connector::Handle> {
// thanks to the RefCell we can sadly not return a `&[connector::Handle]`
self.0.borrow().connectors.clone()
/// Returns the currently set connectors
pub fn used_connectors(&self) -> &[connector::Handle] {
&*self.connectors
}
/// Removes a currently set connector
pub fn remove_connector(&mut self, connector: connector::Handle) {
if let Ok(info) =
connector::Info::load_from_device(self.0.borrow().graphics.head().head().head(), connector)
{
if let Ok(info) = connector::Info::load_from_device(self.graphics.head().head().head(), connector) {
info!(
self.0.borrow().logger,
self.logger,
"Removing connector: {:?}",
info.connector_type()
);
} else {
info!(self.0.borrow().logger, "Removing unknown connector");
info!(self.logger, "Removing unknown connector");
}
self.0.borrow_mut().connectors.retain(|x| *x != connector);
self.connectors.retain(|x| *x != connector);
}
/// Changes the currently set mode
@ -315,13 +259,10 @@ impl DrmBackend {
/// Other errors might occur.
pub fn use_mode(&mut self, mode: Mode) -> Result<()> {
// check the connectors
for connector in self.0.borrow().connectors.iter() {
if !connector::Info::load_from_device(self.0.borrow().graphics.head().head().head(), *connector)
for connector in self.connectors.iter() {
if !connector::Info::load_from_device(self.graphics.head().head().head(), *connector)
.chain_err(|| {
ErrorKind::DrmDev(format!(
"{:?}",
self.0.borrow().graphics.head().head().head()
))
ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head()))
})?
.modes()
.contains(&mode)
@ -332,21 +273,25 @@ impl DrmBackend {
// borrow & clone stuff because rust cannot figure out the upcoming
// closure otherwise.
let crtc = self.0.borrow().crtc;
let mut internal = self.0.borrow_mut();
let connectors = internal.connectors.clone();
let logger = internal.logger.clone();
let crtc = self.crtc;
let connectors_ref = &self.connectors;
let logger_ref = &self.logger;
let (w, h) = mode.size();
internal.graphics.rent_all_mut(|graphics| -> Result<()> {
self.graphics.rent_all_mut(|graphics| -> Result<()> {
// Recreate the surface and the related resources to match the new
// resolution.
debug!(logger, "Reinitializing surface for new mode: {}:{}", w, h);
debug!(
logger_ref,
"Reinitializing surface for new mode: {}:{}",
w,
h
);
graphics.gbm.surface = Surface::try_new(
{
// create a new gbm surface
debug!(logger, "Creating GbmSurface");
debug!(logger_ref, "Creating GbmSurface");
Box::new(graphics
.context
.devices
@ -361,7 +306,7 @@ impl DrmBackend {
},
|surface| {
// create an egl surface from the gbm one
debug!(logger, "Creating EGLSurface");
debug!(logger_ref, "Creating EGLSurface");
let egl_surface = graphics.context.egl.create_surface(&surface)?;
// make it active for the first `crtc::set`
@ -378,18 +323,22 @@ impl DrmBackend {
let mut front_bo = surface
.lock_front_buffer()
.chain_err(|| ErrorKind::FailedToSwap)?;
debug!(logger, "FrontBuffer color format: {:?}", front_bo.format());
debug!(
logger_ref,
"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_ref, "Initialize screen");
crtc::set(
graphics.context.devices.drm,
crtc,
fb.handle(),
&connectors,
connectors_ref,
(0, 0),
Some(mode),
).chain_err(|| {
@ -410,25 +359,17 @@ impl DrmBackend {
Ok(())
})?;
info!(logger, "Setting new mode: {:?}", mode.name());
internal.mode = mode;
info!(self.logger, "Setting new mode: {:?}", mode.name());
self.mode = mode;
Ok(())
}
/// Checks of the `DrmBackend` is of the given `Id`
///
/// Only produces valid results, if the `Id` is from the `DrmDevice`,
/// that created this backend.
pub fn is(&self, id: Id) -> bool {
self.0.borrow().own_id == id
}
}
impl Drop for DrmBackend {
fn drop(&mut self) {
// Drop framebuffers attached to the userdata of the gbm surface buffers.
// (They don't implement drop, as they need the device)
self.0.borrow_mut().graphics.rent_all_mut(|graphics| {
self.graphics.rent_all_mut(|graphics| {
if let Some(fb) = graphics.gbm.surface.rent(|egl| {
if let Some(mut next) = egl.buffers.next_buffer.take() {
return next.take_userdata();
@ -459,84 +400,72 @@ impl GraphicsBackend for DrmBackend {
type Error = Error;
fn set_cursor_position(&self, x: u32, y: u32) -> Result<()> {
trace!(self.0.borrow().logger, "Move the cursor to {},{}", x, y);
trace!(self.logger, "Move the cursor to {},{}", x, y);
crtc::move_cursor(
self.0.borrow().graphics.head().head().head(),
self.0.borrow().crtc,
self.graphics.head().head().head(),
self.crtc,
(x as i32, y as i32),
).chain_err(|| {
ErrorKind::DrmDev(format!(
"{:?}",
self.0.borrow().graphics.head().head().head()
))
ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head()))
})
}
fn set_cursor_representation(&self, buffer: ImageBuffer<Rgba<u8>, Vec<u8>>, hotspot: (u32, u32))
-> Result<()> {
let (w, h) = buffer.dimensions();
debug!(self.0.borrow().logger, "Importing cursor");
// import the cursor into a buffer we can render
self.0
.borrow_mut()
.graphics
.rent_all_mut(|graphics| -> Result<()> {
graphics.gbm.cursor = {
let mut cursor = graphics
.context
.devices
.gbm
.create_buffer_object(
w,
h,
GbmFormat::ARGB8888,
&[BufferObjectFlags::Cursor, BufferObjectFlags::Write],
)
.chain_err(|| ErrorKind::GbmInitFailed)?;
cursor
.write(&*buffer.into_raw())
.chain_err(|| ErrorKind::GbmInitFailed)?;
cursor
};
Ok(())
})?;
trace!(self.0.borrow().logger, "Set the new imported cursor");
// and set it
if crtc::set_cursor2(
self.0.borrow().graphics.head().head().head(),
self.0.borrow().crtc,
self.0
.borrow()
.graphics
.rent(|gbm| Buffer::handle(&gbm.cursor)),
(w, h),
(hotspot.0 as i32, hotspot.1 as i32),
).is_err()
{
crtc::set_cursor(
self.0.borrow().graphics.head().head().head(),
self.0.borrow().crtc,
self.0
.borrow()
.graphics
.rent(|gbm| Buffer::handle(&gbm.cursor)),
(w, h),
).chain_err(|| {
ErrorKind::DrmDev(format!(
"{:?}",
self.0.borrow().graphics.head().head().head()
))
})
} else {
debug!(self.logger, "Importing cursor");
self.graphics.rent_all(|graphics| -> Result<()> {
graphics.gbm.cursor.set({
// import the cursor into a buffer we can render
let mut cursor = graphics
.context
.devices
.gbm
.create_buffer_object(
w,
h,
GbmFormat::ARGB8888,
&[BufferObjectFlags::Cursor, BufferObjectFlags::Write],
)
.chain_err(|| ErrorKind::GbmInitFailed)?;
cursor
.write(&*buffer.into_raw())
.chain_err(|| ErrorKind::GbmInitFailed)?;
trace!(self.logger, "Set the new imported cursor");
// and set it
if crtc::set_cursor2(
self.graphics.head().head().head(),
self.crtc,
Buffer::handle(&cursor),
(w, h),
(hotspot.0 as i32, hotspot.1 as i32),
).is_err()
{
crtc::set_cursor(
self.graphics.head().head().head(),
self.crtc,
Buffer::handle(&cursor),
(w, h),
).chain_err(|| {
ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head()))
})?;
}
// and store it
cursor
});
Ok(())
}
})
}
}
impl EGLGraphicsBackend for DrmBackend {
fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
self.0.borrow().graphics.rent_all(|graphics| {
self.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
if graphics.gbm.surface.rent(|egl| {
let next = egl.buffers.next_buffer.take();
@ -544,7 +473,7 @@ impl EGLGraphicsBackend for DrmBackend {
egl.buffers.next_buffer.set(next);
res
}) {
warn!(self.0.borrow().logger, "Tried to swap a DrmBackend with a queued flip");
warn!(self.logger, "Tried to swap a DrmBackend with a queued flip");
return Err(SwapBuffersError::AlreadySwapped);
}
@ -570,48 +499,36 @@ impl EGLGraphicsBackend for DrmBackend {
};
surface.egl.buffers.next_buffer.set(Some(next_bo));
trace!(self.0.borrow().logger, "Queueing Page flip");
let id: Id = self.0.borrow().own_id;
trace!(self.logger, "Queueing Page flip");
// and flip
crtc::page_flip(graphics.context.devices.drm, self.0.borrow().crtc, fb.handle(), &[crtc::PageFlipFlags::PageFlipEvent], id).map_err(|_| SwapBuffersError::ContextLost)
crtc::page_flip(graphics.context.devices.drm, self.crtc, fb.handle(), &[crtc::PageFlipFlags::PageFlipEvent], self.crtc).map_err(|_| SwapBuffersError::ContextLost)
})
})
}
unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void {
self.0
.borrow()
.graphics
self.graphics
.head()
.rent(|context| context.get_proc_address(symbol))
}
fn get_framebuffer_dimensions(&self) -> (u32, u32) {
let (w, h) = self.0.borrow().mode.size();
let (w, h) = self.mode.size();
(w as u32, h as u32)
}
fn is_current(&self) -> bool {
self.0
.borrow()
.graphics
.head()
.rent(|context| context.is_current())
self.graphics.head().rent(|context| context.is_current())
}
unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
self.0
.borrow()
.graphics
self.graphics
.rent(|gbm| gbm.surface.rent(|egl| egl.surface.make_current()))
}
fn get_pixel_format(&self) -> PixelFormat {
self.0
.borrow()
.graphics
self.graphics
.head()
.rent(|context| context.get_pixel_format())
}

View File

@ -191,20 +191,21 @@ use drm::control::{connector, crtc, encoder, Mode, ResourceInfo};
use drm::control::Device as ControlDevice;
use gbm::Device as GbmDevice;
use nix;
use std::cell::RefCell;
use std::collections::HashMap;
use std::fs::File;
use std::io::{Error as IoError, Result as IoResult};
use std::marker::PhantomData;
use std::ops::Deref;
use std::os::unix::io::{AsRawFd, RawFd};
use std::rc::{Rc, Weak};
use std::rc::Rc;
use std::time::Duration;
use wayland_server::EventLoopHandle;
use wayland_server::{EventLoopHandle, StateToken};
use wayland_server::sources::{FdEventSource, FdEventSourceImpl, READ};
mod backend;
pub mod error;
pub use self::backend::{DrmBackend, Id};
use self::backend::DrmBackendInternal;
pub use self::backend::DrmBackend;
use self::error::*;
/// Internal struct as required by the drm crate
@ -255,13 +256,13 @@ rental! {
use self::devices::{Context, Devices};
/// Representation of an open drm device node to create rendering backends
pub struct DrmDevice {
pub struct DrmDevice<B: Deref<Target = DrmBackend> + 'static> {
context: Rc<Context>,
backends: Vec<Weak<RefCell<DrmBackendInternal>>>,
backends: HashMap<crtc::Handle, StateToken<B>>,
logger: ::slog::Logger,
}
impl DrmDevice {
impl<B: From<DrmBackend> + Deref<Target = DrmBackend> + 'static> DrmDevice<B> {
/// Create a new `DrmDevice` from a raw file descriptor
///
/// Returns an error of opening the device failed or context creation was not
@ -358,7 +359,7 @@ impl DrmDevice {
context: Rc::new(Context::try_new(
Box::new(Devices::try_new(Box::new(drm), |drm| {
debug!(log, "Creating gbm device");
GbmDevice::new_from_drm::<DrmDevice>(drm).chain_err(|| ErrorKind::GbmInitFailed)
GbmDevice::new_from_drm::<DrmDevice<B>>(drm).chain_err(|| ErrorKind::GbmInitFailed)
})?),
|devices| {
debug!(log, "Creating egl context from gbm device");
@ -375,7 +376,7 @@ impl DrmDevice {
).map_err(Error::from)
},
)?),
backends: Vec::new(),
backends: HashMap::new(),
logger: log,
})
}
@ -385,29 +386,32 @@ impl DrmDevice {
///
/// Errors if initialization fails or the mode is not available on all given
/// connectors.
pub fn create_backend<I>(&mut self, crtc: crtc::Handle, mode: Mode, connectors: I) -> Result<DrmBackend>
pub fn create_backend<I>(&mut self, evlh: &mut EventLoopHandle, crtc: crtc::Handle, mode: Mode,
connectors: I)
-> Result<StateToken<B>>
where
I: Into<Vec<connector::Handle>>,
{
for backend in self.backends.iter() {
if let Some(backend) = backend.upgrade() {
if backend.borrow().is_crtc(crtc) {
bail!(ErrorKind::CrtcAlreadyInUse(crtc))
}
}
if self.backends.contains_key(&crtc) {
bail!(ErrorKind::CrtcAlreadyInUse(crtc));
}
// check if the given connectors and crtc match
let connectors = connectors.into();
// check if we have an encoder for every connector
// check if we have an encoder for every connector and the mode mode
for connector in connectors.iter() {
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
// check the mode
if !con_info.modes().contains(&mode) {
bail!(ErrorKind::ModeNotSuitable(mode));
}
// check for every connector which encoders it does support
let encoders = con_info
.encoders()
.iter()
@ -426,59 +430,50 @@ impl DrmDevice {
// configuration is valid, the kernel will figure out the rest
let own_id = self.backends.len();
let logger = self.logger.new(
o!("id" => format!("{}", own_id), "crtc" => format!("{:?}", crtc)),
);
let logger = self.logger.new(o!("crtc" => format!("{:?}", crtc)));
let backend = DrmBackend::new(self.context.clone(), crtc, mode, connectors, logger)?;
let token = evlh.state().insert(backend.into());
self.backends.insert(crtc, token.clone());
let backend = Rc::new(RefCell::new(DrmBackendInternal::new(
self.context.clone(),
crtc,
mode,
connectors,
own_id,
logger,
)?));
self.backends.push(Rc::downgrade(&backend));
Ok(DrmBackend::new(backend))
Ok(token)
}
}
// for users convinience and FdEventSource registering
impl AsRawFd for DrmDevice {
impl<B: Deref<Target = DrmBackend> + 'static> AsRawFd for DrmDevice<B> {
fn as_raw_fd(&self) -> RawFd {
self.context.head().head().as_raw_fd()
}
}
impl BasicDevice for DrmDevice {}
impl ControlDevice for DrmDevice {}
impl<B: Deref<Target = DrmBackend> + 'static> BasicDevice for DrmDevice<B> {}
impl<B: Deref<Target = DrmBackend> + 'static> ControlDevice for DrmDevice<B> {}
/// Handler for drm node events
///
/// See module-level documentation for its use
pub trait DrmHandler {
pub trait DrmHandler<B: Deref<Target = DrmBackend> + 'static> {
/// A `DrmBackend` has finished swapping buffers and new frame can now
/// (and should be immediately) be rendered.
///
/// The `id` argument is the `Id` of the `DrmBackend` that finished rendering,
/// check using `DrmBackend::is`.
fn ready(&mut self, evlh: &mut EventLoopHandle, id: Id, frame: u32, duration: Duration);
fn ready(&mut self, evlh: &mut EventLoopHandle, device: &mut DrmDevice<B>, backend: &StateToken<B>,
frame: u32, duration: Duration);
/// The `DrmDevice` has thrown an error.
///
/// The related backends are most likely *not* usable anymore and
/// the whole stack has to be recreated.
fn error(&mut self, evlh: &mut EventLoopHandle, error: IoError);
fn error(&mut self, evlh: &mut EventLoopHandle, device: &mut DrmDevice<B>, error: IoError);
}
/// Bind a `DrmDevice` to an EventLoop,
///
/// This will cause it to recieve events and feed them into an `DrmHandler`
pub fn drm_device_bind<H>(evlh: &mut EventLoopHandle, device: DrmDevice, handler: H)
-> IoResult<FdEventSource<(DrmDevice, H)>>
pub fn drm_device_bind<B, H>(evlh: &mut EventLoopHandle, device: DrmDevice<B>, handler: H)
-> IoResult<FdEventSource<(DrmDevice<B>, H)>>
where
H: DrmHandler + 'static,
B: Deref<Target = DrmBackend> + 'static,
H: DrmHandler<B> + 'static,
{
evlh.add_fd_event_source(
device.as_raw_fd(),
@ -488,28 +483,44 @@ where
)
}
fn fd_event_source_implementation<H>() -> FdEventSourceImpl<(DrmDevice, H)>
fn fd_event_source_implementation<B, H>() -> FdEventSourceImpl<(DrmDevice<B>, H)>
where
H: DrmHandler + 'static,
B: Deref<Target = DrmBackend> + 'static,
H: DrmHandler<B> + 'static,
{
FdEventSourceImpl {
ready: |evlh, id, _, _| {
use std::any::Any;
let &mut (ref dev, ref mut handler) = id;
let &mut (ref mut dev, ref mut handler) = id;
struct PageFlipHandler<'a, 'b, H: DrmHandler + 'static>(&'a mut H, &'b mut EventLoopHandle);
struct PageFlipHandler<
'a,
'b,
B: Deref<Target = DrmBackend> + 'static,
H: DrmHandler<B> + 'static,
> {
handler: &'a mut H,
evlh: &'b mut EventLoopHandle,
_marker: PhantomData<B>,
};
impl<'a, 'b, H: DrmHandler + 'static> crtc::PageFlipHandler<DrmDevice> for PageFlipHandler<'a, 'b, H> {
fn handle_event(&mut self, device: &DrmDevice, frame: u32, duration: Duration,
impl<'a, 'b, B, H> crtc::PageFlipHandler<DrmDevice<B>> for PageFlipHandler<'a, 'b, B, H>
where
B: Deref<Target = DrmBackend> + 'static,
H: DrmHandler<B> + 'static,
{
fn handle_event(&mut self, device: &mut DrmDevice<B>, frame: u32, duration: Duration,
userdata: Box<Any>) {
let id: Id = *userdata.downcast().unwrap();
if let Some(backend) = device.backends[id.raw()].upgrade() {
let crtc_id: crtc::Handle = *userdata.downcast().unwrap();
let token = device.backends.get(&crtc_id).cloned();
if let Some(token) = token {
// we can now unlock the buffer
backend.borrow().unlock_buffer();
trace!(device.logger, "Handling event for backend {:?}", id.raw());
(**self.evlh.state().get(&token)).unlock_buffer();
trace!(device.logger, "Handling event for backend {:?}", crtc_id);
// and then call the user to render the next frame
self.0.ready(self.1, id, frame, duration);
self.handler
.ready(self.evlh, device, &token, frame, duration);
}
}
}
@ -518,13 +529,17 @@ where
dev,
2,
None::<&mut ()>,
Some(&mut PageFlipHandler(handler, evlh)),
Some(&mut PageFlipHandler {
handler,
evlh,
_marker: PhantomData,
}),
None::<&mut ()>,
).unwrap();
},
error: |evlh, id, _, error| {
warn!(id.0.logger, "DrmDevice errored: {}", error);
id.1.error(evlh, error);
id.1.error(evlh, &mut id.0, error);
},
}
}

View File

@ -7,7 +7,6 @@ use glium::backend::{Backend, Context, Facade};
use glium::debug::DebugCallbackBehavior;
use std::ops::Deref;
use std::os::raw::c_void;
use std::rc::Rc;
impl From<SwapBuffersError> for GliumSwapBuffersError {
@ -69,15 +68,9 @@ impl<T: EGLGraphicsBackend> Facade for GliumGraphicsBackend<T> {
}
}
/// Converter trait to expose `glium` compatibility for all `EGLGraphicsBackend`s
pub trait IntoGlium: EGLGraphicsBackend + Sized {
/// Wrap the given `EGLGraphicsBackend` to a `GliumGraphicBackend`
fn into_glium(self) -> GliumGraphicsBackend<Self>;
}
impl<T: EGLGraphicsBackend + 'static> IntoGlium for T {
fn into_glium(self) -> GliumGraphicsBackend<Self> {
GliumGraphicsBackend::new(self)
impl<T: EGLGraphicsBackend + 'static> From<T> for GliumGraphicsBackend<T> {
fn from(backend: T) -> Self {
GliumGraphicsBackend::new(backend)
}
}