Store DrmBackend in EventLoop state
This commit is contained in:
parent
0758ec98ba
commit
518f7dbdfc
|
@ -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" }
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue