diff --git a/Cargo.toml b/Cargo.toml index ce826fd..41d31b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" } diff --git a/examples/drm.rs b/examples/drm.rs index 30541b0..3c1d4eb 100644 --- a/examples/drm.rs +++ b/examples/drm.rs @@ -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> = 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>, shell_state_token: StateToken>, compositor_token: CompositorToken, 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> for DrmHandlerImpl { + fn ready(&mut self, evlh: &mut EventLoopHandle, _device: &mut DrmDevice>, + backend: &StateToken>, _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>, + error: IoError) { panic!("{:?}", error); } } diff --git a/examples/helpers/glium.rs b/examples/helpers/glium.rs index 936d183..a70f91f 100644 --- a/examples/helpers/glium.rs +++ b/examples/helpers/glium.rs @@ -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 { - display: F, +pub struct GliumDrawer { + display: GliumGraphicsBackend, vertex_buffer: glium::VertexBuffer, index_buffer: glium::IndexBuffer, program: glium::Program, } -impl Deref for GliumDrawer { +impl Deref for GliumDrawer { type Target = F; fn deref(&self) -> &F { - &self.display + &*self.display } } -impl GliumDrawer { - pub fn new(display: F) -> GliumDrawer { +impl> + EGLGraphicsBackend + 'static> From for GliumDrawer { + fn from(backend: T) -> GliumDrawer { + 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 GliumDrawer { program, } } +} +impl GliumDrawer { 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 GliumDrawer { ) .unwrap(); } + + #[inline] + pub fn draw(&self) -> Frame { + self.display.draw() + } } diff --git a/examples/winit.rs b/examples/winit.rs index c1b63e9..037ece2 100644 --- a/examples/winit.rs +++ b/examples/winit.rs @@ -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: diff --git a/src/backend/drm/backend.rs b/src/backend/drm/backend.rs index 4d2ba49..65b8253 100644 --- a/src/backend/drm/backend.rs +++ b/src/backend/drm/backend.rs @@ -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>); - -impl DrmBackend { - pub(crate) fn new(drm: Rc>) -> DrmBackend { - DrmBackend(drm) - } -} - /* Dependency graph - drm @@ -32,7 +23,7 @@ impl DrmBackend { */ pub(crate) struct GbmTypes<'dev, 'context> { - cursor: BufferObject<'dev, ()>, + cursor: Cell>, 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, - own_id: Id, logger: ::slog::Logger, } -impl DrmBackendInternal { - pub(crate) fn new(context: Rc, crtc: crtc::Handle, mode: Mode, connectors: I, - own_id: usize, logger: L) - -> Result - where - I: Into>, - L: Into>, - { +impl DrmBackend { + pub(crate) fn new(context: Rc, crtc: crtc::Handle, mode: Mode, + connectors: Vec, logger: ::slog::Logger) + -> Result { // 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::>>()?; // 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 { - // 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, Vec>, 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()) } diff --git a/src/backend/drm/mod.rs b/src/backend/drm/mod.rs index 1dc2510..fea1d12 100644 --- a/src/backend/drm/mod.rs +++ b/src/backend/drm/mod.rs @@ -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 + 'static> { context: Rc, - backends: Vec>>, + backends: HashMap>, logger: ::slog::Logger, } -impl DrmDevice { +impl + Deref + 'static> DrmDevice { /// 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::(drm).chain_err(|| ErrorKind::GbmInitFailed) + GbmDevice::new_from_drm::>(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(&mut self, crtc: crtc::Handle, mode: Mode, connectors: I) -> Result + pub fn create_backend(&mut self, evlh: &mut EventLoopHandle, crtc: crtc::Handle, mode: Mode, + connectors: I) + -> Result> where I: Into>, { - 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 + 'static> AsRawFd for DrmDevice { fn as_raw_fd(&self) -> RawFd { self.context.head().head().as_raw_fd() } } -impl BasicDevice for DrmDevice {} -impl ControlDevice for DrmDevice {} +impl + 'static> BasicDevice for DrmDevice {} +impl + 'static> ControlDevice for DrmDevice {} /// Handler for drm node events /// /// See module-level documentation for its use -pub trait DrmHandler { +pub trait DrmHandler + '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, backend: &StateToken, + 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, 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(evlh: &mut EventLoopHandle, device: DrmDevice, handler: H) - -> IoResult> +pub fn drm_device_bind(evlh: &mut EventLoopHandle, device: DrmDevice, handler: H) + -> IoResult, H)>> where - H: DrmHandler + 'static, + B: Deref + 'static, + H: DrmHandler + 'static, { evlh.add_fd_event_source( device.as_raw_fd(), @@ -488,28 +483,44 @@ where ) } -fn fd_event_source_implementation() -> FdEventSourceImpl<(DrmDevice, H)> +fn fd_event_source_implementation() -> FdEventSourceImpl<(DrmDevice, H)> where - H: DrmHandler + 'static, + B: Deref + 'static, + H: DrmHandler + '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 + 'static, + H: DrmHandler + 'static, + > { + handler: &'a mut H, + evlh: &'b mut EventLoopHandle, + _marker: PhantomData, + }; - impl<'a, 'b, H: DrmHandler + 'static> crtc::PageFlipHandler for PageFlipHandler<'a, 'b, H> { - fn handle_event(&mut self, device: &DrmDevice, frame: u32, duration: Duration, + impl<'a, 'b, B, H> crtc::PageFlipHandler> for PageFlipHandler<'a, 'b, B, H> + where + B: Deref + 'static, + H: DrmHandler + 'static, + { + fn handle_event(&mut self, device: &mut DrmDevice, frame: u32, duration: Duration, userdata: Box) { - 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); }, } } diff --git a/src/backend/graphics/glium.rs b/src/backend/graphics/glium.rs index 3a820d3..c7ea06c 100644 --- a/src/backend/graphics/glium.rs +++ b/src/backend/graphics/glium.rs @@ -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 for GliumSwapBuffersError { @@ -69,15 +68,9 @@ impl Facade for GliumGraphicsBackend { } } -/// 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; -} - -impl IntoGlium for T { - fn into_glium(self) -> GliumGraphicsBackend { - GliumGraphicsBackend::new(self) +impl From for GliumGraphicsBackend { + fn from(backend: T) -> Self { + GliumGraphicsBackend::new(backend) } }