Fixup drm backends for egl hardware-acceleration
- Decouple DrmBackend and DrmDevice through Weak references - Move EGL-WlBuffer related functionality into it's own trait - Impl new trait by every struct that can theoretically bind the display although it is no rendering target (no EGLGraphicsBackend), e.g. DrmDevice - Move texture binding into own struct `EGLDisplay` created by `bind_wl_display` that can be passed around freely - Add device num to DrmDevice for HashMap storage - Fixup and enable acceleration on drm and udev examples made possible by the previous changes.
This commit is contained in:
parent
c63d693a74
commit
a527456ba6
111
examples/drm.rs
111
examples/drm.rs
|
@ -13,25 +13,41 @@ extern crate slog_term;
|
||||||
|
|
||||||
mod helpers;
|
mod helpers;
|
||||||
|
|
||||||
|
use drm::Device as BasicDevice;
|
||||||
use drm::control::{Device as ControlDevice, ResourceInfo};
|
use drm::control::{Device as ControlDevice, ResourceInfo};
|
||||||
use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState};
|
use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState};
|
||||||
use drm::control::crtc;
|
use drm::control::crtc;
|
||||||
use drm::control::encoder::Info as EncoderInfo;
|
use drm::control::encoder::Info as EncoderInfo;
|
||||||
use drm::result::Error as DrmError;
|
use drm::result::Error as DrmError;
|
||||||
use glium::Surface;
|
use glium::{Blend, Surface};
|
||||||
use helpers::{init_shell, GliumDrawer, MyWindowMap, Roles, SurfaceData};
|
use helpers::{init_shell, GliumDrawer, MyWindowMap, Roles, SurfaceData, Buffer};
|
||||||
use slog::{Drain, Logger};
|
use slog::{Drain, Logger};
|
||||||
use smithay::backend::drm::{drm_device_bind, DrmBackend, DrmDevice, DrmHandler};
|
use smithay::backend::drm::{drm_device_bind, DrmBackend, DrmDevice, DrmHandler};
|
||||||
use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
||||||
|
use smithay::backend::graphics::egl::wayland::{Format, EGLDisplay, EGLWaylandExtensions};
|
||||||
use smithay::wayland::compositor::{CompositorToken, SubsurfaceRole, TraversalAction};
|
use smithay::wayland::compositor::{CompositorToken, SubsurfaceRole, TraversalAction};
|
||||||
use smithay::wayland::compositor::roles::Role;
|
use smithay::wayland::compositor::roles::Role;
|
||||||
use smithay::wayland::shell::ShellState;
|
use smithay::wayland::shell::ShellState;
|
||||||
use smithay::wayland::shm::init_shm_global;
|
use smithay::wayland::shm::init_shm_global;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::fs::OpenOptions;
|
use std::fs::{File, OpenOptions};
|
||||||
|
use std::os::unix::io::RawFd;
|
||||||
|
use std::os::unix::io::AsRawFd;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use wayland_server::{StateToken, StateProxy};
|
use wayland_server::{StateToken};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Card(File);
|
||||||
|
|
||||||
|
impl AsRawFd for Card {
|
||||||
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
|
self.0.as_raw_fd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BasicDevice for Card {}
|
||||||
|
impl ControlDevice for Card {}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// A logger facility, here we use the terminal for this example
|
// A logger facility, here we use the terminal for this example
|
||||||
|
@ -50,8 +66,8 @@ fn main() {
|
||||||
let mut options = OpenOptions::new();
|
let mut options = OpenOptions::new();
|
||||||
options.read(true);
|
options.read(true);
|
||||||
options.write(true);
|
options.write(true);
|
||||||
let mut device: DrmDevice<GliumDrawer<DrmBackend>> =
|
let mut device =
|
||||||
DrmDevice::new_from_file(options.clone().open("/dev/dri/card0").unwrap(), log.clone()).unwrap();
|
DrmDevice::new(Card(options.clone().open("/dev/dri/card0").unwrap()), log.clone()).unwrap();
|
||||||
|
|
||||||
// Get a set of all modesetting resource handles (excluding planes):
|
// Get a set of all modesetting resource handles (excluding planes):
|
||||||
let res_handles = device.resource_handles().unwrap();
|
let res_handles = device.resource_handles().unwrap();
|
||||||
|
@ -81,27 +97,35 @@ fn main() {
|
||||||
// Assuming we found a good connector and loaded the info into `connector_info`
|
// Assuming we found a good connector and loaded the info into `connector_info`
|
||||||
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.)
|
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
|
// Initialize the hardware backend
|
||||||
let renderer = device
|
let renderer = GliumDrawer::from(device
|
||||||
.create_backend(event_loop.state(), crtc, mode, vec![connector_info.handle()])
|
.create_backend(crtc, mode, vec![connector_info.handle()])
|
||||||
.unwrap();
|
.unwrap());
|
||||||
|
{
|
||||||
/*
|
/*
|
||||||
* Initialize glium
|
* Initialize glium
|
||||||
*/
|
*/
|
||||||
let mut frame = event_loop.state().get(renderer).draw();
|
let mut frame = renderer.draw();
|
||||||
frame.clear_color(0.8, 0.8, 0.9, 1.0);
|
frame.clear_color(0.8, 0.8, 0.9, 1.0);
|
||||||
frame.finish().unwrap();
|
frame.finish().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let egl_display = Rc::new(RefCell::new(
|
||||||
|
if let Ok(egl_display) = renderer.bind_wl_display(&display) {
|
||||||
|
info!(log, "EGL hardware-acceleration enabled");
|
||||||
|
Some(egl_display)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize the globals
|
* Initialize the globals
|
||||||
*/
|
*/
|
||||||
|
|
||||||
init_shm_global(&mut event_loop, vec![], log.clone());
|
init_shm_global(&mut event_loop, vec![], log.clone());
|
||||||
|
|
||||||
let (compositor_token, shell_state_token, window_map) = init_shell(&mut event_loop, log.clone());
|
let (compositor_token, shell_state_token, window_map) = init_shell(&mut event_loop, log.clone(), egl_display.clone());
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add a listening socket:
|
* Add a listening socket:
|
||||||
|
@ -120,6 +144,7 @@ fn main() {
|
||||||
shell_state_token,
|
shell_state_token,
|
||||||
compositor_token,
|
compositor_token,
|
||||||
window_map: window_map.clone(),
|
window_map: window_map.clone(),
|
||||||
|
drawer: renderer,
|
||||||
logger: log,
|
logger: log,
|
||||||
},
|
},
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
@ -133,22 +158,20 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DrmHandlerImpl {
|
pub struct DrmHandlerImpl {
|
||||||
shell_state_token: StateToken<ShellState<SurfaceData, Roles, (), ()>>,
|
shell_state_token: StateToken<ShellState<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>, ()>>,
|
||||||
compositor_token: CompositorToken<SurfaceData, Roles, ()>,
|
compositor_token: CompositorToken<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>>,
|
||||||
window_map: Rc<RefCell<MyWindowMap>>,
|
window_map: Rc<RefCell<MyWindowMap>>,
|
||||||
|
drawer: GliumDrawer<DrmBackend<Card>>,
|
||||||
logger: ::slog::Logger,
|
logger: ::slog::Logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DrmHandler<GliumDrawer<DrmBackend>> for DrmHandlerImpl {
|
impl DrmHandler<Card> for DrmHandlerImpl {
|
||||||
fn ready<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, _device: &mut DrmDevice<GliumDrawer<DrmBackend>>,
|
fn ready(&mut self, _device: &mut DrmDevice<Card>, _crtc: crtc::Handle, _frame: u32, _duration: Duration) {
|
||||||
backend: &StateToken<GliumDrawer<DrmBackend>>, _crtc: crtc::Handle, _frame: u32, _duration: Duration) {
|
let mut frame = self.drawer.draw();
|
||||||
let state = state.into();
|
|
||||||
let drawer = state.get(backend);
|
|
||||||
let mut frame = drawer.draw();
|
|
||||||
frame.clear_color(0.8, 0.8, 0.9, 1.0);
|
frame.clear_color(0.8, 0.8, 0.9, 1.0);
|
||||||
// redraw the frame, in a simple but inneficient way
|
// redraw the frame, in a simple but inneficient way
|
||||||
{
|
{
|
||||||
let screen_dimensions = drawer.get_framebuffer_dimensions();
|
let screen_dimensions = self.drawer.get_framebuffer_dimensions();
|
||||||
self.window_map
|
self.window_map
|
||||||
.borrow()
|
.borrow()
|
||||||
.with_windows_from_bottom_to_top(|toplevel_surface, initial_place| {
|
.with_windows_from_bottom_to_top(|toplevel_surface, initial_place| {
|
||||||
|
@ -159,18 +182,52 @@ impl DrmHandler<GliumDrawer<DrmBackend>> for DrmHandlerImpl {
|
||||||
wl_surface,
|
wl_surface,
|
||||||
initial_place,
|
initial_place,
|
||||||
|_surface, attributes, role, &(mut x, mut y)| {
|
|_surface, attributes, role, &(mut x, mut y)| {
|
||||||
if let Some((ref contents, (w, h))) = attributes.user_data.buffer {
|
|
||||||
// there is actually something to draw !
|
// there is actually something to draw !
|
||||||
|
if attributes.user_data.texture.is_none() {
|
||||||
|
let mut remove = false;
|
||||||
|
match attributes.user_data.buffer {
|
||||||
|
Some(Buffer::Egl { ref images }) => {
|
||||||
|
match images.format {
|
||||||
|
Format::RGB | Format::RGBA => {
|
||||||
|
attributes.user_data.texture = self.drawer.texture_from_egl(&images);
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
// we don't handle the more complex formats here.
|
||||||
|
attributes.user_data.texture = None;
|
||||||
|
remove = true;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
Some(Buffer::Shm { ref data, ref size }) => {
|
||||||
|
attributes.user_data.texture = Some(self.drawer.texture_from_mem(data, *size));
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
if remove {
|
||||||
|
attributes.user_data.buffer = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ref texture) = attributes.user_data.texture {
|
||||||
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
|
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
|
||||||
x += subdata.x;
|
x += subdata.x;
|
||||||
y += subdata.y;
|
y += subdata.y;
|
||||||
}
|
}
|
||||||
drawer.render(
|
info!(self.logger, "Render window");
|
||||||
|
self.drawer.render_texture(
|
||||||
&mut frame,
|
&mut frame,
|
||||||
contents,
|
texture,
|
||||||
(w, h),
|
match *attributes.user_data.buffer.as_ref().unwrap() {
|
||||||
|
Buffer::Egl { ref images } => images.y_inverted,
|
||||||
|
Buffer::Shm { .. } => false,
|
||||||
|
},
|
||||||
|
match *attributes.user_data.buffer.as_ref().unwrap() {
|
||||||
|
Buffer::Egl { ref images } => (images.width, images.height),
|
||||||
|
Buffer::Shm { ref size, .. } => *size,
|
||||||
|
},
|
||||||
(x, y),
|
(x, y),
|
||||||
screen_dimensions,
|
screen_dimensions,
|
||||||
|
Blend::alpha_blending(),
|
||||||
);
|
);
|
||||||
TraversalAction::DoChildren((x, y))
|
TraversalAction::DoChildren((x, y))
|
||||||
} else {
|
} else {
|
||||||
|
@ -186,7 +243,7 @@ impl DrmHandler<GliumDrawer<DrmBackend>> for DrmHandlerImpl {
|
||||||
frame.finish().unwrap();
|
frame.finish().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn error<'a, S: Into<StateProxy<'a>>>(&mut self, _state: S, _device: &mut DrmDevice<GliumDrawer<DrmBackend>>,
|
fn error(&mut self, _device: &mut DrmDevice<Card>,
|
||||||
error: DrmError) {
|
error: DrmError) {
|
||||||
panic!("{:?}", error);
|
panic!("{:?}", error);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,13 @@ use glium::backend::Facade;
|
||||||
use glium::index::PrimitiveType;
|
use glium::index::PrimitiveType;
|
||||||
use glium::texture::{MipmapsOption, UncompressedFloatFormat, Texture2d};
|
use glium::texture::{MipmapsOption, UncompressedFloatFormat, Texture2d};
|
||||||
use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
||||||
use smithay::backend::graphics::egl::wayland::{Format, EGLImages};
|
use smithay::backend::graphics::egl::error::Result as EGLResult;
|
||||||
|
use smithay::backend::graphics::egl::wayland::{Format, EGLImages, EGLDisplay, EGLWaylandExtensions, BufferAccessError};
|
||||||
use smithay::backend::graphics::glium::GliumGraphicsBackend;
|
use smithay::backend::graphics::glium::GliumGraphicsBackend;
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
use wayland_server::Display;
|
||||||
|
use wayland_server::protocol::wl_buffer::WlBuffer;
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
struct Vertex {
|
struct Vertex {
|
||||||
|
@ -135,15 +138,14 @@ impl<F: EGLGraphicsBackend + 'static> GliumDrawer<F> {
|
||||||
images.width,
|
images.width,
|
||||||
images.height,
|
images.height,
|
||||||
).unwrap();
|
).unwrap();
|
||||||
if let Err(_) = unsafe { self.display.get_context().exec_in_context(|| {
|
unsafe { images.bind_to_texture(0, opengl_texture.get_id()).expect("Failed to bind to texture"); }
|
||||||
images.bind_to_texture(0, opengl_texture.get_id())
|
|
||||||
}) } { return None };
|
|
||||||
Some(opengl_texture)
|
Some(opengl_texture)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_texture(&self, target: &mut glium::Frame, texture: &Texture2d,
|
pub fn render_texture(&self, target: &mut glium::Frame, texture: &Texture2d,
|
||||||
y_inverted: bool, surface_dimensions: (u32, u32),
|
y_inverted: bool, surface_dimensions: (u32, u32),
|
||||||
surface_location: (i32, i32), screen_size: (u32, u32))
|
surface_location: (i32, i32), screen_size: (u32, u32),
|
||||||
|
blending: glium::Blend)
|
||||||
{
|
{
|
||||||
let xscale = 2.0 * (surface_dimensions.0 as f32) / (screen_size.0 as f32);
|
let xscale = 2.0 * (surface_dimensions.0 as f32) / (screen_size.0 as f32);
|
||||||
let mut yscale = -2.0 * (surface_dimensions.1 as f32) / (screen_size.1 as f32);
|
let mut yscale = -2.0 * (surface_dimensions.1 as f32) / (screen_size.1 as f32);
|
||||||
|
@ -173,24 +175,9 @@ impl<F: EGLGraphicsBackend + 'static> GliumDrawer<F> {
|
||||||
&self.program,
|
&self.program,
|
||||||
&uniforms,
|
&uniforms,
|
||||||
&glium::DrawParameters {
|
&glium::DrawParameters {
|
||||||
blend: glium::Blend {
|
blend: blending,
|
||||||
color: glium::BlendingFunction::Addition {
|
|
||||||
source: glium::LinearBlendingFactor::One,
|
|
||||||
destination: glium::LinearBlendingFactor::OneMinusSourceAlpha,
|
|
||||||
},
|
|
||||||
alpha: glium::BlendingFunction::Addition {
|
|
||||||
source: glium::LinearBlendingFactor::One,
|
|
||||||
destination: glium::LinearBlendingFactor::OneMinusSourceAlpha,
|
|
||||||
},
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
}
|
||||||
depth: glium::Depth {
|
|
||||||
test: glium::DepthTest::IfLess,
|
|
||||||
write: false,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
.. Default::default()
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -200,3 +187,12 @@ impl<F: EGLGraphicsBackend + 'static> GliumDrawer<F> {
|
||||||
self.display.draw()
|
self.display.draw()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<G: EGLWaylandExtensions + EGLGraphicsBackend + 'static> EGLWaylandExtensions for GliumDrawer<G> {
|
||||||
|
fn bind_wl_display(&self, display: &Display) -> EGLResult<EGLDisplay> {
|
||||||
|
self.display.bind_wl_display(display)
|
||||||
|
}
|
||||||
|
fn unbind_wl_display(&self, display: &Display) -> EGLResult<()> {
|
||||||
|
self.display.unbind_wl_display(display)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use super::{GliumDrawer, WindowMap};
|
use super::WindowMap;
|
||||||
use smithay::backend::graphics::egl::wayland::{Format, BufferAccessError};
|
use smithay::backend::graphics::egl::wayland::{Format, BufferAccessError};
|
||||||
use glium::texture::Texture2d;
|
use glium::texture::Texture2d;
|
||||||
use rand;
|
use rand;
|
||||||
use smithay::backend::graphics::egl::{EGLGraphicsBackend, EGLImages};
|
use smithay::backend::graphics::egl::wayland::{EGLDisplay, EGLImages};
|
||||||
use smithay::wayland::compositor::{compositor_init, CompositorToken, SurfaceAttributes,
|
use smithay::wayland::compositor::{compositor_init, CompositorToken, SurfaceAttributes,
|
||||||
SurfaceUserImplementation};
|
SurfaceUserImplementation};
|
||||||
use smithay::wayland::shell::{shell_init, PopupConfigure, ShellState, ShellSurfaceRole,
|
use smithay::wayland::shell::{shell_init, PopupConfigure, ShellState, ShellSurfaceRole,
|
||||||
|
@ -10,7 +10,6 @@ use smithay::wayland::shell::{shell_init, PopupConfigure, ShellState, ShellSurfa
|
||||||
use smithay::wayland::shm::with_buffer_contents as shm_buffer_contents;
|
use smithay::wayland::shm::with_buffer_contents as shm_buffer_contents;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::borrow::Borrow;
|
|
||||||
use wayland_server::{EventLoop, StateToken};
|
use wayland_server::{EventLoop, StateToken};
|
||||||
|
|
||||||
define_roles!(Roles => [ ShellSurface, ShellSurfaceRole ] );
|
define_roles!(Roles => [ ShellSurface, ShellSurfaceRole ] );
|
||||||
|
@ -28,17 +27,22 @@ pub enum Buffer {
|
||||||
|
|
||||||
unsafe impl Send for Buffer {}
|
unsafe impl Send for Buffer {}
|
||||||
|
|
||||||
pub fn surface_implementation<G: EGLGraphicsBackend + 'static>() -> SurfaceUserImplementation<SurfaceData, Roles, Rc<GliumDrawer<G>>> {
|
pub fn surface_implementation() -> SurfaceUserImplementation<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>> {
|
||||||
SurfaceUserImplementation {
|
SurfaceUserImplementation {
|
||||||
commit: |_, drawer, surface, token| {
|
commit: |_, display, surface, token| {
|
||||||
// we retrieve the contents of the associated buffer and copy it
|
// we retrieve the contents of the associated buffer and copy it
|
||||||
token.with_surface_data(surface, |attributes| {
|
token.with_surface_data(surface, |attributes| {
|
||||||
match attributes.buffer.take() {
|
match attributes.buffer.take() {
|
||||||
Some(Some((buffer, (_x, _y)))) => {
|
Some(Some((buffer, (_x, _y)))) => {
|
||||||
// we ignore hotspot coordinates in this simple example
|
// we ignore hotspot coordinates in this simple example
|
||||||
match <GliumDrawer<G> as Borrow<G>>::borrow(&**drawer).egl_buffer_contents(buffer) {
|
match if let Some(display) = display.borrow().as_ref() {
|
||||||
|
display.egl_buffer_contents(buffer)
|
||||||
|
} else {
|
||||||
|
Err(BufferAccessError::NotManaged(buffer))
|
||||||
|
}
|
||||||
|
{
|
||||||
Ok(images) => {
|
Ok(images) => {
|
||||||
let format = match images.format {
|
match images.format {
|
||||||
Format::RGB => {},
|
Format::RGB => {},
|
||||||
Format::RGBA => {},
|
Format::RGBA => {},
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -48,7 +52,7 @@ pub fn surface_implementation<G: EGLGraphicsBackend + 'static>() -> SurfaceUserI
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
attributes.user_data.texture = drawer.texture_from_egl(&images);
|
attributes.user_data.texture = None;
|
||||||
attributes.user_data.buffer = Some(Buffer::Egl { images });
|
attributes.user_data.buffer = Some(Buffer::Egl { images });
|
||||||
},
|
},
|
||||||
Err(BufferAccessError::NotManaged(buffer)) => {
|
Err(BufferAccessError::NotManaged(buffer)) => {
|
||||||
|
@ -62,9 +66,9 @@ pub fn surface_implementation<G: EGLGraphicsBackend + 'static>() -> SurfaceUserI
|
||||||
new_vec
|
new_vec
|
||||||
.extend(&slice[(offset + i * stride)..(offset + i * stride + width * 4)]);
|
.extend(&slice[(offset + i * stride)..(offset + i * stride + width * 4)]);
|
||||||
}
|
}
|
||||||
attributes.user_data.texture = Some(drawer.texture_from_mem(&new_vec, (data.width as u32, data.height as u32)));
|
attributes.user_data.texture = None;
|
||||||
attributes.user_data.buffer = Some(Buffer::Shm { data: new_vec, size: (data.width as u32, data.height as u32) });
|
attributes.user_data.buffer = Some(Buffer::Shm { data: new_vec, size: (data.width as u32, data.height as u32) });
|
||||||
}).unwrap();
|
}).expect("Got EGL buffer with no set EGLDisplay. You need to unbind your EGLContexts before dropping them!");
|
||||||
buffer.release();
|
buffer.release();
|
||||||
},
|
},
|
||||||
Err(err) => panic!("EGL error: {}", err),
|
Err(err) => panic!("EGL error: {}", err),
|
||||||
|
@ -85,15 +89,14 @@ pub fn surface_implementation<G: EGLGraphicsBackend + 'static>() -> SurfaceUserI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ShellIData<F, G: EGLGraphicsBackend + 'static> {
|
pub struct ShellIData<F> {
|
||||||
pub token: CompositorToken<SurfaceData, Roles, Rc<GliumDrawer<G>>>,
|
pub token: CompositorToken<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>>,
|
||||||
pub window_map: Rc<RefCell<super::WindowMap<SurfaceData, Roles, Rc<GliumDrawer<G>>, (), F>>>,
|
pub window_map: Rc<RefCell<super::WindowMap<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>, (), F>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn shell_implementation<F, G>() -> ShellSurfaceUserImplementation<SurfaceData, Roles, Rc<GliumDrawer<G>>, ShellIData<F, G>, ()>
|
pub fn shell_implementation<F>() -> ShellSurfaceUserImplementation<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>, ShellIData<F>, ()>
|
||||||
where
|
where
|
||||||
F: Fn(&SurfaceAttributes<SurfaceData>) -> Option<(i32, i32)>,
|
F: Fn(&SurfaceAttributes<SurfaceData>) -> Option<(i32, i32)>,
|
||||||
G: EGLGraphicsBackend + 'static,
|
|
||||||
{
|
{
|
||||||
ShellSurfaceUserImplementation {
|
ShellSurfaceUserImplementation {
|
||||||
new_client: |_, _, _| {},
|
new_client: |_, _, _| {},
|
||||||
|
@ -145,20 +148,20 @@ fn get_size(attrs: &SurfaceAttributes<SurfaceData>) -> Option<(i32, i32)> {
|
||||||
.map(|(x, y)| (x as i32, y as i32))
|
.map(|(x, y)| (x as i32, y as i32))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type MyWindowMap<G: EGLGraphicsBackend + 'static> = WindowMap<
|
pub type MyWindowMap = WindowMap<
|
||||||
SurfaceData,
|
SurfaceData,
|
||||||
Roles,
|
Roles,
|
||||||
Rc<GliumDrawer<G>>,
|
Rc<RefCell<Option<EGLDisplay>>>,
|
||||||
(),
|
(),
|
||||||
fn(&SurfaceAttributes<SurfaceData>) -> Option<(i32, i32)>,
|
fn(&SurfaceAttributes<SurfaceData>) -> Option<(i32, i32)>,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
pub fn init_shell<G: EGLGraphicsBackend + 'static>(
|
pub fn init_shell(
|
||||||
evl: &mut EventLoop, log: ::slog::Logger, data: Rc<GliumDrawer<G>>)
|
evl: &mut EventLoop, log: ::slog::Logger, data: Rc<RefCell<Option<EGLDisplay>>>)
|
||||||
-> (
|
-> (
|
||||||
CompositorToken<SurfaceData, Roles, Rc<GliumDrawer<G>>>,
|
CompositorToken<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>>,
|
||||||
StateToken<ShellState<SurfaceData, Roles, Rc<GliumDrawer<G>>, ()>>,
|
StateToken<ShellState<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>, ()>>,
|
||||||
Rc<RefCell<MyWindowMap<G>>>,
|
Rc<RefCell<MyWindowMap>>,
|
||||||
) {
|
) {
|
||||||
let (compositor_token, _, _) = compositor_init(evl, surface_implementation(), data, log.clone());
|
let (compositor_token, _, _) = compositor_init(evl, surface_implementation(), data, log.clone());
|
||||||
|
|
||||||
|
|
166
examples/udev.rs
166
examples/udev.rs
|
@ -19,24 +19,26 @@ extern crate ctrlc;
|
||||||
|
|
||||||
mod helpers;
|
mod helpers;
|
||||||
|
|
||||||
|
use drm::Device as BasicDevice;
|
||||||
use drm::control::{Device as ControlDevice, ResourceInfo};
|
use drm::control::{Device as ControlDevice, ResourceInfo};
|
||||||
use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState};
|
use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState};
|
||||||
use drm::control::encoder::Info as EncoderInfo;
|
use drm::control::encoder::Info as EncoderInfo;
|
||||||
use drm::control::crtc;
|
use drm::control::crtc;
|
||||||
use drm::result::Error as DrmError;
|
use drm::result::Error as DrmError;
|
||||||
use glium::Surface;
|
use glium::{Blend, Surface};
|
||||||
use image::{ImageBuffer, Rgba};
|
use image::{ImageBuffer, Rgba};
|
||||||
use libinput::{Libinput, Device as LibinputDevice, event};
|
use libinput::{Libinput, Device as LibinputDevice, event};
|
||||||
use libinput::event::keyboard::KeyboardEventTrait;
|
use libinput::event::keyboard::KeyboardEventTrait;
|
||||||
use helpers::{init_shell, GliumDrawer, MyWindowMap, Roles, SurfaceData};
|
use helpers::{init_shell, GliumDrawer, MyWindowMap, Roles, SurfaceData, Buffer};
|
||||||
use slog::{Drain, Logger};
|
use slog::{Drain, Logger};
|
||||||
use smithay::backend::drm::{DrmBackend, DrmDevice, DrmHandler};
|
use smithay::backend::drm::{DrmBackend, DrmDevice, DrmHandler, DevPath};
|
||||||
use smithay::backend::graphics::GraphicsBackend;
|
use smithay::backend::graphics::GraphicsBackend;
|
||||||
use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
||||||
|
use smithay::backend::graphics::egl::wayland::{EGLWaylandExtensions, EGLDisplay, Format};
|
||||||
use smithay::backend::input::{self, Event, InputBackend, InputHandler, KeyboardKeyEvent, PointerButtonEvent,
|
use smithay::backend::input::{self, Event, InputBackend, InputHandler, KeyboardKeyEvent, PointerButtonEvent,
|
||||||
PointerAxisEvent, KeyState};
|
PointerAxisEvent, KeyState};
|
||||||
use smithay::backend::libinput::{LibinputInputBackend, libinput_bind, PointerAxisEvent as LibinputPointerAxisEvent, LibinputSessionInterface};
|
use smithay::backend::libinput::{LibinputInputBackend, libinput_bind, PointerAxisEvent as LibinputPointerAxisEvent, LibinputSessionInterface};
|
||||||
use smithay::backend::udev::{UdevBackend, UdevHandler, udev_backend_bind};
|
use smithay::backend::udev::{UdevBackend, UdevHandler, udev_backend_bind, primary_gpu, SessionFdDrmDevice};
|
||||||
use smithay::backend::session::{Session, SessionNotifier};
|
use smithay::backend::session::{Session, SessionNotifier};
|
||||||
use smithay::backend::session::direct::{direct_session_bind, DirectSession};
|
use smithay::backend::session::direct::{direct_session_bind, DirectSession};
|
||||||
use smithay::wayland::compositor::{CompositorToken, SubsurfaceRole, TraversalAction};
|
use smithay::wayland::compositor::{CompositorToken, SubsurfaceRole, TraversalAction};
|
||||||
|
@ -46,15 +48,17 @@ use smithay::wayland::seat::{KeyboardHandle, PointerHandle, Seat};
|
||||||
use smithay::wayland::shell::ShellState;
|
use smithay::wayland::shell::ShellState;
|
||||||
use smithay::wayland::shm::init_shm_global;
|
use smithay::wayland::shm::init_shm_global;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashMap;
|
||||||
use std::io::Error as IoError;
|
use std::io::Error as IoError;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
use xkbcommon::xkb::keysyms as xkb;
|
use xkbcommon::xkb::keysyms as xkb;
|
||||||
use wayland_server::{StateToken, StateProxy};
|
use wayland_server::{Display, StateToken, StateProxy};
|
||||||
use wayland_server::protocol::{wl_output, wl_pointer};
|
use wayland_server::protocol::{wl_output, wl_pointer};
|
||||||
|
|
||||||
struct LibinputInputHandler {
|
struct LibinputInputHandler {
|
||||||
|
@ -172,6 +176,8 @@ impl InputHandler<LibinputInputBackend> for LibinputInputHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
let active_egl_context = Rc::new(RefCell::new(None));
|
||||||
|
|
||||||
// A logger facility, here we use the terminal for this example
|
// A logger facility, here we use the terminal for this example
|
||||||
let log = Logger::root(
|
let log = Logger::root(
|
||||||
slog_term::FullFormat::new(slog_term::PlainSyncDecorator::new(std::io::stdout())).build().fuse(),
|
slog_term::FullFormat::new(slog_term::PlainSyncDecorator::new(std::io::stdout())).build().fuse(),
|
||||||
|
@ -181,12 +187,19 @@ fn main() {
|
||||||
// Initialize the wayland server
|
// Initialize the wayland server
|
||||||
let (mut display, mut event_loop) = wayland_server::create_display();
|
let (mut display, mut event_loop) = wayland_server::create_display();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add a listening socket
|
||||||
|
*/
|
||||||
|
let name = display.add_socket_auto().unwrap().into_string().unwrap();
|
||||||
|
println!("Listening on socket: {}", name);
|
||||||
|
let display = Rc::new(display);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize the compositor
|
* Initialize the compositor
|
||||||
*/
|
*/
|
||||||
init_shm_global(&mut event_loop, vec![], log.clone());
|
init_shm_global(&mut event_loop, vec![], log.clone());
|
||||||
|
|
||||||
let (compositor_token, shell_state_token, window_map) = init_shell(&mut event_loop, log.clone());
|
let (compositor_token, shell_state_token, window_map) = init_shell(&mut event_loop, log.clone(), active_egl_context.clone());
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize session on the current tty
|
* Initialize session on the current tty
|
||||||
|
@ -206,11 +219,19 @@ fn main() {
|
||||||
* Initialize the udev backend
|
* Initialize the udev backend
|
||||||
*/
|
*/
|
||||||
let context = udev::Context::new().unwrap();
|
let context = udev::Context::new().unwrap();
|
||||||
|
let seat = session.seat();
|
||||||
|
|
||||||
|
let primary_gpu = primary_gpu(&context, &seat).unwrap_or_default();
|
||||||
|
|
||||||
let bytes = include_bytes!("resources/cursor2.rgba");
|
let bytes = include_bytes!("resources/cursor2.rgba");
|
||||||
let udev_token
|
let udev_token
|
||||||
= UdevBackend::new(&mut event_loop, &context, session.clone(), UdevHandlerImpl {
|
= UdevBackend::new(&mut event_loop, &context, session.clone(), UdevHandlerImpl {
|
||||||
shell_state_token,
|
shell_state_token,
|
||||||
compositor_token,
|
compositor_token,
|
||||||
|
active_egl_context,
|
||||||
|
backends: HashMap::new(),
|
||||||
|
display: display.clone(),
|
||||||
|
primary_gpu,
|
||||||
window_map: window_map.clone(),
|
window_map: window_map.clone(),
|
||||||
pointer_location: pointer_location.clone(),
|
pointer_location: pointer_location.clone(),
|
||||||
pointer_image: ImageBuffer::from_raw(64, 64, bytes.to_vec()).unwrap(),
|
pointer_image: ImageBuffer::from_raw(64, 64, bytes.to_vec()).unwrap(),
|
||||||
|
@ -266,7 +287,6 @@ fn main() {
|
||||||
/*
|
/*
|
||||||
* Initialize libinput backend
|
* Initialize libinput backend
|
||||||
*/
|
*/
|
||||||
let seat = session.seat();
|
|
||||||
let mut libinput_context = Libinput::new_from_udev::<LibinputSessionInterface<Rc<RefCell<DirectSession>>>>(session.into(), &context);
|
let mut libinput_context = Libinput::new_from_udev::<LibinputSessionInterface<Rc<RefCell<DirectSession>>>>(session.into(), &context);
|
||||||
let libinput_session_id = notifier.register(libinput_context.clone());
|
let libinput_session_id = notifier.register(libinput_context.clone());
|
||||||
libinput_context.udev_assign_seat(&seat).unwrap();
|
libinput_context.udev_assign_seat(&seat).unwrap();
|
||||||
|
@ -286,12 +306,6 @@ fn main() {
|
||||||
let session_event_source = direct_session_bind(notifier, &mut event_loop, log.clone()).unwrap();
|
let session_event_source = direct_session_bind(notifier, &mut event_loop, log.clone()).unwrap();
|
||||||
let udev_event_source = udev_backend_bind(&mut event_loop, udev_token).unwrap();
|
let udev_event_source = udev_backend_bind(&mut event_loop, udev_token).unwrap();
|
||||||
|
|
||||||
/*
|
|
||||||
* Add a listening socket
|
|
||||||
*/
|
|
||||||
let name = display.add_socket_auto().unwrap().into_string().unwrap();
|
|
||||||
println!("Listening on socket: {}", name);
|
|
||||||
|
|
||||||
while running.load(Ordering::SeqCst) {
|
while running.load(Ordering::SeqCst) {
|
||||||
event_loop.dispatch(Some(16));
|
event_loop.dispatch(Some(16));
|
||||||
display.flush_clients();
|
display.flush_clients();
|
||||||
|
@ -312,8 +326,12 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct UdevHandlerImpl {
|
struct UdevHandlerImpl {
|
||||||
shell_state_token: StateToken<ShellState<SurfaceData, Roles, (), ()>>,
|
shell_state_token: StateToken<ShellState<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>, ()>>,
|
||||||
compositor_token: CompositorToken<SurfaceData, Roles, ()>,
|
compositor_token: CompositorToken<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>>,
|
||||||
|
active_egl_context: Rc<RefCell<Option<EGLDisplay>>>,
|
||||||
|
backends: HashMap<u64, Rc<RefCell<HashMap<crtc::Handle, GliumDrawer<DrmBackend<SessionFdDrmDevice>>>>>>,
|
||||||
|
display: Rc<Display>,
|
||||||
|
primary_gpu: Option<PathBuf>,
|
||||||
window_map: Rc<RefCell<MyWindowMap>>,
|
window_map: Rc<RefCell<MyWindowMap>>,
|
||||||
pointer_location: Rc<RefCell<(f64, f64)>>,
|
pointer_location: Rc<RefCell<(f64, f64)>>,
|
||||||
pointer_image: ImageBuffer<Rgba<u8>, Vec<u8>>,
|
pointer_image: ImageBuffer<Rgba<u8>, Vec<u8>>,
|
||||||
|
@ -321,7 +339,7 @@ struct UdevHandlerImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UdevHandlerImpl {
|
impl UdevHandlerImpl {
|
||||||
pub fn scan_connectors<'a, S: Into<StateProxy<'a>>>(&self, state: S, device: &mut DrmDevice<GliumDrawer<DrmBackend>>) {
|
pub fn scan_connectors(&self, device: &mut DrmDevice<SessionFdDrmDevice>) -> HashMap<crtc::Handle, GliumDrawer<DrmBackend<SessionFdDrmDevice>>> {
|
||||||
// Get a set of all modesetting resource handles (excluding planes):
|
// Get a set of all modesetting resource handles (excluding planes):
|
||||||
let res_handles = device.resource_handles().unwrap();
|
let res_handles = device.resource_handles().unwrap();
|
||||||
|
|
||||||
|
@ -336,72 +354,78 @@ impl UdevHandlerImpl {
|
||||||
.inspect(|conn| info!(self.logger, "Connected: {:?}", conn.connector_type()))
|
.inspect(|conn| info!(self.logger, "Connected: {:?}", conn.connector_type()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut used_crtcs: HashSet<crtc::Handle> = HashSet::new();
|
let mut backends = HashMap::new();
|
||||||
|
|
||||||
let mut state = state.into();
|
|
||||||
|
|
||||||
// very naive way of finding good crtc/encoder/connector combinations. This problem is np-complete
|
// very naive way of finding good crtc/encoder/connector combinations. This problem is np-complete
|
||||||
for connector_info in connector_infos {
|
for connector_info in connector_infos {
|
||||||
let encoder_infos = connector_info.encoders().iter().flat_map(|encoder_handle| EncoderInfo::load_from_device(device, *encoder_handle)).collect::<Vec<EncoderInfo>>();
|
let encoder_infos = connector_info.encoders().iter().flat_map(|encoder_handle| EncoderInfo::load_from_device(device, *encoder_handle)).collect::<Vec<EncoderInfo>>();
|
||||||
for encoder_info in encoder_infos {
|
for encoder_info in encoder_infos {
|
||||||
for crtc in res_handles.filter_crtcs(encoder_info.possible_crtcs()) {
|
for crtc in res_handles.filter_crtcs(encoder_info.possible_crtcs()) {
|
||||||
if !used_crtcs.contains(&crtc) {
|
if !backends.contains_key(&crtc) {
|
||||||
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.)
|
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.)
|
||||||
// create a backend
|
// create a backend
|
||||||
let renderer_token = device.create_backend(&mut state, crtc, mode, vec![connector_info.handle()]).unwrap();
|
let renderer = GliumDrawer::from(device.create_backend(crtc, mode, vec![connector_info.handle()]).unwrap());
|
||||||
|
|
||||||
// create cursor
|
// create cursor
|
||||||
{
|
|
||||||
let renderer = state.get_mut(renderer_token);
|
|
||||||
renderer.set_cursor_representation(&self.pointer_image, (2, 2)).unwrap();
|
renderer.set_cursor_representation(&self.pointer_image, (2, 2)).unwrap();
|
||||||
}
|
|
||||||
|
|
||||||
// render first frame
|
// render first frame
|
||||||
{
|
{
|
||||||
let renderer = state.get_mut(renderer_token);
|
|
||||||
let mut frame = renderer.draw();
|
let mut frame = renderer.draw();
|
||||||
frame.clear_color(0.8, 0.8, 0.9, 1.0);
|
frame.clear_color(0.8, 0.8, 0.9, 1.0);
|
||||||
frame.finish().unwrap();
|
frame.finish().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
used_crtcs.insert(crtc);
|
backends.insert(crtc, renderer);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
backends
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UdevHandler<GliumDrawer<DrmBackend>, DrmHandlerImpl> for UdevHandlerImpl {
|
impl UdevHandler<DrmHandlerImpl> for UdevHandlerImpl {
|
||||||
fn device_added<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &mut DrmDevice<GliumDrawer<DrmBackend>>) -> Option<DrmHandlerImpl>
|
fn device_added<'a, S: Into<StateProxy<'a>>>(&mut self, _state: S, device: &mut DrmDevice<SessionFdDrmDevice>) -> Option<DrmHandlerImpl>
|
||||||
{
|
{
|
||||||
self.scan_connectors(state, device);
|
// init hardware acceleration on the primary gpu.
|
||||||
|
if device.dev_path().and_then(|path| path.canonicalize().ok()) == self.primary_gpu {
|
||||||
|
*self.active_egl_context.borrow_mut() = device.bind_wl_display(&*self.display).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
let backends = Rc::new(RefCell::new(self.scan_connectors(device)));
|
||||||
|
self.backends.insert(device.device_id(), backends.clone());
|
||||||
|
|
||||||
Some(DrmHandlerImpl {
|
Some(DrmHandlerImpl {
|
||||||
shell_state_token: self.shell_state_token.clone(),
|
shell_state_token: self.shell_state_token.clone(),
|
||||||
compositor_token: self.compositor_token.clone(),
|
compositor_token: self.compositor_token.clone(),
|
||||||
|
backends,
|
||||||
window_map: self.window_map.clone(),
|
window_map: self.window_map.clone(),
|
||||||
pointer_location: self.pointer_location.clone(),
|
pointer_location: self.pointer_location.clone(),
|
||||||
logger: self.logger.clone(),
|
logger: self.logger.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn device_changed<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &StateToken<DrmDevice<GliumDrawer<DrmBackend>>>) {
|
fn device_changed<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &StateToken<DrmDevice<SessionFdDrmDevice>>) {
|
||||||
//quick and dirt, just re-init the device
|
//quick and dirt, just re-init all backends
|
||||||
let mut state = state.into();
|
let mut state = state.into();
|
||||||
self.device_removed(&mut state, device);
|
let backends = self.backends.get(&state.get(device).device_id()).unwrap();
|
||||||
state.with_value(device, |state, device| self.scan_connectors(state, device));
|
*backends.borrow_mut() = self.scan_connectors(state.get_mut(device));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn device_removed<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &StateToken<DrmDevice<GliumDrawer<DrmBackend>>>) {
|
fn device_removed<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &StateToken<DrmDevice<SessionFdDrmDevice>>) {
|
||||||
state.into().with_value(device, |state, device| {
|
let state = state.into();
|
||||||
let crtcs = device.current_backends().into_iter().map(|backend| state.get(backend).crtc()).collect::<Vec<crtc::Handle>>();
|
let device = state.get(device);
|
||||||
let mut state: StateProxy = state.into();
|
|
||||||
for crtc in crtcs {
|
// drop the backends on this side
|
||||||
device.destroy_backend(&mut state, &crtc);
|
self.backends.remove(&device.device_id());
|
||||||
|
|
||||||
|
// don't use hardware acceleration anymore, if this was the primary gpu
|
||||||
|
if device.dev_path().and_then(|path| path.canonicalize().ok()) == self.primary_gpu {
|
||||||
|
*self.active_egl_context.borrow_mut() = None;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn error<'a, S: Into<StateProxy<'a>>>(&mut self, _state: S, error: IoError) {
|
fn error<'a, S: Into<StateProxy<'a>>>(&mut self, _state: S, error: IoError) {
|
||||||
|
@ -410,21 +434,20 @@ impl UdevHandler<GliumDrawer<DrmBackend>, DrmHandlerImpl> for UdevHandlerImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DrmHandlerImpl {
|
pub struct DrmHandlerImpl {
|
||||||
shell_state_token: StateToken<ShellState<SurfaceData, Roles, (), ()>>,
|
shell_state_token: StateToken<ShellState<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>, ()>>,
|
||||||
compositor_token: CompositorToken<SurfaceData, Roles, ()>,
|
compositor_token: CompositorToken<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>>,
|
||||||
|
backends: Rc<RefCell<HashMap<crtc::Handle, GliumDrawer<DrmBackend<SessionFdDrmDevice>>>>>,
|
||||||
window_map: Rc<RefCell<MyWindowMap>>,
|
window_map: Rc<RefCell<MyWindowMap>>,
|
||||||
pointer_location: Rc<RefCell<(f64, f64)>>,
|
pointer_location: Rc<RefCell<(f64, f64)>>,
|
||||||
logger: ::slog::Logger,
|
logger: ::slog::Logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DrmHandler<GliumDrawer<DrmBackend>> for DrmHandlerImpl {
|
impl DrmHandler<SessionFdDrmDevice> for DrmHandlerImpl {
|
||||||
fn ready<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, _device: &mut DrmDevice<GliumDrawer<DrmBackend>>,
|
fn ready(&mut self, _device: &mut DrmDevice<SessionFdDrmDevice>, crtc: crtc::Handle, _frame: u32, _duration: Duration) {
|
||||||
backend: &StateToken<GliumDrawer<DrmBackend>>, _crtc: crtc::Handle, _frame: u32, _duration: Duration) {
|
if let Some(drawer) = self.backends.borrow().get(&crtc) {
|
||||||
let state = state.into();
|
|
||||||
let drawer = state.get(backend);
|
|
||||||
{
|
{
|
||||||
let (x, y) = *self.pointer_location.borrow();
|
let (x, y) = *self.pointer_location.borrow();
|
||||||
let _ = (**drawer).set_cursor_position(x.trunc().abs() as u32, y.trunc().abs() as u32);
|
let _ = drawer.set_cursor_position(x.trunc().abs() as u32, y.trunc().abs() as u32);
|
||||||
}
|
}
|
||||||
let mut frame = drawer.draw();
|
let mut frame = drawer.draw();
|
||||||
frame.clear_color(0.8, 0.8, 0.9, 1.0);
|
frame.clear_color(0.8, 0.8, 0.9, 1.0);
|
||||||
|
@ -441,18 +464,52 @@ impl DrmHandler<GliumDrawer<DrmBackend>> for DrmHandlerImpl {
|
||||||
wl_surface,
|
wl_surface,
|
||||||
initial_place,
|
initial_place,
|
||||||
|_surface, attributes, role, &(mut x, mut y)| {
|
|_surface, attributes, role, &(mut x, mut y)| {
|
||||||
if let Some((ref contents, (w, h))) = attributes.user_data.buffer {
|
|
||||||
// there is actually something to draw !
|
// there is actually something to draw !
|
||||||
|
if attributes.user_data.texture.is_none() {
|
||||||
|
let mut remove = false;
|
||||||
|
match attributes.user_data.buffer {
|
||||||
|
Some(Buffer::Egl { ref images }) => {
|
||||||
|
match images.format {
|
||||||
|
Format::RGB | Format::RGBA => {
|
||||||
|
attributes.user_data.texture = drawer.texture_from_egl(&images);
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
// we don't handle the more complex formats here.
|
||||||
|
attributes.user_data.texture = None;
|
||||||
|
remove = true;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
Some(Buffer::Shm { ref data, ref size }) => {
|
||||||
|
attributes.user_data.texture = Some(drawer.texture_from_mem(data, *size));
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
if remove {
|
||||||
|
attributes.user_data.buffer = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ref texture) = attributes.user_data.texture {
|
||||||
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
|
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
|
||||||
x += subdata.x;
|
x += subdata.x;
|
||||||
y += subdata.y;
|
y += subdata.y;
|
||||||
}
|
}
|
||||||
drawer.render(
|
info!(self.logger, "Render window");
|
||||||
|
drawer.render_texture(
|
||||||
&mut frame,
|
&mut frame,
|
||||||
contents,
|
texture,
|
||||||
(w, h),
|
match *attributes.user_data.buffer.as_ref().unwrap() {
|
||||||
|
Buffer::Egl { ref images } => images.y_inverted,
|
||||||
|
Buffer::Shm { .. } => false,
|
||||||
|
},
|
||||||
|
match *attributes.user_data.buffer.as_ref().unwrap() {
|
||||||
|
Buffer::Egl { ref images } => (images.width, images.height),
|
||||||
|
Buffer::Shm { ref size, .. } => *size,
|
||||||
|
},
|
||||||
(x, y),
|
(x, y),
|
||||||
screen_dimensions,
|
screen_dimensions,
|
||||||
|
Blend::alpha_blending(),
|
||||||
);
|
);
|
||||||
TraversalAction::DoChildren((x, y))
|
TraversalAction::DoChildren((x, y))
|
||||||
} else {
|
} else {
|
||||||
|
@ -469,8 +526,9 @@ impl DrmHandler<GliumDrawer<DrmBackend>> for DrmHandlerImpl {
|
||||||
error!(self.logger, "Error during rendering: {:?}", err);
|
error!(self.logger, "Error during rendering: {:?}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn error<'a, S: Into<StateProxy<'a>>>(&mut self, _state: S, _device: &mut DrmDevice<GliumDrawer<DrmBackend>>,
|
fn error(&mut self, _device: &mut DrmDevice<SessionFdDrmDevice>,
|
||||||
error: DrmError) {
|
error: DrmError) {
|
||||||
error!(self.logger, "{:?}", error);
|
error!(self.logger, "{:?}", error);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ use glium::Surface;
|
||||||
use helpers::{init_shell, GliumDrawer, MyWindowMap, Buffer};
|
use helpers::{init_shell, GliumDrawer, MyWindowMap, Buffer};
|
||||||
use slog::{Drain, Logger};
|
use slog::{Drain, Logger};
|
||||||
use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
||||||
|
use smithay::backend::graphics::egl::wayland::{EGLWaylandExtensions, Format};
|
||||||
use smithay::backend::input::{self, Event, InputBackend, InputHandler, KeyboardKeyEvent, PointerAxisEvent,
|
use smithay::backend::input::{self, Event, InputBackend, InputHandler, KeyboardKeyEvent, PointerAxisEvent,
|
||||||
PointerButtonEvent, PointerMotionAbsoluteEvent};
|
PointerButtonEvent, PointerMotionAbsoluteEvent};
|
||||||
use smithay::backend::winit;
|
use smithay::backend::winit;
|
||||||
|
@ -27,23 +28,23 @@ use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use wayland_server::protocol::{wl_output, wl_pointer};
|
use wayland_server::protocol::{wl_output, wl_pointer};
|
||||||
|
|
||||||
struct WinitInputHandler<G: EGLGraphicsBackend + 'static> {
|
struct WinitInputHandler {
|
||||||
log: Logger,
|
log: Logger,
|
||||||
pointer: PointerHandle,
|
pointer: PointerHandle,
|
||||||
keyboard: KeyboardHandle,
|
keyboard: KeyboardHandle,
|
||||||
window_map: Rc<RefCell<MyWindowMap<G>>>,
|
window_map: Rc<RefCell<MyWindowMap>>,
|
||||||
pointer_location: (f64, f64),
|
pointer_location: (f64, f64),
|
||||||
serial: u32,
|
serial: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<G: EGLGraphicsBackend + 'static> WinitInputHandler<G> {
|
impl WinitInputHandler {
|
||||||
fn next_serial(&mut self) -> u32 {
|
fn next_serial(&mut self) -> u32 {
|
||||||
self.serial += 1;
|
self.serial += 1;
|
||||||
self.serial
|
self.serial
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<G: EGLGraphicsBackend + 'static> InputHandler<winit::WinitInputBackend> for WinitInputHandler<G> {
|
impl InputHandler<winit::WinitInputBackend> for WinitInputHandler {
|
||||||
fn on_seat_created(&mut self, _: &input::Seat) {
|
fn on_seat_created(&mut self, _: &input::Seat) {
|
||||||
/* never happens with winit */
|
/* never happens with winit */
|
||||||
}
|
}
|
||||||
|
@ -136,12 +137,17 @@ fn main() {
|
||||||
|
|
||||||
let (mut display, mut event_loop) = wayland_server::create_display();
|
let (mut display, mut event_loop) = wayland_server::create_display();
|
||||||
|
|
||||||
if let Ok(_) = renderer.bind_wl_display(&display) {
|
let egl_display = Rc::new(RefCell::new(
|
||||||
|
if let Ok(egl_display) = renderer.bind_wl_display(&display) {
|
||||||
info!(log, "EGL hardware-acceleration enabled");
|
info!(log, "EGL hardware-acceleration enabled");
|
||||||
|
Some(egl_display)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
));
|
||||||
|
|
||||||
let (w, h) = renderer.get_framebuffer_dimensions();
|
let (w, h) = renderer.get_framebuffer_dimensions();
|
||||||
let drawer = Rc::new(GliumDrawer::from(renderer));
|
let drawer = GliumDrawer::from(renderer);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize the globals
|
* Initialize the globals
|
||||||
|
@ -149,7 +155,7 @@ fn main() {
|
||||||
|
|
||||||
init_shm_global(&mut event_loop, vec![], log.clone());
|
init_shm_global(&mut event_loop, vec![], log.clone());
|
||||||
|
|
||||||
let (compositor_token, _shell_state_token, window_map) = init_shell(&mut event_loop, log.clone(), drawer.clone());
|
let (compositor_token, _shell_state_token, window_map) = init_shell(&mut event_loop, log.clone(), egl_display);
|
||||||
|
|
||||||
let (seat_token, _) = Seat::new(&mut event_loop, "winit".into(), log.clone());
|
let (seat_token, _) = Seat::new(&mut event_loop, "winit".into(), log.clone());
|
||||||
|
|
||||||
|
@ -228,6 +234,31 @@ fn main() {
|
||||||
initial_place,
|
initial_place,
|
||||||
|_surface, attributes, role, &(mut x, mut y)| {
|
|_surface, attributes, role, &(mut x, mut y)| {
|
||||||
// there is actually something to draw !
|
// there is actually something to draw !
|
||||||
|
if attributes.user_data.texture.is_none() {
|
||||||
|
let mut remove = false;
|
||||||
|
match attributes.user_data.buffer {
|
||||||
|
Some(Buffer::Egl { ref images }) => {
|
||||||
|
match images.format {
|
||||||
|
Format::RGB | Format::RGBA => {
|
||||||
|
attributes.user_data.texture = drawer.texture_from_egl(&images);
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
// we don't handle the more complex formats here.
|
||||||
|
attributes.user_data.texture = None;
|
||||||
|
remove = true;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
Some(Buffer::Shm { ref data, ref size }) => {
|
||||||
|
attributes.user_data.texture = Some(drawer.texture_from_mem(data, *size));
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
if remove {
|
||||||
|
attributes.user_data.buffer = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(ref texture) = attributes.user_data.texture {
|
if let Some(ref texture) = attributes.user_data.texture {
|
||||||
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
|
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
|
||||||
x += subdata.x;
|
x += subdata.x;
|
||||||
|
@ -246,6 +277,17 @@ fn main() {
|
||||||
},
|
},
|
||||||
(x, y),
|
(x, y),
|
||||||
screen_dimensions,
|
screen_dimensions,
|
||||||
|
glium::Blend {
|
||||||
|
color: glium::BlendingFunction::Addition {
|
||||||
|
source: glium::LinearBlendingFactor::One,
|
||||||
|
destination: glium::LinearBlendingFactor::OneMinusSourceAlpha,
|
||||||
|
},
|
||||||
|
alpha: glium::BlendingFunction::Addition {
|
||||||
|
source: glium::LinearBlendingFactor::One,
|
||||||
|
destination: glium::LinearBlendingFactor::OneMinusSourceAlpha,
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
);
|
);
|
||||||
TraversalAction::DoChildren((x, y))
|
TraversalAction::DoChildren((x, y))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,27 +1,35 @@
|
||||||
use super::error::*;
|
use super::error::*;
|
||||||
use super::DevPath;
|
use super::DevPath;
|
||||||
use backend::graphics::GraphicsBackend;
|
use backend::graphics::GraphicsBackend;
|
||||||
use backend::graphics::egl::{EGLGraphicsBackend, EGLContext, EGLSurface, EGLImage, PixelFormat, SwapBuffersError, EglExtensionNotSupportedError};
|
use backend::graphics::egl::{EGLGraphicsBackend, EGLContext, EGLSurface, PixelFormat, SwapBuffersError, EglExtensionNotSupportedError};
|
||||||
|
use backend::graphics::egl::error::Result as EGLResult;
|
||||||
use backend::graphics::egl::native::{Gbm, GbmSurfaceArguments};
|
use backend::graphics::egl::native::{Gbm, GbmSurfaceArguments};
|
||||||
|
use backend::graphics::egl::wayland::{EGLWaylandExtensions, EGLDisplay, BufferAccessError, EGLImages};
|
||||||
use drm::control::{Device, ResourceInfo};
|
use drm::control::{Device, ResourceInfo};
|
||||||
use drm::control::{connector, crtc, encoder, framebuffer, Mode};
|
use drm::control::{connector, crtc, encoder, framebuffer, Mode};
|
||||||
use gbm::{Device as GbmDevice, BufferObject, BufferObjectFlags, Format as GbmFormat, Surface as GbmSurface, SurfaceBufferHandle};
|
use gbm::{Device as GbmDevice, BufferObject, BufferObjectFlags, Format as GbmFormat, Surface as GbmSurface, SurfaceBufferHandle};
|
||||||
use image::{ImageBuffer, Rgba};
|
use image::{ImageBuffer, Rgba};
|
||||||
use nix::libc::{c_void, c_uint};
|
use nix::libc::c_void;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::rc::Rc;
|
use std::rc::{Rc, Weak};
|
||||||
use wayland_server::Display;
|
use wayland_server::Display;
|
||||||
|
use wayland_server::protocol::wl_buffer::WlBuffer;
|
||||||
|
|
||||||
|
pub struct DrmBackend<A: Device + 'static> {
|
||||||
|
backend: Rc<DrmBackendInternal<A>>,
|
||||||
|
surface: EGLSurface<GbmSurface<framebuffer::Info>>,
|
||||||
|
mode: Mode,
|
||||||
|
connectors: Vec<connector::Handle>,
|
||||||
|
}
|
||||||
|
|
||||||
/// Backend based on a `DrmDevice` and a given crtc
|
/// Backend based on a `DrmDevice` and a given crtc
|
||||||
pub struct DrmBackend<A: Device + 'static> {
|
pub(crate) struct DrmBackendInternal<A: Device + 'static> {
|
||||||
context: Rc<EGLContext<Gbm<framebuffer::Info>, GbmDevice<A>>>,
|
context: Rc<EGLContext<Gbm<framebuffer::Info>, GbmDevice<A>>>,
|
||||||
surface: EGLSurface<GbmSurface<framebuffer::Info>>,
|
|
||||||
cursor: Cell<BufferObject<()>>,
|
cursor: Cell<BufferObject<()>>,
|
||||||
|
current_frame_buffer: Cell<framebuffer::Info>,
|
||||||
front_buffer: Cell<SurfaceBufferHandle<framebuffer::Info>>,
|
front_buffer: Cell<SurfaceBufferHandle<framebuffer::Info>>,
|
||||||
next_buffer: Cell<Option<SurfaceBufferHandle<framebuffer::Info>>>,
|
next_buffer: Cell<Option<SurfaceBufferHandle<framebuffer::Info>>>,
|
||||||
crtc: crtc::Handle,
|
crtc: crtc::Handle,
|
||||||
mode: Mode,
|
|
||||||
connectors: Vec<connector::Handle>,
|
|
||||||
logger: ::slog::Logger,
|
logger: ::slog::Logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,10 +39,9 @@ impl<A: Device + 'static> DrmBackend<A> {
|
||||||
crtc: crtc::Handle,
|
crtc: crtc::Handle,
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
connectors: Vec<connector::Handle>,
|
connectors: Vec<connector::Handle>,
|
||||||
logger: ::slog::Logger,
|
log: ::slog::Logger,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
// logger already initialized by the DrmDevice
|
// logger already initialized by the DrmDevice
|
||||||
let log = ::slog_or_stdlog(logger);
|
|
||||||
info!(log, "Initializing DrmBackend");
|
info!(log, "Initializing DrmBackend");
|
||||||
|
|
||||||
let (w, h) = mode.size();
|
let (w, h) = mode.size();
|
||||||
|
@ -89,26 +96,24 @@ impl<A: Device + 'static> DrmBackend<A> {
|
||||||
).chain_err(|| ErrorKind::GbmInitFailed)?);
|
).chain_err(|| ErrorKind::GbmInitFailed)?);
|
||||||
|
|
||||||
Ok(DrmBackend {
|
Ok(DrmBackend {
|
||||||
|
backend: Rc::new(DrmBackendInternal {
|
||||||
context,
|
context,
|
||||||
surface,
|
|
||||||
cursor,
|
cursor,
|
||||||
|
current_frame_buffer: Cell::new(fb),
|
||||||
front_buffer: Cell::new(front_bo),
|
front_buffer: Cell::new(front_bo),
|
||||||
next_buffer: Cell::new(None),
|
next_buffer: Cell::new(None),
|
||||||
crtc,
|
crtc,
|
||||||
|
logger: log,
|
||||||
|
}),
|
||||||
|
surface,
|
||||||
mode,
|
mode,
|
||||||
connectors,
|
connectors,
|
||||||
logger: log,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn unlock_buffer(&self) {
|
pub(crate) fn weak(&self) -> Weak<DrmBackendInternal<A>>
|
||||||
// after the page swap is finished we need to release the rendered buffer.
|
{
|
||||||
// this is called from the PageFlipHandler
|
Rc::downgrade(&self.backend)
|
||||||
if let Some(next_buffer) = self.next_buffer.replace(None) {
|
|
||||||
trace!(self.logger, "Releasing old front buffer");
|
|
||||||
self.front_buffer.set(next_buffer);
|
|
||||||
// drop and release the old buffer
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a connector to backend
|
/// Add a connector to backend
|
||||||
|
@ -117,8 +122,8 @@ impl<A: Device + 'static> DrmBackend<A> {
|
||||||
///
|
///
|
||||||
/// Errors if the new connector does not support the currently set `Mode`
|
/// Errors if the new connector does not support the currently set `Mode`
|
||||||
pub fn add_connector(&mut self, connector: connector::Handle) -> Result<()> {
|
pub fn add_connector(&mut self, connector: connector::Handle) -> Result<()> {
|
||||||
let info = connector::Info::load_from_device(&*self.context, connector)
|
let info = connector::Info::load_from_device(&*self.backend.context, connector)
|
||||||
.chain_err(|| ErrorKind::DrmDev(format!("Error loading connector info on {:?}", self.context.dev_path())))?;
|
.chain_err(|| ErrorKind::DrmDev(format!("Error loading connector info on {:?}", self.backend.context.dev_path())))?;
|
||||||
|
|
||||||
// check if the connector can handle the current mode
|
// check if the connector can handle the current mode
|
||||||
if info.modes().contains(&self.mode) {
|
if info.modes().contains(&self.mode) {
|
||||||
|
@ -126,28 +131,28 @@ impl<A: Device + 'static> DrmBackend<A> {
|
||||||
let encoders = info.encoders()
|
let encoders = info.encoders()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|encoder| {
|
.map(|encoder| {
|
||||||
encoder::Info::load_from_device(&*self.context, *encoder)
|
encoder::Info::load_from_device(&*self.backend.context, *encoder)
|
||||||
.chain_err(|| ErrorKind::DrmDev(format!("Error loading encoder info on {:?}", self.context.dev_path())))
|
.chain_err(|| ErrorKind::DrmDev(format!("Error loading encoder info on {:?}", self.backend.context.dev_path())))
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<encoder::Info>>>()?;
|
.collect::<Result<Vec<encoder::Info>>>()?;
|
||||||
|
|
||||||
// and if any encoder supports the selected crtc
|
// and if any encoder supports the selected crtc
|
||||||
let resource_handles = self.context
|
let resource_handles = self.backend.context
|
||||||
.resource_handles()
|
.resource_handles()
|
||||||
.chain_err(|| ErrorKind::DrmDev(format!("Error loading resources on {:?}", self.context.dev_path())))?;
|
.chain_err(|| ErrorKind::DrmDev(format!("Error loading resources on {:?}", self.backend.context.dev_path())))?;
|
||||||
if !encoders
|
if !encoders
|
||||||
.iter()
|
.iter()
|
||||||
.map(|encoder| encoder.possible_crtcs())
|
.map(|encoder| encoder.possible_crtcs())
|
||||||
.all(|crtc_list| {
|
.all(|crtc_list| {
|
||||||
resource_handles
|
resource_handles
|
||||||
.filter_crtcs(crtc_list)
|
.filter_crtcs(crtc_list)
|
||||||
.contains(&self.crtc)
|
.contains(&self.backend.crtc)
|
||||||
}) {
|
}) {
|
||||||
bail!(ErrorKind::NoSuitableEncoder(info, self.crtc));
|
bail!(ErrorKind::NoSuitableEncoder(info, self.backend.crtc));
|
||||||
}
|
}
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
self.logger,
|
self.backend.logger,
|
||||||
"Adding new connector: {:?}",
|
"Adding new connector: {:?}",
|
||||||
info.connector_type()
|
info.connector_type()
|
||||||
);
|
);
|
||||||
|
@ -165,14 +170,14 @@ impl<A: Device + 'static> DrmBackend<A> {
|
||||||
|
|
||||||
/// Removes a currently set connector
|
/// Removes a currently set connector
|
||||||
pub fn remove_connector(&mut self, connector: connector::Handle) {
|
pub fn remove_connector(&mut self, connector: connector::Handle) {
|
||||||
if let Ok(info) = connector::Info::load_from_device(&*self.context, connector) {
|
if let Ok(info) = connector::Info::load_from_device(&*self.backend.context, connector) {
|
||||||
info!(
|
info!(
|
||||||
self.logger,
|
self.backend.logger,
|
||||||
"Removing connector: {:?}",
|
"Removing connector: {:?}",
|
||||||
info.connector_type()
|
info.connector_type()
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
info!(self.logger, "Removing unknown connector");
|
info!(self.backend.logger, "Removing unknown connector");
|
||||||
}
|
}
|
||||||
|
|
||||||
self.connectors.retain(|x| *x != connector);
|
self.connectors.retain(|x| *x != connector);
|
||||||
|
@ -188,8 +193,8 @@ impl<A: Device + 'static> DrmBackend<A> {
|
||||||
pub fn use_mode(&mut self, mode: Mode) -> Result<()> {
|
pub fn use_mode(&mut self, mode: Mode) -> Result<()> {
|
||||||
// check the connectors
|
// check the connectors
|
||||||
for connector in &self.connectors {
|
for connector in &self.connectors {
|
||||||
if !connector::Info::load_from_device(&*self.context, *connector)
|
if !connector::Info::load_from_device(&*self.backend.context, *connector)
|
||||||
.chain_err(|| ErrorKind::DrmDev(format!("Error loading connector info on {:?}", self.context.dev_path())))?
|
.chain_err(|| ErrorKind::DrmDev(format!("Error loading connector info on {:?}", self.backend.context.dev_path())))?
|
||||||
.modes()
|
.modes()
|
||||||
.contains(&mode)
|
.contains(&mode)
|
||||||
{
|
{
|
||||||
|
@ -197,13 +202,13 @@ impl<A: Device + 'static> DrmBackend<A> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
info!(self.logger, "Setting new mode: {:?}", mode.name());
|
info!(self.backend.logger, "Setting new mode: {:?}", mode.name());
|
||||||
let (w, h) = mode.size();
|
let (w, h) = mode.size();
|
||||||
|
|
||||||
// Recreate the surface and the related resources to match the new
|
// Recreate the surface and the related resources to match the new
|
||||||
// resolution.
|
// resolution.
|
||||||
debug!(self.logger, "Reinitializing surface for new mode: {}:{}", w, h);
|
debug!(self.backend.logger, "Reinitializing surface for new mode: {}:{}", w, h);
|
||||||
let surface = self.context.create_surface(
|
let surface = self.backend.context.create_surface(
|
||||||
GbmSurfaceArguments {
|
GbmSurfaceArguments {
|
||||||
size: (w as u32, h as u32),
|
size: (w as u32, h as u32),
|
||||||
format: GbmFormat::XRGB8888,
|
format: GbmFormat::XRGB8888,
|
||||||
|
@ -224,10 +229,10 @@ impl<A: Device + 'static> DrmBackend<A> {
|
||||||
|
|
||||||
// Clean up next_buffer
|
// Clean up next_buffer
|
||||||
{
|
{
|
||||||
if let Some(mut old_bo) = self.next_buffer.take() {
|
if let Some(mut old_bo) = self.backend.next_buffer.take() {
|
||||||
if let Ok(Some(fb)) = old_bo.take_userdata() {
|
if let Ok(Some(fb)) = old_bo.take_userdata() {
|
||||||
if let Err(err) = framebuffer::destroy(&*self.context, fb.handle()) {
|
if let Err(err) = framebuffer::destroy(&*self.backend.context, fb.handle()) {
|
||||||
warn!(self.logger, "Error releasing old back_buffer framebuffer: {:?}", err);
|
warn!(self.backend.logger, "Error releasing old back_buffer framebuffer: {:?}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -235,41 +240,37 @@ impl<A: Device + 'static> DrmBackend<A> {
|
||||||
|
|
||||||
// Cleanup front_buffer and init the first screen on the new front_buffer
|
// Cleanup front_buffer and init the first screen on the new front_buffer
|
||||||
// (must be done before calling page_flip for the first time)
|
// (must be done before calling page_flip for the first time)
|
||||||
let fb = {
|
let mut old_front_bo = self.backend.front_buffer.replace({
|
||||||
let mut old_front_bo = self.front_buffer.replace(
|
let mut front_bo = surface
|
||||||
surface
|
|
||||||
.lock_front_buffer()
|
.lock_front_buffer()
|
||||||
.chain_err(|| ErrorKind::FailedToSwap)?
|
.chain_err(|| ErrorKind::FailedToSwap)?;
|
||||||
);
|
|
||||||
if let Ok(Some(fb)) = old_front_bo.take_userdata() {
|
|
||||||
if let Err(err) = framebuffer::destroy(&*self.context, fb.handle()) {
|
|
||||||
warn!(self.logger, "Error releasing old front_buffer framebuffer: {:?}", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let front_bo = self.front_buffer.get_mut();
|
debug!(self.backend.logger, "FrontBuffer color format: {:?}", front_bo.format());
|
||||||
debug!(self.logger, "FrontBuffer color format: {:?}", front_bo.format());
|
|
||||||
|
|
||||||
// we also need a new framebuffer for the front buffer
|
// we also need a new framebuffer for the front buffer
|
||||||
let dev_path = self.context.dev_path();
|
let dev_path = self.backend.context.dev_path();
|
||||||
let fb = framebuffer::create(&*self.context, &**front_bo)
|
let fb = framebuffer::create(&*self.backend.context, &*front_bo)
|
||||||
.chain_err(|| ErrorKind::DrmDev(format!("Error creating framebuffer on {:?}", dev_path)))?;
|
.chain_err(|| ErrorKind::DrmDev(format!("Error creating framebuffer on {:?}", dev_path)))?;
|
||||||
|
|
||||||
front_bo.set_userdata(fb).unwrap();
|
front_bo.set_userdata(fb).unwrap();
|
||||||
|
|
||||||
fb
|
debug!(self.backend.logger, "Setting screen");
|
||||||
};
|
|
||||||
|
|
||||||
debug!(self.logger, "Setting screen");
|
|
||||||
crtc::set(
|
crtc::set(
|
||||||
&*self.context,
|
&*self.backend.context,
|
||||||
self.crtc,
|
self.backend.crtc,
|
||||||
fb.handle(),
|
fb.handle(),
|
||||||
&self.connectors,
|
&self.connectors,
|
||||||
(0, 0),
|
(0, 0),
|
||||||
Some(mode),
|
Some(mode),
|
||||||
).chain_err(|| ErrorKind::DrmDev(format!("Error setting crtc {:?} on {:?}", self.crtc, self.context.dev_path())))?;
|
).chain_err(|| ErrorKind::DrmDev(format!("Error setting crtc {:?} on {:?}", self.backend.crtc, self.backend.context.dev_path())))?;
|
||||||
|
|
||||||
|
front_bo
|
||||||
|
});
|
||||||
|
if let Ok(Some(fb)) = old_front_bo.take_userdata() {
|
||||||
|
if let Err(err) = framebuffer::destroy(&*self.backend.context, fb.handle()) {
|
||||||
|
warn!(self.backend.logger, "Error releasing old front_buffer framebuffer: {:?}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Drop the old surface after cleanup
|
// Drop the old surface after cleanup
|
||||||
self.surface = surface;
|
self.surface = surface;
|
||||||
|
@ -279,7 +280,37 @@ impl<A: Device + 'static> DrmBackend<A> {
|
||||||
|
|
||||||
/// Returns the crtc id used by this backend
|
/// Returns the crtc id used by this backend
|
||||||
pub fn crtc(&self) -> crtc::Handle {
|
pub fn crtc(&self) -> crtc::Handle {
|
||||||
self.crtc
|
self.backend.crtc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A: Device + 'static> DrmBackendInternal<A> {
|
||||||
|
pub(crate) fn unlock_buffer(&self) {
|
||||||
|
// after the page swap is finished we need to release the rendered buffer.
|
||||||
|
// this is called from the PageFlipHandler
|
||||||
|
if let Some(next_buffer) = self.next_buffer.replace(None) {
|
||||||
|
trace!(self.logger, "Releasing old front buffer");
|
||||||
|
self.front_buffer.set(next_buffer);
|
||||||
|
// drop and release the old buffer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn page_flip(&self, fb: Option<&framebuffer::Info>) -> ::std::result::Result<(), SwapBuffersError> {
|
||||||
|
trace!(self.logger, "Queueing Page flip");
|
||||||
|
|
||||||
|
let fb = *fb.unwrap_or(&self.current_frame_buffer.get());
|
||||||
|
|
||||||
|
// and flip
|
||||||
|
crtc::page_flip(
|
||||||
|
&*self.context,
|
||||||
|
self.crtc,
|
||||||
|
fb.handle(),
|
||||||
|
&[crtc::PageFlipFlags::PageFlipEvent],
|
||||||
|
).map_err(|_| SwapBuffersError::ContextLost)?;
|
||||||
|
|
||||||
|
self.current_frame_buffer.set(fb);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,7 +319,7 @@ impl<A: Device + 'static> Drop for DrmBackend<A> {
|
||||||
// Drop framebuffers attached to the userdata of the gbm surface buffers.
|
// Drop framebuffers attached to the userdata of the gbm surface buffers.
|
||||||
// (They don't implement drop, as they need the device)
|
// (They don't implement drop, as they need the device)
|
||||||
if let Ok(Some(fb)) = {
|
if let Ok(Some(fb)) = {
|
||||||
if let Some(mut next) = self.next_buffer.take() {
|
if let Some(mut next) = self.backend.next_buffer.take() {
|
||||||
next.take_userdata()
|
next.take_userdata()
|
||||||
} else if let Ok(mut next) = self.surface.lock_front_buffer() {
|
} else if let Ok(mut next) = self.surface.lock_front_buffer() {
|
||||||
next.take_userdata()
|
next.take_userdata()
|
||||||
|
@ -297,9 +328,13 @@ impl<A: Device + 'static> Drop for DrmBackend<A> {
|
||||||
}
|
}
|
||||||
} {
|
} {
|
||||||
// ignore failure at this point
|
// ignore failure at this point
|
||||||
let _ = framebuffer::destroy(&*self.context, fb.handle());
|
let _ = framebuffer::destroy(&*self.backend.context, fb.handle());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A: Device + 'static> Drop for DrmBackendInternal<A> {
|
||||||
|
fn drop(&mut self) {
|
||||||
if let Ok(Some(fb)) = self.front_buffer.get_mut().take_userdata() {
|
if let Ok(Some(fb)) = self.front_buffer.get_mut().take_userdata() {
|
||||||
// ignore failure at this point
|
// ignore failure at this point
|
||||||
let _ = framebuffer::destroy(&*self.context, fb.handle());
|
let _ = framebuffer::destroy(&*self.context, fb.handle());
|
||||||
|
@ -315,22 +350,23 @@ impl<A: Device + 'static> GraphicsBackend for DrmBackend<A> {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn set_cursor_position(&self, x: u32, y: u32) -> Result<()> {
|
fn set_cursor_position(&self, x: u32, y: u32) -> Result<()> {
|
||||||
trace!(self.logger, "Move the cursor to {},{}", x, y);
|
trace!(self.backend.logger, "Move the cursor to {},{}", x, y);
|
||||||
crtc::move_cursor(
|
crtc::move_cursor(
|
||||||
&*self.context,
|
&*self.backend.context,
|
||||||
self.crtc,
|
self.backend.crtc,
|
||||||
(x as i32, y as i32),
|
(x as i32, y as i32),
|
||||||
).chain_err(|| ErrorKind::DrmDev(format!("Error moving cursor on {:?}", self.context.dev_path())))
|
).chain_err(|| ErrorKind::DrmDev(format!("Error moving cursor on {:?}", self.backend.context.dev_path())))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_cursor_representation(
|
fn set_cursor_representation(
|
||||||
&self, buffer: &ImageBuffer<Rgba<u8>, Vec<u8>>, hotspot: (u32, u32)
|
&self, buffer: &ImageBuffer<Rgba<u8>, Vec<u8>>, hotspot: (u32, u32)
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let (w, h) = buffer.dimensions();
|
let (w, h) = buffer.dimensions();
|
||||||
debug!(self.logger, "Importing cursor");
|
debug!(self.backend.logger, "Importing cursor");
|
||||||
|
|
||||||
// import the cursor into a buffer we can render
|
// import the cursor into a buffer we can render
|
||||||
let mut cursor = self
|
let mut cursor = self
|
||||||
|
.backend
|
||||||
.context
|
.context
|
||||||
.create_buffer_object(
|
.create_buffer_object(
|
||||||
w,
|
w,
|
||||||
|
@ -344,37 +380,37 @@ impl<A: Device + 'static> GraphicsBackend for DrmBackend<A> {
|
||||||
.chain_err(|| ErrorKind::GbmInitFailed)?
|
.chain_err(|| ErrorKind::GbmInitFailed)?
|
||||||
.chain_err(|| ErrorKind::GbmInitFailed)?;
|
.chain_err(|| ErrorKind::GbmInitFailed)?;
|
||||||
|
|
||||||
trace!(self.logger, "Setting the new imported cursor");
|
trace!(self.backend.logger, "Setting the new imported cursor");
|
||||||
|
|
||||||
// and set it
|
// and set it
|
||||||
if crtc::set_cursor2(
|
if crtc::set_cursor2(
|
||||||
&*self.context,
|
&*self.backend.context,
|
||||||
self.crtc,
|
self.backend.crtc,
|
||||||
&cursor,
|
&cursor,
|
||||||
(hotspot.0 as i32, hotspot.1 as i32),
|
(hotspot.0 as i32, hotspot.1 as i32),
|
||||||
).is_err()
|
).is_err()
|
||||||
{
|
{
|
||||||
crtc::set_cursor(&*self.context, self.crtc, &cursor).chain_err(
|
crtc::set_cursor(&*self.backend.context, self.backend.crtc, &cursor).chain_err(
|
||||||
|| ErrorKind::DrmDev(format!("Failed to set cursor on {:?}", self.context.dev_path())),
|
|| ErrorKind::DrmDev(format!("Failed to set cursor on {:?}", self.backend.context.dev_path())),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// and store it
|
// and store it
|
||||||
self.cursor.set(cursor);
|
self.backend.cursor.set(cursor);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: Device + 'static> EGLGraphicsBackend for DrmBackend<A> {
|
impl<A: Device + 'static> EGLGraphicsBackend for DrmBackend<A> {
|
||||||
fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
|
fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
|
||||||
// We cannot call lock_front_buffer anymore without releasing the previous buffer, which will happen when the page flip is done
|
|
||||||
if {
|
if {
|
||||||
let nb = self.next_buffer.take();
|
let nb = self.backend.next_buffer.take();
|
||||||
let res = nb.is_some();
|
let res = nb.is_some();
|
||||||
self.next_buffer.set(nb);
|
self.backend.next_buffer.set(nb);
|
||||||
res
|
res
|
||||||
} {
|
} {
|
||||||
warn!(self.logger, "Tried to swap a DrmBackend with a queued flip");
|
// We cannot call lock_front_buffer anymore without releasing the previous buffer, which will happen when the page flip is done
|
||||||
|
warn!(self.backend.logger, "Tried to swap a DrmBackend with a queued flip");
|
||||||
return Err(SwapBuffersError::AlreadySwapped);
|
return Err(SwapBuffersError::AlreadySwapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -395,26 +431,18 @@ impl<A: Device + 'static> EGLGraphicsBackend for DrmBackend<A> {
|
||||||
let fb = if let Some(info) = maybe_fb {
|
let fb = if let Some(info) = maybe_fb {
|
||||||
info
|
info
|
||||||
} else {
|
} else {
|
||||||
let fb = framebuffer::create(&*self.context, &*next_bo)
|
let fb = framebuffer::create(&*self.backend.context, &*next_bo)
|
||||||
.map_err(|_| SwapBuffersError::ContextLost)?;
|
.map_err(|_| SwapBuffersError::ContextLost)?;
|
||||||
next_bo.set_userdata(fb).unwrap();
|
next_bo.set_userdata(fb).unwrap();
|
||||||
fb
|
fb
|
||||||
};
|
};
|
||||||
self.next_buffer.set(Some(next_bo));
|
self.backend.next_buffer.set(Some(next_bo));
|
||||||
|
|
||||||
trace!(self.logger, "Queueing Page flip");
|
self.backend.page_flip(Some(&fb))
|
||||||
|
|
||||||
// and flip
|
|
||||||
crtc::page_flip(
|
|
||||||
&*self.context,
|
|
||||||
self.crtc,
|
|
||||||
fb.handle(),
|
|
||||||
&[crtc::PageFlipFlags::PageFlipEvent],
|
|
||||||
).map_err(|_| SwapBuffersError::ContextLost)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void {
|
unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void {
|
||||||
self.context.get_proc_address(symbol)
|
self.backend.context.get_proc_address(symbol)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_framebuffer_dimensions(&self) -> (u32, u32) {
|
fn get_framebuffer_dimensions(&self) -> (u32, u32) {
|
||||||
|
@ -423,7 +451,7 @@ impl<A: Device + 'static> EGLGraphicsBackend for DrmBackend<A> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_current(&self) -> bool {
|
fn is_current(&self) -> bool {
|
||||||
self.context.is_current() && self.surface.is_current()
|
self.backend.context.is_current() && self.surface.is_current()
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
|
unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
|
||||||
|
@ -431,19 +459,16 @@ impl<A: Device + 'static> EGLGraphicsBackend for DrmBackend<A> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_pixel_format(&self) -> PixelFormat {
|
fn get_pixel_format(&self) -> PixelFormat {
|
||||||
self.context.get_pixel_format()
|
self.backend.context.get_pixel_format()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A: Device + 'static> EGLWaylandExtensions for DrmBackend<A> {
|
||||||
|
fn bind_wl_display(&self, display: &Display) -> EGLResult<EGLDisplay> {
|
||||||
|
self.backend.context.bind_wl_display(display)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unbind_wl_display(&self, display: &Display) -> EGLResult<()> {
|
||||||
|
self.backend.context.unbind_wl_display(display)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bind_wl_display(&self, display: &Display) -> ::std::result::Result<(), EglExtensionNotSupportedError> {
|
|
||||||
self.context.bind_wl_display(display).map_err(EglExtensionNotSupportedError::from)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unbind_wl_display(&self, display: &Display) -> ::std::result::Result<(), EglExtensionNotSupportedError> {
|
|
||||||
self.context.unbind_wl_display(display).map_err(EglExtensionNotSupportedError::from)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*unsafe fn egl_image_to_texture(&self, image: EGLImage, tex_id: c_uint) -> ::std::result::Result<(), EglExtensionNotSupportedError> {
|
|
||||||
self.graphics.head().rent(|context| context.egl_image_to_texture(image, tex_id))?;
|
|
||||||
Ok(())
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,11 @@ error_chain! {
|
||||||
display("The drm device ({:?}) encountered an access error", dev),
|
display("The drm device ({:?}) encountered an access error", dev),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc = "Unable to determine device id of drm device"]
|
||||||
|
UnableToGetDeviceId {
|
||||||
|
description("Unable to determine device id of drm device"),
|
||||||
|
}
|
||||||
|
|
||||||
#[doc = "Creation of gbm resource failed"]
|
#[doc = "Creation of gbm resource failed"]
|
||||||
GbmInitFailed {
|
GbmInitFailed {
|
||||||
description("Creation of gbm resource failed"),
|
description("Creation of gbm resource failed"),
|
||||||
|
|
|
@ -188,9 +188,11 @@
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
#[cfg(feature = "backend_session")]
|
#[cfg(feature = "backend_session")]
|
||||||
use backend::graphics::egl::EGLGraphicsBackend;
|
use backend::graphics::egl::EglExtensionNotSupportedError;
|
||||||
use backend::graphics::egl::context::{EGLContext, GlAttributes, PixelFormatRequirements};
|
use backend::graphics::egl::context::{EGLContext, GlAttributes, PixelFormatRequirements};
|
||||||
|
use backend::graphics::egl::error::Result as EGLResult;
|
||||||
use backend::graphics::egl::native::Gbm;
|
use backend::graphics::egl::native::Gbm;
|
||||||
|
use backend::graphics::egl::wayland::{EGLWaylandExtensions, EGLDisplay, BufferAccessError, EGLImages};
|
||||||
#[cfg(feature = "backend_session")]
|
#[cfg(feature = "backend_session")]
|
||||||
use backend::session::SessionObserver;
|
use backend::session::SessionObserver;
|
||||||
use drm::Device as BasicDevice;
|
use drm::Device as BasicDevice;
|
||||||
|
@ -200,36 +202,41 @@ use drm::result::Error as DrmError;
|
||||||
use drm::control::framebuffer;
|
use drm::control::framebuffer;
|
||||||
use gbm::Device as GbmDevice;
|
use gbm::Device as GbmDevice;
|
||||||
use nix;
|
use nix;
|
||||||
use std::borrow::Borrow;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::io::Result as IoResult;
|
use std::io::Result as IoResult;
|
||||||
use std::os::unix::io::{AsRawFd, RawFd};
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
use std::rc::Rc;
|
use std::rc::{Rc, Weak};
|
||||||
use std::sync::{Once, ONCE_INIT};
|
use std::sync::{Once, ONCE_INIT};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use wayland_server::{EventLoopHandle, StateProxy, StateToken};
|
use nix::sys::stat::{dev_t, fstat};
|
||||||
|
use wayland_server::{EventLoopHandle, StateToken};
|
||||||
use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest};
|
use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest};
|
||||||
|
#[cfg(feature = "backend_session")]
|
||||||
|
use wayland_server::{Display, StateProxy};
|
||||||
|
use wayland_server::protocol::wl_buffer::WlBuffer;
|
||||||
|
|
||||||
mod backend;
|
mod backend;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
|
||||||
pub use self::backend::DrmBackend;
|
pub use self::backend::DrmBackend;
|
||||||
|
use self::backend::DrmBackendInternal;
|
||||||
use self::error::*;
|
use self::error::*;
|
||||||
|
|
||||||
static LOAD: Once = ONCE_INIT;
|
static LOAD: Once = ONCE_INIT;
|
||||||
|
|
||||||
/// Representation of an open drm device node to create rendering backends
|
/// Representation of an open drm device node to create rendering backends
|
||||||
pub struct DrmDevice<A: ControlDevice + 'static, B: Borrow<DrmBackend<A>> + 'static> {
|
pub struct DrmDevice<A: ControlDevice + 'static> {
|
||||||
context: Rc<EGLContext<Gbm<framebuffer::Info>, GbmDevice<A>>>,
|
context: Rc<EGLContext<Gbm<framebuffer::Info>, GbmDevice<A>>>,
|
||||||
backends: HashMap<crtc::Handle, StateToken<B>>,
|
|
||||||
old_state: HashMap<crtc::Handle, (crtc::Info, Vec<connector::Handle>)>,
|
old_state: HashMap<crtc::Handle, (crtc::Info, Vec<connector::Handle>)>,
|
||||||
|
device_id: dev_t,
|
||||||
|
backends: HashMap<crtc::Handle, Weak<DrmBackendInternal<A>>>,
|
||||||
active: bool,
|
active: bool,
|
||||||
logger: ::slog::Logger,
|
logger: ::slog::Logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: ControlDevice + 'static, B: From<DrmBackend<A>> + Borrow<DrmBackend<A>> + 'static> DrmDevice<A, B> {
|
impl<A: ControlDevice + 'static> DrmDevice<A> {
|
||||||
/// Create a new `DrmDevice` from an open drm node
|
/// Create a new `DrmDevice` from an open drm node
|
||||||
///
|
///
|
||||||
/// Returns an error if the file is no valid drm node or context creation was not
|
/// Returns an error if the file is no valid drm node or context creation was not
|
||||||
|
@ -273,6 +280,8 @@ impl<A: ControlDevice + 'static, B: From<DrmBackend<A>> + Borrow<DrmBackend<A>>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let device_id = fstat(dev.as_raw_fd()).chain_err(|| ErrorKind::UnableToGetDeviceId)?.st_rdev;
|
||||||
|
|
||||||
let mut drm = DrmDevice {
|
let mut drm = DrmDevice {
|
||||||
// Open the gbm device from the drm device and create a context based on that
|
// Open the gbm device from the drm device and create a context based on that
|
||||||
context: Rc::new(EGLContext::new(
|
context: Rc::new(EGLContext::new(
|
||||||
|
@ -283,15 +292,11 @@ impl<A: ControlDevice + 'static, B: From<DrmBackend<A>> + Borrow<DrmBackend<A>>
|
||||||
gbm
|
gbm
|
||||||
},
|
},
|
||||||
attributes,
|
attributes,
|
||||||
PixelFormatRequirements {
|
Default::default(),
|
||||||
hardware_accelerated: Some(true),
|
|
||||||
color_bits: Some(24),
|
|
||||||
alpha_bits: Some(8),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
log.clone(),
|
log.clone(),
|
||||||
).map_err(Error::from)?),
|
).map_err(Error::from)?),
|
||||||
backends: HashMap::new(),
|
backends: HashMap::new(),
|
||||||
|
device_id,
|
||||||
old_state: HashMap::new(),
|
old_state: HashMap::new(),
|
||||||
active: true,
|
active: true,
|
||||||
logger: log.clone(),
|
logger: log.clone(),
|
||||||
|
@ -330,12 +335,11 @@ impl<A: ControlDevice + 'static, B: From<DrmBackend<A>> + Borrow<DrmBackend<A>>
|
||||||
///
|
///
|
||||||
/// Errors if initialization fails or the mode is not available on all given
|
/// Errors if initialization fails or the mode is not available on all given
|
||||||
/// connectors.
|
/// connectors.
|
||||||
pub fn create_backend<'a, I, S>(
|
pub fn create_backend<I>(
|
||||||
&mut self, state: S, crtc: crtc::Handle, mode: Mode, connectors: I
|
&mut self, crtc: crtc::Handle, mode: Mode, connectors: I
|
||||||
) -> Result<&StateToken<B>>
|
) -> Result<DrmBackend<A>>
|
||||||
where
|
where
|
||||||
I: Into<Vec<connector::Handle>>,
|
I: Into<Vec<connector::Handle>>
|
||||||
S: Into<StateProxy<'a>>,
|
|
||||||
{
|
{
|
||||||
if self.backends.contains_key(&crtc) {
|
if self.backends.contains_key(&crtc) {
|
||||||
bail!(ErrorKind::CrtcAlreadyInUse(crtc));
|
bail!(ErrorKind::CrtcAlreadyInUse(crtc));
|
||||||
|
@ -388,33 +392,13 @@ impl<A: ControlDevice + 'static, B: From<DrmBackend<A>> + Borrow<DrmBackend<A>>
|
||||||
|
|
||||||
let logger = self.logger.new(o!("crtc" => format!("{:?}", crtc)));
|
let logger = self.logger.new(o!("crtc" => format!("{:?}", crtc)));
|
||||||
let backend = DrmBackend::new(self.context.clone(), crtc, mode, connectors, logger)?;
|
let backend = DrmBackend::new(self.context.clone(), crtc, mode, connectors, logger)?;
|
||||||
self.backends
|
self.backends.insert(crtc, backend.weak());
|
||||||
.insert(crtc, state.into().insert(backend.into()));
|
Ok(backend)
|
||||||
|
|
||||||
Ok(self.backends.get(&crtc).unwrap())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the current backend for a given crtc if any
|
/// Returns an internal device id, that is unique per boot per system
|
||||||
pub fn backend_for_crtc(&self, crtc: &crtc::Handle) -> Option<&StateToken<B>> {
|
pub fn device_id(&self) -> u64 {
|
||||||
self.backends.get(crtc)
|
self.device_id
|
||||||
}
|
|
||||||
|
|
||||||
/// Get all belonging backends
|
|
||||||
pub fn current_backends(&self) -> Vec<&StateToken<B>> {
|
|
||||||
self.backends.values().collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Destroy the backend using a given crtc if any
|
|
||||||
///
|
|
||||||
/// ## Panics
|
|
||||||
/// Panics if the backend is already borrowed from the state
|
|
||||||
pub fn destroy_backend<'a, S>(&mut self, state: S, crtc: &crtc::Handle)
|
|
||||||
where
|
|
||||||
S: Into<StateProxy<'a>>,
|
|
||||||
{
|
|
||||||
if let Some(token) = self.backends.remove(crtc) {
|
|
||||||
state.into().remove(token);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -430,20 +414,43 @@ impl<A: AsRawFd> DevPath for A {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<A: ControlDevice + 'static> PartialEq for DrmDevice<A> {
|
||||||
|
fn eq(&self, other: &DrmDevice<A>) -> bool {
|
||||||
|
self.device_id == other.device_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<A: ControlDevice + 'static> Eq for DrmDevice<A> {}
|
||||||
|
|
||||||
|
impl<A: ControlDevice + 'static> Hash for DrmDevice<A> {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.device_id.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// for users convinience and FdEventSource registering
|
// for users convinience and FdEventSource registering
|
||||||
impl<A: ControlDevice + 'static, B: Borrow<DrmBackend<A>> + 'static> AsRawFd for DrmDevice<A, B> {
|
impl<A: ControlDevice + 'static> AsRawFd for DrmDevice<A> {
|
||||||
fn as_raw_fd(&self) -> RawFd {
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
self.context.as_raw_fd()
|
self.context.as_raw_fd()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: ControlDevice + 'static, B: Borrow<DrmBackend<A>> + 'static> BasicDevice for DrmDevice<A, B> {}
|
impl<A: ControlDevice + 'static> BasicDevice for DrmDevice<A> {}
|
||||||
impl<A: ControlDevice + 'static, B: Borrow<DrmBackend<A>> + 'static> ControlDevice for DrmDevice<A, B> {}
|
impl<A: ControlDevice + 'static> ControlDevice for DrmDevice<A> {}
|
||||||
|
|
||||||
impl<A: ControlDevice + 'static, B: Borrow<DrmBackend<A>> + 'static> Drop for DrmDevice<A, B> {
|
impl<A: ControlDevice + 'static> EGLWaylandExtensions for DrmDevice<A> {
|
||||||
|
fn bind_wl_display(&self, display: &Display) -> EGLResult<EGLDisplay> {
|
||||||
|
self.context.bind_wl_display(display)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unbind_wl_display(&self, display: &Display) -> EGLResult<()> {
|
||||||
|
self.context.unbind_wl_display(display)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A: ControlDevice + 'static> Drop for DrmDevice<A> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if Rc::strong_count(&self.context) > 1 {
|
if Rc::strong_count(&self.context) > 1 {
|
||||||
panic!("Pending DrmBackends. Please free all backends before the DrmDevice gets destroyed");
|
panic!("Pending DrmBackends. You need to free all backends before the DrmDevice gets destroyed");
|
||||||
}
|
}
|
||||||
for (handle, (info, connectors)) in self.old_state.drain() {
|
for (handle, (info, connectors)) in self.old_state.drain() {
|
||||||
if let Err(err) = crtc::set(
|
if let Err(err) = crtc::set(
|
||||||
|
@ -469,50 +476,32 @@ impl<A: ControlDevice + 'static, B: Borrow<DrmBackend<A>> + 'static> Drop for Dr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: ControlDevice + 'static, B: Borrow<DrmBackend<A>> + 'static> Hash for DrmDevice<A, B> {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
||||||
self.as_raw_fd().hash(state)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Handler for drm node events
|
/// Handler for drm node events
|
||||||
///
|
///
|
||||||
/// See module-level documentation for its use
|
/// See module-level documentation for its use
|
||||||
pub trait DrmHandler<A: ControlDevice + 'static, B: Borrow<DrmBackend<A>> + 'static> {
|
pub trait DrmHandler<A: ControlDevice + 'static> {
|
||||||
/// A `DrmBackend` has finished swapping buffers and new frame can now
|
/// The `DrmBackend` of crtc has finished swapping buffers and new frame can now
|
||||||
/// (and should be immediately) be rendered.
|
/// (and should be immediately) be rendered.
|
||||||
///
|
fn ready(
|
||||||
/// The `id` argument is the `Id` of the `DrmBackend` that finished rendering,
|
&mut self, device: &mut DrmDevice<A>, crtc: crtc::Handle,
|
||||||
/// check using `DrmBackend::is`.
|
|
||||||
///
|
|
||||||
/// ## Panics
|
|
||||||
/// The device is already borrowed from the given `state`. Borrowing it again will panic
|
|
||||||
/// and is not necessary as it is already provided via the `device` parameter.
|
|
||||||
fn ready<'a, S: Into<StateProxy<'a>>>(
|
|
||||||
&mut self, state: S, device: &mut DrmDevice<A, B>, backend: &StateToken<B>, crtc: crtc::Handle,
|
|
||||||
frame: u32, duration: Duration,
|
frame: u32, duration: Duration,
|
||||||
);
|
);
|
||||||
/// The `DrmDevice` has thrown an error.
|
/// The `DrmDevice` has thrown an error.
|
||||||
///
|
///
|
||||||
/// The related backends are most likely *not* usable anymore and
|
/// The related backends are most likely *not* usable anymore and
|
||||||
/// the whole stack has to be recreated..
|
/// the whole stack has to be recreated..
|
||||||
///
|
fn error(&mut self, device: &mut DrmDevice<A>, error: DrmError);
|
||||||
/// ## Panics
|
|
||||||
/// The device is already borrowed from the given `state`. Borrowing it again will panic
|
|
||||||
/// and is not necessary as it is already provided via the `device` parameter.
|
|
||||||
fn error<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &mut DrmDevice<A, B>, error: DrmError);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Bind a `DrmDevice` to an `EventLoop`,
|
/// Bind a `DrmDevice` to an `EventLoop`,
|
||||||
///
|
///
|
||||||
/// This will cause it to recieve events and feed them into an `DrmHandler`
|
/// This will cause it to recieve events and feed them into an `DrmHandler`
|
||||||
pub fn drm_device_bind<A, B, H>(
|
pub fn drm_device_bind<A, H>(
|
||||||
evlh: &mut EventLoopHandle, device: StateToken<DrmDevice<A, B>>, handler: H
|
evlh: &mut EventLoopHandle, device: StateToken<DrmDevice<A>>, handler: H
|
||||||
) -> IoResult<FdEventSource<(StateToken<DrmDevice<A, B>>, H)>>
|
) -> IoResult<FdEventSource<(StateToken<DrmDevice<A>>, H)>>
|
||||||
where
|
where
|
||||||
A: ControlDevice + 'static,
|
A: ControlDevice + 'static,
|
||||||
B: From<DrmBackend<A>> + Borrow<DrmBackend<A>> + 'static,
|
H: DrmHandler<A> + 'static,
|
||||||
H: DrmHandler<A, B> + 'static,
|
|
||||||
{
|
{
|
||||||
let fd = evlh.state().get(&device).as_raw_fd();
|
let fd = evlh.state().get(&device).as_raw_fd();
|
||||||
evlh.add_fd_event_source(
|
evlh.add_fd_event_source(
|
||||||
|
@ -523,11 +512,10 @@ where
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_event_source_implementation<A, B, H>() -> FdEventSourceImpl<(StateToken<DrmDevice<A, B>>, H)>
|
fn fd_event_source_implementation<A, H>() -> FdEventSourceImpl<(StateToken<DrmDevice<A>>, H)>
|
||||||
where
|
where
|
||||||
A: ControlDevice + 'static,
|
A: ControlDevice + 'static,
|
||||||
B: From<DrmBackend<A>> + Borrow<DrmBackend<A>> + 'static,
|
H: DrmHandler<A> + 'static,
|
||||||
H: DrmHandler<A, B> + 'static,
|
|
||||||
{
|
{
|
||||||
FdEventSourceImpl {
|
FdEventSourceImpl {
|
||||||
ready: |evlh, &mut (ref mut dev_token, ref mut handler), _, _| {
|
ready: |evlh, &mut (ref mut dev_token, ref mut handler), _, _| {
|
||||||
|
@ -541,44 +529,40 @@ where
|
||||||
match events {
|
match events {
|
||||||
Ok(events) => for event in events {
|
Ok(events) => for event in events {
|
||||||
if let crtc::Event::PageFlip(event) = event {
|
if let crtc::Event::PageFlip(event) = event {
|
||||||
evlh.state().with_value(dev_token, |state, mut dev| {
|
let dev = evlh.state().get_mut(dev_token);
|
||||||
if dev.active {
|
if dev.active {
|
||||||
if let Some(backend_token) = dev.backend_for_crtc(&event.crtc).cloned() {
|
if let Some(backend) = dev.backends.get(&event.crtc).iter().flat_map(|x| x.upgrade()).next() {
|
||||||
// we can now unlock the buffer
|
// we can now unlock the buffer
|
||||||
state.get(&backend_token).borrow().unlock_buffer();
|
backend.unlock_buffer();
|
||||||
trace!(logger, "Handling event for backend {:?}", event.crtc);
|
trace!(logger, "Handling event for backend {:?}", event.crtc);
|
||||||
// and then call the user to render the next frame
|
// and then call the user to render the next frame
|
||||||
handler.ready(
|
handler.ready(
|
||||||
state,
|
dev,
|
||||||
&mut dev,
|
|
||||||
&backend_token,
|
|
||||||
event.crtc,
|
event.crtc,
|
||||||
event.frame,
|
event.frame,
|
||||||
event.duration,
|
event.duration,
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
dev.backends.remove(&event.crtc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(err) => evlh.state().with_value(dev_token, |state, mut dev| {
|
Err(err) => handler.error(evlh.state().get_mut(dev_token), err),
|
||||||
handler.error(state, &mut dev, err)
|
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
error: |evlh, &mut (ref mut dev_token, ref mut handler), _, error| {
|
error: |evlh, &mut (ref mut dev_token, ref mut handler), _, error| {
|
||||||
evlh.state().with_value(dev_token, |state, mut dev| {
|
let mut dev = evlh.state().get_mut(dev_token);
|
||||||
warn!(dev.logger, "DrmDevice errored: {}", error);
|
warn!(dev.logger, "DrmDevice errored: {}", error);
|
||||||
handler.error(state, &mut dev, error.into());
|
handler.error(&mut dev, error.into());
|
||||||
})
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "backend_session")]
|
#[cfg(feature = "backend_session")]
|
||||||
impl<A: ControlDevice + 'static, B: Borrow<DrmBackend<A>> + 'static> SessionObserver for StateToken<DrmDevice<A, B>> {
|
impl<A: ControlDevice + 'static> SessionObserver for StateToken<DrmDevice<A>> {
|
||||||
fn pause<'a>(&mut self, state: &mut StateProxy<'a>) {
|
fn pause<'a>(&mut self, state: &mut StateProxy<'a>) {
|
||||||
let device: &mut DrmDevice<A, B> = state.get_mut(self);
|
let device: &mut DrmDevice<A> = state.get_mut(self);
|
||||||
device.active = false;
|
device.active = false;
|
||||||
if let Err(err) = device.drop_master() {
|
if let Err(err) = device.drop_master() {
|
||||||
error!(
|
error!(
|
||||||
|
@ -598,17 +582,24 @@ impl<A: ControlDevice + 'static, B: Borrow<DrmBackend<A>> + 'static> SessionObse
|
||||||
err
|
err
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
for token in device.backends.values() {
|
let mut crtcs = Vec::new();
|
||||||
let backend = state.get(token);
|
for (crtc, backend) in device.backends.iter() {
|
||||||
if let Err(err) = backend.borrow().swap_buffers() {
|
if let Some(backend) = backend.upgrade() {
|
||||||
// TODO handle this better?
|
backend.unlock_buffer();
|
||||||
|
if let Err(err) = backend.page_flip(None) {
|
||||||
error!(
|
error!(
|
||||||
device.logger,
|
device.logger,
|
||||||
"Failed to activate crtc ({:?}) again. Error: {}",
|
"Failed to activate crtc ({:?}) again. Error: {}",
|
||||||
backend.borrow().crtc(),
|
crtc,
|
||||||
err
|
err
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
crtcs.push(*crtc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for crtc in crtcs {
|
||||||
|
device.backends.remove(&crtc);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ pub mod native;
|
||||||
pub mod surface;
|
pub mod surface;
|
||||||
pub use self::surface::EGLSurface;
|
pub use self::surface::EGLSurface;
|
||||||
pub mod wayland;
|
pub mod wayland;
|
||||||
pub use self::wayland::{EGLImages, BufferAccessError};
|
pub use self::wayland::{EGLWaylandExtensions, EGLImages, BufferAccessError};
|
||||||
|
|
||||||
/// Error that can happen when swapping buffers.
|
/// Error that can happen when swapping buffers.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
@ -90,15 +90,6 @@ impl ::std::error::Error for EglExtensionNotSupportedError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<error::Error> for EglExtensionNotSupportedError {
|
|
||||||
fn from(error: error::Error) -> Self {
|
|
||||||
match *error.kind() {
|
|
||||||
error::ErrorKind::EglExtensionNotSupported(extensions) => EglExtensionNotSupportedError(extensions),
|
|
||||||
_ => panic!("Error not convertible"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Describes the pixel format of the main framebuffer
|
/// Describes the pixel format of the main framebuffer
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct PixelFormat {
|
pub struct PixelFormat {
|
||||||
|
@ -153,35 +144,4 @@ pub trait EGLGraphicsBackend: GraphicsBackend {
|
||||||
|
|
||||||
/// Returns the pixel format of the main framebuffer of the context.
|
/// Returns the pixel format of the main framebuffer of the context.
|
||||||
fn get_pixel_format(&self) -> PixelFormat;
|
fn get_pixel_format(&self) -> PixelFormat;
|
||||||
|
|
||||||
/// Binds this EGL context to the given Wayland display.
|
|
||||||
///
|
|
||||||
/// This will allow clients to utilize EGL to create hardware-accelerated
|
|
||||||
/// surfaces. The server will need to be able to handle egl-wl_buffers.
|
|
||||||
/// See the `wayland::drm` module.
|
|
||||||
///
|
|
||||||
/// ## Errors
|
|
||||||
///
|
|
||||||
/// This might return `WlExtensionNotSupported` if binding is not supported
|
|
||||||
/// by the EGL implementation.
|
|
||||||
///
|
|
||||||
/// This might return `OtherEGLDisplayAlreadyBound` if called for the same
|
|
||||||
/// `Display` multiple times, as only one context may be bound at any given time.
|
|
||||||
fn bind_wl_display(&self, display: &Display) -> ::std::result::Result<(), EglExtensionNotSupportedError>;
|
|
||||||
|
|
||||||
/// Unbinds this EGL context from the given Wayland display.
|
|
||||||
///
|
|
||||||
/// This will stop clients from using previously available extensions
|
|
||||||
/// to utilize hardware-accelerated surface via EGL.
|
|
||||||
///
|
|
||||||
/// ## Errors
|
|
||||||
///
|
|
||||||
/// This might return `WlExtensionNotSupported` if binding is not supported
|
|
||||||
/// by the EGL implementation.
|
|
||||||
///
|
|
||||||
/// This might return `OtherEGLDisplayAlreadyBound` if called for the same
|
|
||||||
/// `Display` multiple times, as only one context may be bound at any given time.
|
|
||||||
fn unbind_wl_display(&self, display: &Display) -> ::std::result::Result<(), EglExtensionNotSupportedError>;
|
|
||||||
|
|
||||||
fn egl_buffer_contents(&self, buffer: WlBuffer) -> ::std::result::Result<EGLImages, wayland::BufferAccessError>;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,6 +66,7 @@ impl Backend for Wayland {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "backend_winit")]
|
||||||
pub struct XlibWindow(*const c_void);
|
pub struct XlibWindow(*const c_void);
|
||||||
#[cfg(feature = "backend_winit")]
|
#[cfg(feature = "backend_winit")]
|
||||||
pub enum X11 {}
|
pub enum X11 {}
|
||||||
|
|
|
@ -4,11 +4,13 @@ use backend::graphics::egl::ffi::egl::types::EGLImage;
|
||||||
use nix::libc::{c_uint};
|
use nix::libc::{c_uint};
|
||||||
use std::rc::{Rc, Weak};
|
use std::rc::{Rc, Weak};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use wayland_server::{Display, Resource};
|
use wayland_server::{Display, Resource, StateToken, StateProxy};
|
||||||
use wayland_server::protocol::wl_buffer::WlBuffer;
|
use wayland_server::protocol::wl_buffer::WlBuffer;
|
||||||
|
|
||||||
/// Error that can occur when accessing an EGL buffer
|
/// Error that can occur when accessing an EGL buffer
|
||||||
pub enum BufferAccessError {
|
pub enum BufferAccessError {
|
||||||
|
/// The corresponding Context is not alive anymore
|
||||||
|
ContextLost,
|
||||||
/// This buffer is not managed by the EGL buffer
|
/// This buffer is not managed by the EGL buffer
|
||||||
NotManaged(WlBuffer),
|
NotManaged(WlBuffer),
|
||||||
/// Failed to create EGLImages from the buffer
|
/// Failed to create EGLImages from the buffer
|
||||||
|
@ -20,6 +22,7 @@ pub enum BufferAccessError {
|
||||||
impl fmt::Debug for BufferAccessError {
|
impl fmt::Debug for BufferAccessError {
|
||||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
|
fn fmt(&self, formatter: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
|
||||||
match *self {
|
match *self {
|
||||||
|
BufferAccessError::ContextLost => write!(formatter, "BufferAccessError::ContextLost"),
|
||||||
BufferAccessError::NotManaged(_) => write!(formatter, "BufferAccessError::NotManaged"),
|
BufferAccessError::NotManaged(_) => write!(formatter, "BufferAccessError::NotManaged"),
|
||||||
BufferAccessError::EGLImageCreationFailed => write!(formatter, "BufferAccessError::EGLImageCreationFailed"),
|
BufferAccessError::EGLImageCreationFailed => write!(formatter, "BufferAccessError::EGLImageCreationFailed"),
|
||||||
BufferAccessError::EglExtensionNotSupported(ref err) => write!(formatter, "{:?}", err),
|
BufferAccessError::EglExtensionNotSupported(ref err) => write!(formatter, "{:?}", err),
|
||||||
|
@ -31,8 +34,7 @@ impl fmt::Display for BufferAccessError {
|
||||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
|
fn fmt(&self, formatter: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
|
||||||
use ::std::error::Error;
|
use ::std::error::Error;
|
||||||
match *self {
|
match *self {
|
||||||
BufferAccessError::NotManaged(_) => write!(formatter, "{}", self.description()),
|
BufferAccessError::ContextLost | BufferAccessError::NotManaged(_) | BufferAccessError::EGLImageCreationFailed => write!(formatter, "{}", self.description()),
|
||||||
BufferAccessError::EGLImageCreationFailed => write!(formatter, "{}", self.description()),
|
|
||||||
BufferAccessError::EglExtensionNotSupported(ref err) => err.fmt(formatter),
|
BufferAccessError::EglExtensionNotSupported(ref err) => err.fmt(formatter),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,6 +43,7 @@ impl fmt::Display for BufferAccessError {
|
||||||
impl ::std::error::Error for BufferAccessError {
|
impl ::std::error::Error for BufferAccessError {
|
||||||
fn description(&self) -> &str {
|
fn description(&self) -> &str {
|
||||||
match *self {
|
match *self {
|
||||||
|
BufferAccessError::ContextLost => "The corresponding context was lost",
|
||||||
BufferAccessError::NotManaged(_) => "This buffer is not mananged by EGL",
|
BufferAccessError::NotManaged(_) => "This buffer is not mananged by EGL",
|
||||||
BufferAccessError::EGLImageCreationFailed => "Failed to create EGLImages from the buffer",
|
BufferAccessError::EGLImageCreationFailed => "Failed to create EGLImages from the buffer",
|
||||||
BufferAccessError::EglExtensionNotSupported(ref err) => err.description(),
|
BufferAccessError::EglExtensionNotSupported(ref err) => err.description(),
|
||||||
|
@ -173,7 +176,7 @@ impl Drop for EGLImages {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B: native::Backend, N: native::NativeDisplay<B>> EGLContext<B, N> {
|
pub trait EGLWaylandExtensions {
|
||||||
/// Binds this EGL context to the given Wayland display.
|
/// Binds this EGL context to the given Wayland display.
|
||||||
///
|
///
|
||||||
/// This will allow clients to utilize EGL to create hardware-accelerated
|
/// This will allow clients to utilize EGL to create hardware-accelerated
|
||||||
|
@ -187,16 +190,7 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLContext<B, N> {
|
||||||
///
|
///
|
||||||
/// This might return `OtherEGLDisplayAlreadyBound` if called for the same
|
/// This might return `OtherEGLDisplayAlreadyBound` if called for the same
|
||||||
/// `Display` multiple times, as only one context may be bound at any given time.
|
/// `Display` multiple times, as only one context may be bound at any given time.
|
||||||
pub fn bind_wl_display(&self, display: &Display) -> Result<()> {
|
fn bind_wl_display(&self, display: &Display) -> Result<EGLDisplay>;
|
||||||
if !self.wl_drm_support {
|
|
||||||
bail!(ErrorKind::EglExtensionNotSupported(&["EGL_WL_bind_wayland_display"]));
|
|
||||||
}
|
|
||||||
let res = unsafe { ffi::egl::BindWaylandDisplayWL(*self.display, display.ptr() as *mut _) };
|
|
||||||
if res == 0 {
|
|
||||||
bail!(ErrorKind::OtherEGLDisplayAlreadyBound);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Unbinds this EGL context from the given Wayland display.
|
/// Unbinds this EGL context from the given Wayland display.
|
||||||
///
|
///
|
||||||
|
@ -210,27 +204,20 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLContext<B, N> {
|
||||||
///
|
///
|
||||||
/// This might return `OtherEGLDisplayAlreadyBound` if called for the same
|
/// This might return `OtherEGLDisplayAlreadyBound` if called for the same
|
||||||
/// `Display` multiple times, as only one context may be bound at any given time.
|
/// `Display` multiple times, as only one context may be bound at any given time.
|
||||||
pub fn unbind_wl_display(&self, display: &Display) -> Result<()> {
|
fn unbind_wl_display(&self, display: &Display) -> Result<()>;
|
||||||
if !self.wl_drm_support {
|
}
|
||||||
bail!(ErrorKind::EglExtensionNotSupported(&["EGL_WL_bind_wayland_display"]));
|
|
||||||
}
|
pub struct EGLDisplay(Weak<ffi::egl::types::EGLDisplay>);
|
||||||
let res = unsafe { ffi::egl::UnbindWaylandDisplayWL(*self.display, display.ptr() as *mut _) };
|
|
||||||
if res == 0 {
|
impl EGLDisplay {
|
||||||
bail!(ErrorKind::NoEGLDisplayBound);
|
pub fn new<B: native::Backend, N: native::NativeDisplay<B>>(context: &EGLContext<B, N>) -> EGLDisplay {
|
||||||
}
|
EGLDisplay(Rc::downgrade(&context.display))
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn egl_buffer_contents(&self, buffer: WlBuffer) -> ::std::result::Result<EGLImages, BufferAccessError> {
|
pub fn egl_buffer_contents(&self, buffer: WlBuffer) -> ::std::result::Result<EGLImages, BufferAccessError> {
|
||||||
if !self.wl_drm_support {
|
if let Some(display) = self.0.upgrade() {
|
||||||
return Err(EglExtensionNotSupportedError(&["EGL_WL_bind_wayland_display"]).into());
|
|
||||||
}
|
|
||||||
if !self.egl_to_texture_support {
|
|
||||||
return Err(EglExtensionNotSupportedError(&["GL_OES_EGL_image"]).into());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut format: i32 = 0;
|
let mut format: i32 = 0;
|
||||||
if unsafe { ffi::egl::QueryWaylandBufferWL(*self.display, buffer.ptr() as *mut _, ffi::egl::EGL_TEXTURE_FORMAT, &mut format as *mut _) == 0 } {
|
if unsafe { ffi::egl::QueryWaylandBufferWL(*display, buffer.ptr() as *mut _, ffi::egl::EGL_TEXTURE_FORMAT, &mut format as *mut _) == 0 } {
|
||||||
return Err(BufferAccessError::NotManaged(buffer));
|
return Err(BufferAccessError::NotManaged(buffer));
|
||||||
}
|
}
|
||||||
let format = match format {
|
let format = match format {
|
||||||
|
@ -244,17 +231,17 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLContext<B, N> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut width: i32 = 0;
|
let mut width: i32 = 0;
|
||||||
if unsafe { ffi::egl::QueryWaylandBufferWL(*self.display, buffer.ptr() as *mut _, ffi::egl::WIDTH as i32, &mut width as *mut _) == 0 } {
|
if unsafe { ffi::egl::QueryWaylandBufferWL(*display, buffer.ptr() as *mut _, ffi::egl::WIDTH as i32, &mut width as *mut _) == 0 } {
|
||||||
return Err(BufferAccessError::NotManaged(buffer));
|
return Err(BufferAccessError::NotManaged(buffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut height: i32 = 0;
|
let mut height: i32 = 0;
|
||||||
if unsafe { ffi::egl::QueryWaylandBufferWL(*self.display, buffer.ptr() as *mut _, ffi::egl::HEIGHT as i32, &mut height as *mut _) == 0 } {
|
if unsafe { ffi::egl::QueryWaylandBufferWL(*display, buffer.ptr() as *mut _, ffi::egl::HEIGHT as i32, &mut height as *mut _) == 0 } {
|
||||||
return Err(BufferAccessError::NotManaged(buffer));
|
return Err(BufferAccessError::NotManaged(buffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut inverted: i32 = 0;
|
let mut inverted: i32 = 0;
|
||||||
if unsafe { ffi::egl::QueryWaylandBufferWL(*self.display, buffer.ptr() as *mut _, ffi::egl::WAYLAND_Y_INVERTED_WL, &mut inverted as *mut _) != 0 } {
|
if unsafe { ffi::egl::QueryWaylandBufferWL(*display, buffer.ptr() as *mut _, ffi::egl::WAYLAND_Y_INVERTED_WL, &mut inverted as *mut _) != 0 } {
|
||||||
inverted = 1;
|
inverted = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -268,7 +255,7 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLContext<B, N> {
|
||||||
images.push({
|
images.push({
|
||||||
let image =
|
let image =
|
||||||
unsafe { ffi::egl::CreateImageKHR(
|
unsafe { ffi::egl::CreateImageKHR(
|
||||||
*self.display,
|
*display,
|
||||||
ffi::egl::NO_CONTEXT,
|
ffi::egl::NO_CONTEXT,
|
||||||
ffi::egl::WAYLAND_BUFFER_WL,
|
ffi::egl::WAYLAND_BUFFER_WL,
|
||||||
buffer.ptr() as *mut _,
|
buffer.ptr() as *mut _,
|
||||||
|
@ -283,7 +270,7 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLContext<B, N> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(EGLImages {
|
Ok(EGLImages {
|
||||||
display: Rc::downgrade(&self.display),
|
display: Rc::downgrade(&display),
|
||||||
width: width as u32,
|
width: width as u32,
|
||||||
height: height as u32,
|
height: height as u32,
|
||||||
y_inverted: inverted != 0,
|
y_inverted: inverted != 0,
|
||||||
|
@ -291,5 +278,45 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLContext<B, N> {
|
||||||
images,
|
images,
|
||||||
buffer,
|
buffer,
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
Err(BufferAccessError::ContextLost)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: EGLWaylandExtensions> EGLWaylandExtensions for Rc<E>
|
||||||
|
{
|
||||||
|
fn bind_wl_display(&self, display: &Display) -> Result<EGLDisplay> {
|
||||||
|
(**self).bind_wl_display(display)
|
||||||
|
}
|
||||||
|
fn unbind_wl_display(&self, display: &Display) -> Result<()> {
|
||||||
|
(**self).unbind_wl_display(display)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: native::Backend, N: native::NativeDisplay<B>> EGLWaylandExtensions for EGLContext<B, N> {
|
||||||
|
fn bind_wl_display(&self, display: &Display) -> Result<EGLDisplay> {
|
||||||
|
if !self.wl_drm_support {
|
||||||
|
bail!(ErrorKind::EglExtensionNotSupported(&["EGL_WL_bind_wayland_display"]));
|
||||||
|
}
|
||||||
|
if !self.egl_to_texture_support {
|
||||||
|
bail!(ErrorKind::EglExtensionNotSupported(&["GL_OES_EGL_image"]));
|
||||||
|
}
|
||||||
|
let res = unsafe { ffi::egl::BindWaylandDisplayWL(*self.display, display.ptr() as *mut _) };
|
||||||
|
if res == 0 {
|
||||||
|
bail!(ErrorKind::OtherEGLDisplayAlreadyBound);
|
||||||
|
}
|
||||||
|
Ok(EGLDisplay::new(self))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unbind_wl_display(&self, display: &Display) -> Result<()> {
|
||||||
|
if !self.wl_drm_support {
|
||||||
|
bail!(ErrorKind::EglExtensionNotSupported(&["EGL_WL_bind_wayland_display"]));
|
||||||
|
}
|
||||||
|
let res = unsafe { ffi::egl::UnbindWaylandDisplayWL(*self.display, display.ptr() as *mut _) };
|
||||||
|
if res == 0 {
|
||||||
|
bail!(ErrorKind::NoEGLDisplayBound);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
//! Glium compatibility module
|
//! Glium compatibility module
|
||||||
|
|
||||||
use backend::graphics::egl::{EGLGraphicsBackend, SwapBuffersError};
|
use backend::graphics::egl::{EGLGraphicsBackend, SwapBuffersError, EglExtensionNotSupportedError};
|
||||||
|
use backend::graphics::egl::wayland::{BufferAccessError, EGLImages, EGLWaylandExtensions, EGLDisplay};
|
||||||
|
use backend::graphics::egl::error::Result as EGLResult;
|
||||||
use glium::Frame;
|
use glium::Frame;
|
||||||
use glium::SwapBuffersError as GliumSwapBuffersError;
|
use glium::SwapBuffersError as GliumSwapBuffersError;
|
||||||
use glium::backend::{Backend, Context, Facade};
|
use glium::backend::{Backend, Context, Facade};
|
||||||
|
@ -8,6 +10,8 @@ use glium::debug::DebugCallbackBehavior;
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use wayland_server::Display;
|
||||||
|
use wayland_server::protocol::wl_buffer::WlBuffer;
|
||||||
|
|
||||||
impl From<SwapBuffersError> for GliumSwapBuffersError {
|
impl From<SwapBuffersError> for GliumSwapBuffersError {
|
||||||
fn from(error: SwapBuffersError) -> Self {
|
fn from(error: SwapBuffersError) -> Self {
|
||||||
|
@ -73,6 +77,16 @@ impl<T: EGLGraphicsBackend + 'static> From<T> for GliumGraphicsBackend<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: EGLGraphicsBackend + EGLWaylandExtensions + 'static> EGLWaylandExtensions for GliumGraphicsBackend<T> {
|
||||||
|
fn bind_wl_display(&self, display: &Display) -> EGLResult<EGLDisplay> {
|
||||||
|
(*self.backend).0.bind_wl_display(display)
|
||||||
|
}
|
||||||
|
fn unbind_wl_display(&self, display: &Display) -> EGLResult<()> {
|
||||||
|
(*self.backend).0.unbind_wl_display(display)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
unsafe impl<T: EGLGraphicsBackend> Backend for InternalBackend<T> {
|
unsafe impl<T: EGLGraphicsBackend> Backend for InternalBackend<T> {
|
||||||
fn swap_buffers(&self) -> Result<(), GliumSwapBuffersError> {
|
fn swap_buffers(&self) -> Result<(), GliumSwapBuffersError> {
|
||||||
self.0.swap_buffers().map_err(Into::into)
|
self.0.swap_buffers().map_err(Into::into)
|
||||||
|
|
|
@ -11,11 +11,10 @@
|
||||||
|
|
||||||
use drm::Device as BasicDevice;
|
use drm::Device as BasicDevice;
|
||||||
use drm::control::Device as ControlDevice;
|
use drm::control::Device as ControlDevice;
|
||||||
use backend::drm::{drm_device_bind, DrmBackend, DrmDevice, DrmHandler};
|
use backend::drm::{drm_device_bind, DrmDevice, DrmHandler};
|
||||||
use backend::session::{Session, SessionObserver};
|
use backend::session::{Session, SessionObserver};
|
||||||
use nix::fcntl;
|
use nix::fcntl;
|
||||||
use nix::sys::stat::{dev_t, fstat};
|
use nix::sys::stat::{dev_t, fstat};
|
||||||
use std::borrow::Borrow;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::io::{Error as IoError, Result as IoResult};
|
use std::io::{Error as IoError, Result as IoResult};
|
||||||
|
@ -42,16 +41,15 @@ impl ControlDevice for SessionFdDrmDevice {}
|
||||||
/// given handler of any changes. Can be used to provide hot-plug functionality for gpus and
|
/// given handler of any changes. Can be used to provide hot-plug functionality for gpus and
|
||||||
/// attached monitors.
|
/// attached monitors.
|
||||||
pub struct UdevBackend<
|
pub struct UdevBackend<
|
||||||
B: Borrow<DrmBackend<SessionFdDrmDevice>> + 'static,
|
H: DrmHandler<SessionFdDrmDevice> + 'static,
|
||||||
H: DrmHandler<SessionFdDrmDevice, B> + 'static,
|
|
||||||
S: Session + 'static,
|
S: Session + 'static,
|
||||||
T: UdevHandler<B, H> + 'static,
|
T: UdevHandler<H> + 'static,
|
||||||
> {
|
> {
|
||||||
devices: HashMap<
|
devices: HashMap<
|
||||||
dev_t,
|
dev_t,
|
||||||
(
|
(
|
||||||
StateToken<DrmDevice<SessionFdDrmDevice, B>>,
|
StateToken<DrmDevice<SessionFdDrmDevice>>,
|
||||||
FdEventSource<(StateToken<DrmDevice<SessionFdDrmDevice, B>>, H)>,
|
FdEventSource<(StateToken<DrmDevice<SessionFdDrmDevice>>, H)>,
|
||||||
),
|
),
|
||||||
>,
|
>,
|
||||||
monitor: MonitorSocket,
|
monitor: MonitorSocket,
|
||||||
|
@ -61,11 +59,10 @@ pub struct UdevBackend<
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
B: From<DrmBackend<SessionFdDrmDevice>> + Borrow<DrmBackend<SessionFdDrmDevice>> + 'static,
|
H: DrmHandler<SessionFdDrmDevice> + 'static,
|
||||||
H: DrmHandler<SessionFdDrmDevice, B> + 'static,
|
|
||||||
S: Session + 'static,
|
S: Session + 'static,
|
||||||
T: UdevHandler<B, H> + 'static,
|
T: UdevHandler<H> + 'static,
|
||||||
> UdevBackend<B, H, S, T> {
|
> UdevBackend<H, S, T> {
|
||||||
/// Creates a new `UdevBackend` and adds it to the given `EventLoop`'s state.
|
/// Creates a new `UdevBackend` and adds it to the given `EventLoop`'s state.
|
||||||
///
|
///
|
||||||
/// ## Arguments
|
/// ## Arguments
|
||||||
|
@ -76,7 +73,7 @@ impl<
|
||||||
/// `logger` - slog Logger to be used by the backend and its `DrmDevices`.
|
/// `logger` - slog Logger to be used by the backend and its `DrmDevices`.
|
||||||
pub fn new<'a, L>(
|
pub fn new<'a, L>(
|
||||||
mut evlh: &mut EventLoopHandle, context: &Context, mut session: S, mut handler: T, logger: L
|
mut evlh: &mut EventLoopHandle, context: &Context, mut session: S, mut handler: T, logger: L
|
||||||
) -> Result<StateToken<UdevBackend<B, H, S, T>>>
|
) -> Result<StateToken<UdevBackend<H, S, T>>>
|
||||||
where
|
where
|
||||||
L: Into<Option<::slog::Logger>>,
|
L: Into<Option<::slog::Logger>>,
|
||||||
{
|
{
|
||||||
|
@ -99,31 +96,17 @@ impl<
|
||||||
}, logger.clone()
|
}, logger.clone()
|
||||||
) {
|
) {
|
||||||
// Call the handler, which might add it to the runloop
|
// Call the handler, which might add it to the runloop
|
||||||
Ok(mut device) => match handler.device_added(&mut evlh.state().as_proxy(), &mut device) {
|
Ok(mut device) => {
|
||||||
// fstat them
|
let fd = device.as_raw_fd();
|
||||||
Some(drm_handler) => match fstat(device.as_raw_fd()) {
|
let devnum = device.device_id();
|
||||||
Ok(stat) => {
|
match handler.device_added(&mut evlh.state().as_proxy(), &mut device) {
|
||||||
|
Some(drm_handler) => {
|
||||||
let token = evlh.state().insert(device);
|
let token = evlh.state().insert(device);
|
||||||
if let Ok(event_source) = drm_device_bind(&mut evlh, token.clone(), drm_handler) {
|
if let Ok(event_source) = drm_device_bind(&mut evlh, token.clone(), drm_handler) {
|
||||||
Some((stat.st_rdev, (token, event_source)))
|
Some((devnum, (token, event_source)))
|
||||||
} else {
|
} else {
|
||||||
handler.device_removed(evlh.state(), &token);
|
handler.device_removed(evlh.state(), &token);
|
||||||
let device = evlh.state().remove(token);
|
let device = evlh.state().remove(token);
|
||||||
let fd = device.as_raw_fd();
|
|
||||||
drop(device);
|
|
||||||
if let Err(err) = session.close(fd) {
|
|
||||||
warn!(logger, "Failed to close dropped device. Error: {:?}. Ignoring", err);
|
|
||||||
};
|
|
||||||
None
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(err) => {
|
|
||||||
// almost impossible to hit, but lets do it as good as possible
|
|
||||||
error!(logger, "Failed to get devnum of newly initialized device, dropping. Error: {:?}", err);
|
|
||||||
let token = evlh.state().insert(device);
|
|
||||||
handler.device_removed(evlh.state(), &token);
|
|
||||||
let device = evlh.state().remove(token);
|
|
||||||
let fd = device.as_raw_fd();
|
|
||||||
drop(device);
|
drop(device);
|
||||||
if let Err(err) = session.close(fd) {
|
if let Err(err) = session.close(fd) {
|
||||||
warn!(logger, "Failed to close dropped device. Error: {:?}. Ignoring", err);
|
warn!(logger, "Failed to close dropped device. Error: {:?}. Ignoring", err);
|
||||||
|
@ -132,13 +115,13 @@ impl<
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
let fd = device.as_raw_fd();
|
|
||||||
drop(device); //drops master
|
drop(device); //drops master
|
||||||
if let Err(err) = session.close(fd) {
|
if let Err(err) = session.close(fd) {
|
||||||
warn!(logger, "Failed to close device. Error: {:?}. Ignoring", err);
|
warn!(logger, "Failed to close device. Error: {:?}. Ignoring", err);
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
warn!(logger, "Failed to initialize device {:?}. Error: {:?}. Skipping", path, err);
|
warn!(logger, "Failed to initialize device {:?}. Error: {:?}. Skipping", path, err);
|
||||||
|
@ -146,7 +129,7 @@ impl<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<HashMap<dev_t, (StateToken<DrmDevice<SessionFdDrmDevice, B>>, FdEventSource<(StateToken<DrmDevice<SessionFdDrmDevice, B>>, H)>)>>();
|
.collect::<HashMap<dev_t, (StateToken<DrmDevice<SessionFdDrmDevice>>, FdEventSource<(StateToken<DrmDevice<SessionFdDrmDevice>>, H)>)>>();
|
||||||
|
|
||||||
let mut builder = MonitorBuilder::new(context).chain_err(|| ErrorKind::FailedToInitMonitor)?;
|
let mut builder = MonitorBuilder::new(context).chain_err(|| ErrorKind::FailedToInitMonitor)?;
|
||||||
builder
|
builder
|
||||||
|
@ -193,11 +176,10 @@ impl<
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
B: Borrow<DrmBackend<SessionFdDrmDevice>> + 'static,
|
H: DrmHandler<SessionFdDrmDevice> + 'static,
|
||||||
H: DrmHandler<SessionFdDrmDevice, B> + 'static,
|
|
||||||
S: Session + 'static,
|
S: Session + 'static,
|
||||||
T: UdevHandler<B, H> + 'static,
|
T: UdevHandler<H> + 'static,
|
||||||
> SessionObserver for StateToken<UdevBackend<B, H, S, T>> {
|
> SessionObserver for StateToken<UdevBackend<H, S, T>> {
|
||||||
fn pause<'a>(&mut self, state: &mut StateProxy<'a>) {
|
fn pause<'a>(&mut self, state: &mut StateProxy<'a>) {
|
||||||
state.with_value(self, |state, udev| {
|
state.with_value(self, |state, udev| {
|
||||||
for &mut (ref mut device, _) in udev.devices.values_mut() {
|
for &mut (ref mut device, _) in udev.devices.values_mut() {
|
||||||
|
@ -219,24 +201,22 @@ impl<
|
||||||
///
|
///
|
||||||
/// Allows the backend to recieve kernel events and thus to drive the `UdevHandler`.
|
/// Allows the backend to recieve kernel events and thus to drive the `UdevHandler`.
|
||||||
/// No runtime functionality can be provided without using this function.
|
/// No runtime functionality can be provided without using this function.
|
||||||
pub fn udev_backend_bind<B, S, H, T>(
|
pub fn udev_backend_bind<S, H, T>(
|
||||||
evlh: &mut EventLoopHandle, udev: StateToken<UdevBackend<B, H, S, T>>
|
evlh: &mut EventLoopHandle, udev: StateToken<UdevBackend<H, S, T>>
|
||||||
) -> IoResult<FdEventSource<StateToken<UdevBackend<B, H, S, T>>>>
|
) -> IoResult<FdEventSource<StateToken<UdevBackend<H, S, T>>>>
|
||||||
where
|
where
|
||||||
B: From<DrmBackend<SessionFdDrmDevice>> + Borrow<DrmBackend<SessionFdDrmDevice>> + 'static,
|
H: DrmHandler<SessionFdDrmDevice> + 'static,
|
||||||
H: DrmHandler<SessionFdDrmDevice, B> + 'static,
|
T: UdevHandler<H> + 'static,
|
||||||
T: UdevHandler<B, H> + 'static,
|
|
||||||
S: Session + 'static,
|
S: Session + 'static,
|
||||||
{
|
{
|
||||||
let fd = evlh.state().get(&udev).monitor.as_raw_fd();
|
let fd = evlh.state().get(&udev).monitor.as_raw_fd();
|
||||||
evlh.add_fd_event_source(fd, fd_event_source_implementation(), udev, FdInterest::READ)
|
evlh.add_fd_event_source(fd, fd_event_source_implementation(), udev, FdInterest::READ)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_event_source_implementation<B, S, H, T>() -> FdEventSourceImpl<StateToken<UdevBackend<B, H, S, T>>>
|
fn fd_event_source_implementation<S, H, T>() -> FdEventSourceImpl<StateToken<UdevBackend<H, S, T>>>
|
||||||
where
|
where
|
||||||
B: From<DrmBackend<SessionFdDrmDevice>> + Borrow<DrmBackend<SessionFdDrmDevice>> + 'static,
|
H: DrmHandler<SessionFdDrmDevice> + 'static,
|
||||||
H: DrmHandler<SessionFdDrmDevice, B> + 'static,
|
T: UdevHandler<H> + 'static,
|
||||||
T: UdevHandler<B, H> + 'static,
|
|
||||||
S: Session + 'static,
|
S: Session + 'static,
|
||||||
{
|
{
|
||||||
FdEventSourceImpl {
|
FdEventSourceImpl {
|
||||||
|
@ -282,6 +262,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let fd = device.as_raw_fd();
|
||||||
match evlh.state().with_value(token, |state, udev| {
|
match evlh.state().with_value(token, |state, udev| {
|
||||||
udev.handler.device_added(state, &mut device)
|
udev.handler.device_added(state, &mut device)
|
||||||
}) {
|
}) {
|
||||||
|
@ -299,7 +280,6 @@ where
|
||||||
let mut state: StateProxy = state.into();
|
let mut state: StateProxy = state.into();
|
||||||
udev.handler.device_removed(&mut state, &dev_token);
|
udev.handler.device_removed(&mut state, &dev_token);
|
||||||
let device = state.remove(dev_token);
|
let device = state.remove(dev_token);
|
||||||
let fd = device.as_raw_fd();
|
|
||||||
drop(device);
|
drop(device);
|
||||||
if let Err(err) = udev.session.close(fd) {
|
if let Err(err) = udev.session.close(fd) {
|
||||||
warn!(
|
warn!(
|
||||||
|
@ -312,7 +292,6 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let fd = device.as_raw_fd();
|
|
||||||
drop(device);
|
drop(device);
|
||||||
evlh.state().with_value(token, |_state, udev| {
|
evlh.state().with_value(token, |_state, udev| {
|
||||||
if let Err(err) = udev.session.close(fd) {
|
if let Err(err) = udev.session.close(fd) {
|
||||||
|
@ -375,7 +354,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handler for the `UdevBackend`, allows to open, close and update drm devices as they change during runtime.
|
/// Handler for the `UdevBackend`, allows to open, close and update drm devices as they change during runtime.
|
||||||
pub trait UdevHandler<B: Borrow<DrmBackend<SessionFdDrmDevice>> + 'static, H: DrmHandler<SessionFdDrmDevice, B> + 'static>
|
pub trait UdevHandler<H: DrmHandler<SessionFdDrmDevice> + 'static>
|
||||||
{
|
{
|
||||||
/// Called on initialization for every known device and when a new device is detected.
|
/// Called on initialization for every known device and when a new device is detected.
|
||||||
///
|
///
|
||||||
|
@ -383,7 +362,7 @@ pub trait UdevHandler<B: Borrow<DrmBackend<SessionFdDrmDevice>> + 'static, H: Dr
|
||||||
///
|
///
|
||||||
/// ## Panics
|
/// ## Panics
|
||||||
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
|
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
|
||||||
fn device_added<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &mut DrmDevice<SessionFdDrmDevice, B>)
|
fn device_added<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &mut DrmDevice<SessionFdDrmDevice>)
|
||||||
-> Option<H>;
|
-> Option<H>;
|
||||||
/// Called when an open device is changed.
|
/// Called when an open device is changed.
|
||||||
///
|
///
|
||||||
|
@ -392,7 +371,7 @@ pub trait UdevHandler<B: Borrow<DrmBackend<SessionFdDrmDevice>> + 'static, H: Dr
|
||||||
///
|
///
|
||||||
/// ## Panics
|
/// ## Panics
|
||||||
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
|
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
|
||||||
fn device_changed<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &StateToken<DrmDevice<SessionFdDrmDevice, B>>);
|
fn device_changed<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &StateToken<DrmDevice<SessionFdDrmDevice>>);
|
||||||
/// Called when a device was removed.
|
/// Called when a device was removed.
|
||||||
///
|
///
|
||||||
/// The device will not accept any operations anymore and its file descriptor will be closed once
|
/// The device will not accept any operations anymore and its file descriptor will be closed once
|
||||||
|
@ -400,7 +379,7 @@ pub trait UdevHandler<B: Borrow<DrmBackend<SessionFdDrmDevice>> + 'static, H: Dr
|
||||||
///
|
///
|
||||||
/// ## Panics
|
/// ## Panics
|
||||||
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
|
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
|
||||||
fn device_removed<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &StateToken<DrmDevice<SessionFdDrmDevice, B>>);
|
fn device_removed<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &StateToken<DrmDevice<SessionFdDrmDevice>>);
|
||||||
/// Called when the udev context has encountered and error.
|
/// Called when the udev context has encountered and error.
|
||||||
///
|
///
|
||||||
/// ## Panics
|
/// ## Panics
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
//! Implementation of backend traits for types provided by `winit`
|
//! Implementation of backend traits for types provided by `winit`
|
||||||
|
|
||||||
use backend::graphics::GraphicsBackend;
|
use backend::graphics::GraphicsBackend;
|
||||||
use backend::graphics::egl::{EGLGraphicsBackend, EGLContext, EGLSurface, BufferAccessError, PixelFormat, SwapBuffersError, EglExtensionNotSupportedError, EGLImages};
|
use backend::graphics::egl::{EGLGraphicsBackend, EGLContext, EGLSurface, PixelFormat, SwapBuffersError, EglExtensionNotSupportedError};
|
||||||
use backend::graphics::egl::error as egl_error;
|
use backend::graphics::egl::error as egl_error;
|
||||||
|
use backend::graphics::egl::error::Result as EGLResult;
|
||||||
use backend::graphics::egl::native;
|
use backend::graphics::egl::native;
|
||||||
use backend::graphics::egl::context::{GlAttributes, PixelFormatRequirements};
|
use backend::graphics::egl::context::GlAttributes;
|
||||||
|
use backend::graphics::egl::wayland::{EGLWaylandExtensions, EGLImages, Format, BufferAccessError, EGLDisplay};
|
||||||
use backend::input::{Axis, AxisSource, Event as BackendEvent, InputBackend, InputHandler, KeyState,
|
use backend::input::{Axis, AxisSource, Event as BackendEvent, InputBackend, InputHandler, KeyState,
|
||||||
KeyboardKeyEvent, MouseButton, MouseButtonState, PointerAxisEvent, PointerButtonEvent,
|
KeyboardKeyEvent, MouseButton, MouseButtonState, PointerAxisEvent, PointerButtonEvent,
|
||||||
PointerMotionAbsoluteEvent, Seat, SeatCapabilities, TouchCancelEvent, TouchDownEvent,
|
PointerMotionAbsoluteEvent, Seat, SeatCapabilities, TouchCancelEvent, TouchDownEvent,
|
||||||
|
@ -255,27 +257,20 @@ impl EGLGraphicsBackend for WinitGraphicsBackend {
|
||||||
Window::X11 { ref context, .. } => context.get_pixel_format(),
|
Window::X11 { ref context, .. } => context.get_pixel_format(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn bind_wl_display(&self, display: &Display) -> ::std::result::Result<(), EglExtensionNotSupportedError> {
|
impl EGLWaylandExtensions for WinitGraphicsBackend {
|
||||||
|
fn bind_wl_display(&self, display: &Display) -> EGLResult<EGLDisplay> {
|
||||||
match *self.window {
|
match *self.window {
|
||||||
Window::Wayland { ref context, .. } => context.bind_wl_display(display),
|
Window::Wayland { ref context, .. } => context.bind_wl_display(display),
|
||||||
Window::X11 { ref context, .. } => context.bind_wl_display(display),
|
Window::X11 { ref context, .. } => context.bind_wl_display(display),
|
||||||
}?;
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unbind_wl_display(&self, display: &Display) -> ::std::result::Result<(), EglExtensionNotSupportedError> {
|
fn unbind_wl_display(&self, display: &Display) -> EGLResult<()> {
|
||||||
match *self.window {
|
match *self.window {
|
||||||
Window::Wayland { ref context, .. } => context.unbind_wl_display(display),
|
Window::Wayland { ref context, .. } => context.unbind_wl_display(display),
|
||||||
Window::X11 { ref context, .. } => context.unbind_wl_display(display),
|
Window::X11 { ref context, .. } => context.unbind_wl_display(display),
|
||||||
}?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn egl_buffer_contents(&self, buffer: WlBuffer) -> ::std::result::Result<EGLImages, BufferAccessError> {
|
|
||||||
match *self.window {
|
|
||||||
Window::Wayland { ref context, .. } => context.egl_buffer_contents(buffer),
|
|
||||||
Window::X11 { ref context, .. } => context.egl_buffer_contents(buffer),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue