commit
3386304bd4
16
Cargo.toml
16
Cargo.toml
|
@ -13,12 +13,16 @@ slog = { version = "2.0.0" }
|
||||||
slog-stdlog = "2.0.0-0.2"
|
slog-stdlog = "2.0.0-0.2"
|
||||||
libloading = "0.4.0"
|
libloading = "0.4.0"
|
||||||
wayland-client = { version = "0.9.9", optional = true }
|
wayland-client = { version = "0.9.9", optional = true }
|
||||||
winit = { version = "0.7.0", optional = true }
|
winit = { version = "0.7.5", optional = true }
|
||||||
glium = { version = "0.16.0", optional = true, default-features = false }
|
drm = { version = "0.2.1", optional = true }
|
||||||
|
gbm = { version = "0.2.1", optional = true }
|
||||||
|
glium = { version = "0.17.1", optional = true, default-features = false }
|
||||||
input = { version = "0.2.0", optional = true }
|
input = { version = "0.2.0", optional = true }
|
||||||
clippy = { version = "*", optional = true }
|
clippy = { version = "*", optional = true }
|
||||||
rental = "0.4.11"
|
rental = "0.4.11"
|
||||||
wayland-protocols = { version = "0.10.1", features = ["unstable_protocols", "server"] }
|
wayland-protocols = { version = "0.10.1", features = ["unstable_protocols", "server"] }
|
||||||
|
image = "0.15.0"
|
||||||
|
error-chain = "0.11.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
gl_generator = "0.5"
|
gl_generator = "0.5"
|
||||||
|
@ -29,7 +33,11 @@ slog-async = "2.0"
|
||||||
rand = "0.3"
|
rand = "0.3"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["backend_winit", "backend_libinput", "renderer_glium"]
|
default = ["backend_winit", "backend_drm", "backend_libinput", "renderer_glium"]
|
||||||
backend_winit = ["winit", "wayland-server/dlopen", "wayland-client/dlopen"]
|
backend_winit = ["winit", "wayland-server/dlopen", "wayland-client/dlopen"]
|
||||||
renderer_glium = ["glium"]
|
backend_drm = ["drm", "gbm"]
|
||||||
backend_libinput = ["input"]
|
backend_libinput = ["input"]
|
||||||
|
renderer_glium = ["glium"]
|
||||||
|
|
||||||
|
[replace]
|
||||||
|
"drm:0.2.1" = { git = "https://github.com/Drakulix/drm-rs", branch = "future" }
|
||||||
|
|
1
build.rs
1
build.rs
|
@ -1,7 +1,6 @@
|
||||||
extern crate gl_generator;
|
extern crate gl_generator;
|
||||||
|
|
||||||
use gl_generator::{Api, Fallbacks, Profile, Registry};
|
use gl_generator::{Api, Fallbacks, Profile, Registry};
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
|
@ -0,0 +1,186 @@
|
||||||
|
extern crate drm;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate glium;
|
||||||
|
extern crate rand;
|
||||||
|
#[macro_use(define_roles)]
|
||||||
|
extern crate smithay;
|
||||||
|
extern crate wayland_server;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate slog;
|
||||||
|
extern crate slog_async;
|
||||||
|
extern crate slog_term;
|
||||||
|
|
||||||
|
mod helpers;
|
||||||
|
|
||||||
|
use drm::control::{Device as ControlDevice, ResourceInfo};
|
||||||
|
use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState};
|
||||||
|
use drm::control::encoder::Info as EncoderInfo;
|
||||||
|
use glium::Surface;
|
||||||
|
use helpers::{shell_implementation, surface_implementation, GliumDrawer, Roles, SurfaceData};
|
||||||
|
use slog::{Drain, Logger};
|
||||||
|
use smithay::backend::drm::{drm_device_bind, DrmBackend, DrmDevice, DrmHandler};
|
||||||
|
use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
||||||
|
use smithay::compositor::{compositor_init, CompositorToken, SubsurfaceRole, TraversalAction};
|
||||||
|
use smithay::compositor::roles::Role;
|
||||||
|
use smithay::shell::{shell_init, ShellState};
|
||||||
|
use smithay::shm::init_shm_global;
|
||||||
|
use std::fs::OpenOptions;
|
||||||
|
use std::io::Error as IoError;
|
||||||
|
use std::time::Duration;
|
||||||
|
use wayland_server::{EventLoopHandle, StateToken};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// A logger facility, here we use the terminal for this example
|
||||||
|
let log = Logger::root(
|
||||||
|
slog_async::Async::default(slog_term::term_full().fuse()).fuse(),
|
||||||
|
o!(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Initialize the wayland server
|
||||||
|
let (mut display, mut event_loop) = wayland_server::create_display();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize the drm backend
|
||||||
|
*/
|
||||||
|
// "Find" a suitable drm device
|
||||||
|
let mut options = OpenOptions::new();
|
||||||
|
options.read(true);
|
||||||
|
options.write(true);
|
||||||
|
let mut device: DrmDevice<GliumDrawer<DrmBackend>> =
|
||||||
|
DrmDevice::new_from_file(options.clone().open("/dev/dri/card0").unwrap(), log.clone()).unwrap();
|
||||||
|
|
||||||
|
// Get a set of all modesetting resource handles (excluding planes):
|
||||||
|
let res_handles = device.resource_handles().unwrap();
|
||||||
|
|
||||||
|
// Use first connected connector
|
||||||
|
let connector_info = res_handles
|
||||||
|
.connectors()
|
||||||
|
.iter()
|
||||||
|
.map(|conn| {
|
||||||
|
ConnectorInfo::load_from_device(&device, *conn).unwrap()
|
||||||
|
})
|
||||||
|
.find(|conn| conn.connection_state() == ConnectorState::Connected)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Use the first encoder
|
||||||
|
let encoder_info = EncoderInfo::load_from_device(&device, connector_info.encoders()[0]).unwrap();
|
||||||
|
|
||||||
|
// use the connected crtc if any
|
||||||
|
let crtc = encoder_info.current_crtc()
|
||||||
|
// or use the first one that is compatible with the encoder
|
||||||
|
.unwrap_or_else(||
|
||||||
|
*res_handles.crtcs()
|
||||||
|
.iter()
|
||||||
|
.find(|crtc| encoder_info.supports_crtc(**crtc))
|
||||||
|
.unwrap());
|
||||||
|
|
||||||
|
// 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.)
|
||||||
|
|
||||||
|
// Initialize the hardware backend
|
||||||
|
let renderer_token = device
|
||||||
|
.create_backend(&mut event_loop, crtc, mode, vec![connector_info.handle()])
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize the globals
|
||||||
|
*/
|
||||||
|
|
||||||
|
init_shm_global(&mut event_loop, vec![], log.clone());
|
||||||
|
|
||||||
|
let (compositor_token, _, _) =
|
||||||
|
compositor_init(&mut event_loop, surface_implementation(), (), log.clone());
|
||||||
|
|
||||||
|
let (shell_state_token, _, _) = shell_init(
|
||||||
|
&mut event_loop,
|
||||||
|
compositor_token,
|
||||||
|
shell_implementation(),
|
||||||
|
compositor_token,
|
||||||
|
log.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize glium
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
let drawer = event_loop.state().get(&renderer_token);
|
||||||
|
let mut frame = drawer.draw();
|
||||||
|
frame.clear_color(0.8, 0.8, 0.9, 1.0);
|
||||||
|
frame.finish().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add a listening socket:
|
||||||
|
*/
|
||||||
|
let name = display.add_socket_auto().unwrap().into_string().unwrap();
|
||||||
|
println!("Listening on socket: {}", name);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Register the DrmDevice on the EventLoop
|
||||||
|
*/
|
||||||
|
let _source = drm_device_bind(
|
||||||
|
&mut event_loop,
|
||||||
|
device,
|
||||||
|
DrmHandlerImpl {
|
||||||
|
shell_state_token,
|
||||||
|
compositor_token,
|
||||||
|
logger: log,
|
||||||
|
},
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
event_loop.run().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DrmHandlerImpl {
|
||||||
|
shell_state_token: StateToken<ShellState<SurfaceData, Roles, (), ()>>,
|
||||||
|
compositor_token: CompositorToken<SurfaceData, Roles, ()>,
|
||||||
|
logger: ::slog::Logger,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DrmHandler<GliumDrawer<DrmBackend>> for DrmHandlerImpl {
|
||||||
|
fn ready(&mut self, evlh: &mut EventLoopHandle, _device: &mut DrmDevice<GliumDrawer<DrmBackend>>,
|
||||||
|
backend: &StateToken<GliumDrawer<DrmBackend>>, _frame: u32, _duration: Duration) {
|
||||||
|
let state = evlh.state();
|
||||||
|
let drawer = state.get(backend);
|
||||||
|
let mut frame = drawer.draw();
|
||||||
|
frame.clear_color(0.8, 0.8, 0.9, 1.0);
|
||||||
|
// redraw the frame, in a simple but inneficient way
|
||||||
|
{
|
||||||
|
let screen_dimensions = drawer.get_framebuffer_dimensions();
|
||||||
|
for toplevel_surface in state.get(&self.shell_state_token).toplevel_surfaces() {
|
||||||
|
if let Some(wl_surface) = toplevel_surface.get_surface() {
|
||||||
|
// this surface is a root of a subsurface tree that needs to be drawn
|
||||||
|
let initial_place = self.compositor_token
|
||||||
|
.with_surface_data(wl_surface, |data| data.user_data.location.unwrap_or((0, 0)));
|
||||||
|
self.compositor_token
|
||||||
|
.with_surface_tree(
|
||||||
|
wl_surface,
|
||||||
|
initial_place,
|
||||||
|
|_surface, attributes, role, &(mut x, mut y)| {
|
||||||
|
if let Some((ref contents, (w, h))) = attributes.user_data.buffer {
|
||||||
|
// there is actually something to draw !
|
||||||
|
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
|
||||||
|
x += subdata.x;
|
||||||
|
y += subdata.y;
|
||||||
|
}
|
||||||
|
drawer.render(&mut frame, contents, (w, h), (x, y), screen_dimensions);
|
||||||
|
TraversalAction::DoChildren((x, y))
|
||||||
|
} else {
|
||||||
|
// we are not display, so our children are neither
|
||||||
|
TraversalAction::SkipChildren
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
frame.finish().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn error(&mut self, _evlh: &mut EventLoopHandle, _device: &mut DrmDevice<GliumDrawer<DrmBackend>>,
|
||||||
|
error: IoError) {
|
||||||
|
panic!("{:?}", error);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,9 @@
|
||||||
use glium;
|
use glium;
|
||||||
use glium::Surface;
|
use glium::{Frame, Surface};
|
||||||
use glium::index::PrimitiveType;
|
use glium::index::PrimitiveType;
|
||||||
|
use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
||||||
|
use smithay::backend::graphics::glium::GliumGraphicsBackend;
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
struct Vertex {
|
struct Vertex {
|
||||||
|
@ -10,18 +13,28 @@ struct Vertex {
|
||||||
|
|
||||||
implement_vertex!(Vertex, position, tex_coords);
|
implement_vertex!(Vertex, position, tex_coords);
|
||||||
|
|
||||||
pub struct GliumDrawer<'a, F: 'a> {
|
pub struct GliumDrawer<F: EGLGraphicsBackend + 'static> {
|
||||||
display: &'a F,
|
display: GliumGraphicsBackend<F>,
|
||||||
vertex_buffer: glium::VertexBuffer<Vertex>,
|
vertex_buffer: glium::VertexBuffer<Vertex>,
|
||||||
index_buffer: glium::IndexBuffer<u16>,
|
index_buffer: glium::IndexBuffer<u16>,
|
||||||
program: glium::Program,
|
program: glium::Program,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, F: glium::backend::Facade + 'a> GliumDrawer<'a, F> {
|
impl<F: EGLGraphicsBackend + 'static> Deref for GliumDrawer<F> {
|
||||||
pub fn new(display: &'a F) -> GliumDrawer<'a, F> {
|
type Target = F;
|
||||||
|
|
||||||
|
fn deref(&self) -> &F {
|
||||||
|
&*self.display
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Into<GliumGraphicsBackend<T>> + EGLGraphicsBackend + 'static> From<T> for GliumDrawer<T> {
|
||||||
|
fn from(backend: T) -> GliumDrawer<T> {
|
||||||
|
let display = backend.into();
|
||||||
|
|
||||||
// building the vertex buffer, which contains all the vertices that we will draw
|
// building the vertex buffer, which contains all the vertices that we will draw
|
||||||
let vertex_buffer = glium::VertexBuffer::new(
|
let vertex_buffer = glium::VertexBuffer::new(
|
||||||
display,
|
&display,
|
||||||
&[
|
&[
|
||||||
Vertex {
|
Vertex {
|
||||||
position: [0.0, 0.0],
|
position: [0.0, 0.0],
|
||||||
|
@ -44,10 +57,10 @@ impl<'a, F: glium::backend::Facade + 'a> GliumDrawer<'a, F> {
|
||||||
|
|
||||||
// building the index buffer
|
// building the index buffer
|
||||||
let index_buffer =
|
let index_buffer =
|
||||||
glium::IndexBuffer::new(display, PrimitiveType::TriangleStrip, &[1 as u16, 2, 0, 3]).unwrap();
|
glium::IndexBuffer::new(&display, PrimitiveType::TriangleStrip, &[1 as u16, 2, 0, 3]).unwrap();
|
||||||
|
|
||||||
// compiling shaders and linking them together
|
// compiling shaders and linking them together
|
||||||
let program = program!(display,
|
let program = program!(&display,
|
||||||
100 => {
|
100 => {
|
||||||
vertex: "
|
vertex: "
|
||||||
#version 100
|
#version 100
|
||||||
|
@ -83,8 +96,10 @@ impl<'a, F: glium::backend::Facade + 'a> GliumDrawer<'a, F> {
|
||||||
program,
|
program,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn draw(&self, target: &mut glium::Frame, contents: &[u8], surface_dimensions: (u32, u32),
|
impl<F: EGLGraphicsBackend + 'static> GliumDrawer<F> {
|
||||||
|
pub fn render(&self, target: &mut glium::Frame, contents: &[u8], surface_dimensions: (u32, u32),
|
||||||
surface_location: (i32, i32), screen_size: (u32, u32)) {
|
surface_location: (i32, i32), screen_size: (u32, u32)) {
|
||||||
let image = glium::texture::RawImage2d {
|
let image = glium::texture::RawImage2d {
|
||||||
data: contents.into(),
|
data: contents.into(),
|
||||||
|
@ -92,7 +107,7 @@ impl<'a, F: glium::backend::Facade + 'a> GliumDrawer<'a, F> {
|
||||||
height: surface_dimensions.1,
|
height: surface_dimensions.1,
|
||||||
format: glium::texture::ClientFormat::U8U8U8U8,
|
format: glium::texture::ClientFormat::U8U8U8U8,
|
||||||
};
|
};
|
||||||
let opengl_texture = glium::texture::Texture2d::new(self.display, image).unwrap();
|
let opengl_texture = glium::texture::Texture2d::new(&self.display, image).unwrap();
|
||||||
|
|
||||||
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 yscale = -2.0 * (surface_dimensions.1 as f32) / (screen_size.1 as f32);
|
let yscale = -2.0 * (surface_dimensions.1 as f32) / (screen_size.1 as f32);
|
||||||
|
@ -120,4 +135,9 @@ impl<'a, F: glium::backend::Facade + 'a> GliumDrawer<'a, F> {
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn draw(&self) -> Frame {
|
||||||
|
self.display.draw()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ extern crate slog_async;
|
||||||
extern crate slog_term;
|
extern crate slog_term;
|
||||||
#[macro_use(define_roles)]
|
#[macro_use(define_roles)]
|
||||||
extern crate smithay;
|
extern crate smithay;
|
||||||
extern crate wayland_protocols;
|
|
||||||
extern crate wayland_server;
|
extern crate wayland_server;
|
||||||
|
|
||||||
mod helpers;
|
mod helpers;
|
||||||
|
@ -15,7 +14,7 @@ mod helpers;
|
||||||
use glium::Surface;
|
use glium::Surface;
|
||||||
use helpers::{shell_implementation, surface_implementation, GliumDrawer};
|
use helpers::{shell_implementation, surface_implementation, GliumDrawer};
|
||||||
use slog::{Drain, Logger};
|
use slog::{Drain, Logger};
|
||||||
use smithay::backend::graphics::glium::IntoGlium;
|
use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
||||||
use smithay::backend::input::InputBackend;
|
use smithay::backend::input::InputBackend;
|
||||||
use smithay::backend::winit;
|
use smithay::backend::winit;
|
||||||
use smithay::compositor::{compositor_init, SubsurfaceRole, TraversalAction};
|
use smithay::compositor::{compositor_init, SubsurfaceRole, TraversalAction};
|
||||||
|
@ -55,9 +54,7 @@ fn main() {
|
||||||
/*
|
/*
|
||||||
* Initialize glium
|
* Initialize glium
|
||||||
*/
|
*/
|
||||||
let context = renderer.into_glium();
|
let drawer = GliumDrawer::from(renderer);
|
||||||
|
|
||||||
let drawer = GliumDrawer::new(&context);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add a listening socket:
|
* Add a listening socket:
|
||||||
|
@ -68,11 +65,11 @@ fn main() {
|
||||||
loop {
|
loop {
|
||||||
input.dispatch_new_events().unwrap();
|
input.dispatch_new_events().unwrap();
|
||||||
|
|
||||||
let mut frame = context.draw();
|
let mut frame = drawer.draw();
|
||||||
frame.clear(None, Some((0.8, 0.8, 0.9, 1.0)), false, None, None);
|
frame.clear(None, Some((0.8, 0.8, 0.9, 1.0)), false, None, None);
|
||||||
// redraw the frame, in a simple but inneficient way
|
// redraw the frame, in a simple but inneficient way
|
||||||
{
|
{
|
||||||
let screen_dimensions = context.get_framebuffer_dimensions();
|
let screen_dimensions = drawer.get_framebuffer_dimensions();
|
||||||
let state = event_loop.state();
|
let state = event_loop.state();
|
||||||
for toplevel_surface in state.get(&shell_state_token).toplevel_surfaces() {
|
for toplevel_surface in state.get(&shell_state_token).toplevel_surfaces() {
|
||||||
if let Some(wl_surface) = toplevel_surface.get_surface() {
|
if let Some(wl_surface) = toplevel_surface.get_surface() {
|
||||||
|
@ -90,7 +87,7 @@ fn main() {
|
||||||
x += subdata.x;
|
x += subdata.x;
|
||||||
y += subdata.y;
|
y += subdata.y;
|
||||||
}
|
}
|
||||||
drawer.draw(&mut frame, contents, (w, h), (x, y), screen_dimensions);
|
drawer.render(&mut frame, contents, (w, h), (x, y), screen_dimensions);
|
||||||
TraversalAction::DoChildren((x, y))
|
TraversalAction::DoChildren((x, y))
|
||||||
} else {
|
} else {
|
||||||
// we are not display, so our children are neither
|
// we are not display, so our children are neither
|
|
@ -0,0 +1,535 @@
|
||||||
|
use super::devices;
|
||||||
|
use super::error::*;
|
||||||
|
use backend::graphics::GraphicsBackend;
|
||||||
|
use backend::graphics::egl::{EGLGraphicsBackend, EGLSurface, PixelFormat, SwapBuffersError};
|
||||||
|
use drm::buffer::Buffer;
|
||||||
|
use drm::control::{connector, crtc, encoder, framebuffer, Mode};
|
||||||
|
use drm::control::ResourceInfo;
|
||||||
|
use gbm::{BufferObject, BufferObjectFlags, Format as GbmFormat, Surface as GbmSurface, SurfaceBufferHandle};
|
||||||
|
use image::{ImageBuffer, Rgba};
|
||||||
|
use nix::c_void;
|
||||||
|
use std::cell::Cell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Dependency graph
|
||||||
|
- drm
|
||||||
|
- gbm
|
||||||
|
- context
|
||||||
|
- gbm_surface
|
||||||
|
- egl_surface
|
||||||
|
- gbm_buffers
|
||||||
|
- cursor
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub(crate) struct GbmTypes<'dev, 'context> {
|
||||||
|
cursor: Cell<BufferObject<'dev, ()>>,
|
||||||
|
surface: Surface<'context>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct EGL<'gbm, 'context> {
|
||||||
|
surface: EGLSurface<'context, 'gbm, GbmSurface<'context, framebuffer::Info>>,
|
||||||
|
buffers: GbmBuffers<'gbm>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct GbmBuffers<'gbm> {
|
||||||
|
front_buffer: Cell<SurfaceBufferHandle<'gbm, framebuffer::Info>>,
|
||||||
|
next_buffer: Cell<Option<SurfaceBufferHandle<'gbm, framebuffer::Info>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
rental! {
|
||||||
|
mod graphics {
|
||||||
|
use drm::control::framebuffer;
|
||||||
|
use gbm::Surface as GbmSurface;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use super::devices::{Context, Context_Borrow};
|
||||||
|
use super::GbmTypes;
|
||||||
|
|
||||||
|
#[rental]
|
||||||
|
pub(crate) struct Surface<'context> {
|
||||||
|
gbm: Box<GbmSurface<'context, framebuffer::Info>>,
|
||||||
|
egl: super::EGL<'gbm, 'context>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rental]
|
||||||
|
pub(crate) struct Graphics {
|
||||||
|
#[subrental(arity = 3)]
|
||||||
|
context: Rc<Context>,
|
||||||
|
gbm: GbmTypes<'context_1, 'context_2>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
use self::graphics::{Graphics, Surface};
|
||||||
|
|
||||||
|
|
||||||
|
/// Backend based on a `DrmDevice` and a given crtc
|
||||||
|
pub struct DrmBackend {
|
||||||
|
graphics: Graphics,
|
||||||
|
crtc: crtc::Handle,
|
||||||
|
mode: Mode,
|
||||||
|
connectors: Vec<connector::Handle>,
|
||||||
|
logger: ::slog::Logger,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DrmBackend {
|
||||||
|
pub(crate) fn new(context: Rc<devices::Context>, crtc: crtc::Handle, mode: Mode,
|
||||||
|
connectors: Vec<connector::Handle>, logger: ::slog::Logger)
|
||||||
|
-> Result<DrmBackend> {
|
||||||
|
// logger already initialized by the DrmDevice
|
||||||
|
let log = ::slog_or_stdlog(logger);
|
||||||
|
info!(log, "Initializing DrmBackend");
|
||||||
|
|
||||||
|
let (w, h) = mode.size();
|
||||||
|
|
||||||
|
Ok(DrmBackend {
|
||||||
|
graphics: Graphics::try_new(context, |context| {
|
||||||
|
Ok(GbmTypes {
|
||||||
|
cursor: {
|
||||||
|
// Create an unused cursor buffer (we don't want an Option here)
|
||||||
|
Cell::new(context
|
||||||
|
.devices
|
||||||
|
.gbm
|
||||||
|
.create_buffer_object(
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
GbmFormat::ARGB8888,
|
||||||
|
&[BufferObjectFlags::Cursor, BufferObjectFlags::Write],
|
||||||
|
)
|
||||||
|
.chain_err(|| ErrorKind::GbmInitFailed)?)
|
||||||
|
},
|
||||||
|
surface: Surface::try_new(
|
||||||
|
{
|
||||||
|
debug!(log, "Creating GbmSurface");
|
||||||
|
// create a gbm surface
|
||||||
|
Box::new(context
|
||||||
|
.devices
|
||||||
|
.gbm
|
||||||
|
.create_surface(
|
||||||
|
w as u32,
|
||||||
|
h as u32,
|
||||||
|
GbmFormat::XRGB8888,
|
||||||
|
&[BufferObjectFlags::Scanout, BufferObjectFlags::Rendering],
|
||||||
|
)
|
||||||
|
.chain_err(|| ErrorKind::GbmInitFailed)?)
|
||||||
|
},
|
||||||
|
|surface| {
|
||||||
|
// create an egl surface from the gbm one
|
||||||
|
debug!(log, "Creating EGLSurface");
|
||||||
|
let egl_surface = context.egl.create_surface(&surface)?;
|
||||||
|
|
||||||
|
// make it active for the first `crtc::set`
|
||||||
|
// (which is needed before the first page_flip)
|
||||||
|
unsafe {
|
||||||
|
egl_surface
|
||||||
|
.make_current()
|
||||||
|
.chain_err(|| ErrorKind::FailedToSwap)?
|
||||||
|
};
|
||||||
|
egl_surface
|
||||||
|
.swap_buffers()
|
||||||
|
.chain_err(|| ErrorKind::FailedToSwap)?;
|
||||||
|
|
||||||
|
// init the first screen
|
||||||
|
// (must be done before calling page_flip for the first time)
|
||||||
|
let mut front_bo = surface
|
||||||
|
.lock_front_buffer()
|
||||||
|
.chain_err(|| ErrorKind::FailedToSwap)?;
|
||||||
|
debug!(log, "FrontBuffer color format: {:?}", front_bo.format());
|
||||||
|
// we need a framebuffer per front buffer
|
||||||
|
let fb = framebuffer::create(context.devices.drm, &*front_bo)
|
||||||
|
.chain_err(|| ErrorKind::DrmDev(format!("{:?}", context.devices.drm)))?;
|
||||||
|
|
||||||
|
debug!(log, "Initialize screen");
|
||||||
|
crtc::set(
|
||||||
|
context.devices.drm,
|
||||||
|
crtc,
|
||||||
|
fb.handle(),
|
||||||
|
&connectors,
|
||||||
|
(0, 0),
|
||||||
|
Some(mode),
|
||||||
|
).chain_err(
|
||||||
|
|| ErrorKind::DrmDev(format!("{:?}", context.devices.drm)),
|
||||||
|
)?;
|
||||||
|
front_bo.set_userdata(fb);
|
||||||
|
|
||||||
|
Ok(EGL {
|
||||||
|
surface: egl_surface,
|
||||||
|
buffers: GbmBuffers {
|
||||||
|
front_buffer: Cell::new(front_bo),
|
||||||
|
next_buffer: Cell::new(None),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
).map_err(Error::from)?,
|
||||||
|
})
|
||||||
|
})?,
|
||||||
|
crtc,
|
||||||
|
mode,
|
||||||
|
connectors,
|
||||||
|
logger: log.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
self.graphics.rent(|gbm| {
|
||||||
|
gbm.surface.rent(|egl| {
|
||||||
|
let next_bo = egl.buffers.next_buffer.replace(None);
|
||||||
|
|
||||||
|
if let Some(next_buffer) = next_bo {
|
||||||
|
trace!(self.logger, "Releasing all front buffer");
|
||||||
|
egl.buffers.front_buffer.set(next_buffer);
|
||||||
|
// drop and release the old buffer
|
||||||
|
} else {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a connector to backend
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Errors if the new connector does not support the currently set `Mode`
|
||||||
|
pub fn add_connector(&mut self, connector: connector::Handle) -> Result<()> {
|
||||||
|
let info = connector::Info::load_from_device(self.graphics.head().head().head(), connector)
|
||||||
|
.chain_err(|| {
|
||||||
|
ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head()))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// check if the connector can handle the current mode
|
||||||
|
if info.modes().contains(&self.mode) {
|
||||||
|
// check if there is a valid encoder
|
||||||
|
let encoders = info.encoders()
|
||||||
|
.iter()
|
||||||
|
.map(|encoder| {
|
||||||
|
encoder::Info::load_from_device(self.graphics.head().head().head(), *encoder).chain_err(
|
||||||
|
|| ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head())),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<encoder::Info>>>()?;
|
||||||
|
|
||||||
|
// and if any encoder supports the selected crtc
|
||||||
|
if !encoders
|
||||||
|
.iter()
|
||||||
|
.any(|encoder| encoder.supports_crtc(self.crtc))
|
||||||
|
{
|
||||||
|
bail!(ErrorKind::NoSuitableEncoder(info, self.crtc));
|
||||||
|
}
|
||||||
|
|
||||||
|
info!(
|
||||||
|
self.logger,
|
||||||
|
"Adding new connector: {:?}",
|
||||||
|
info.connector_type()
|
||||||
|
);
|
||||||
|
self.connectors.push(connector);
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
bail!(ErrorKind::ModeNotSuitable(self.mode))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the currently set connectors
|
||||||
|
pub fn used_connectors(&self) -> &[connector::Handle] {
|
||||||
|
&*self.connectors
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes a currently set connector
|
||||||
|
pub fn remove_connector(&mut self, connector: connector::Handle) {
|
||||||
|
if let Ok(info) = connector::Info::load_from_device(self.graphics.head().head().head(), connector) {
|
||||||
|
info!(
|
||||||
|
self.logger,
|
||||||
|
"Removing connector: {:?}",
|
||||||
|
info.connector_type()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
info!(self.logger, "Removing unknown connector");
|
||||||
|
}
|
||||||
|
|
||||||
|
self.connectors.retain(|x| *x != connector);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Changes the currently set mode
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// This will fail if not all set connectors support the new `Mode`.
|
||||||
|
/// Several internal resources will need to be recreated to fit the new `Mode`.
|
||||||
|
/// Other errors might occur.
|
||||||
|
pub fn use_mode(&mut self, mode: Mode) -> Result<()> {
|
||||||
|
// check the connectors
|
||||||
|
for connector in self.connectors.iter() {
|
||||||
|
if !connector::Info::load_from_device(self.graphics.head().head().head(), *connector)
|
||||||
|
.chain_err(|| {
|
||||||
|
ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head()))
|
||||||
|
})?
|
||||||
|
.modes()
|
||||||
|
.contains(&mode)
|
||||||
|
{
|
||||||
|
bail!(ErrorKind::ModeNotSuitable(mode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// borrow & clone stuff because rust cannot figure out the upcoming
|
||||||
|
// closure otherwise.
|
||||||
|
let crtc = self.crtc;
|
||||||
|
let connectors_ref = &self.connectors;
|
||||||
|
let logger_ref = &self.logger;
|
||||||
|
|
||||||
|
let (w, h) = mode.size();
|
||||||
|
|
||||||
|
self.graphics.rent_all_mut(|graphics| -> Result<()> {
|
||||||
|
// Recreate the surface and the related resources to match the new
|
||||||
|
// resolution.
|
||||||
|
debug!(
|
||||||
|
logger_ref,
|
||||||
|
"Reinitializing surface for new mode: {}:{}",
|
||||||
|
w,
|
||||||
|
h
|
||||||
|
);
|
||||||
|
graphics.gbm.surface = Surface::try_new(
|
||||||
|
{
|
||||||
|
// create a new gbm surface
|
||||||
|
debug!(logger_ref, "Creating GbmSurface");
|
||||||
|
Box::new(graphics
|
||||||
|
.context
|
||||||
|
.devices
|
||||||
|
.gbm
|
||||||
|
.create_surface(
|
||||||
|
w as u32,
|
||||||
|
h as u32,
|
||||||
|
GbmFormat::XRGB8888,
|
||||||
|
&[BufferObjectFlags::Scanout, BufferObjectFlags::Rendering],
|
||||||
|
)
|
||||||
|
.chain_err(|| ErrorKind::GbmInitFailed)?)
|
||||||
|
},
|
||||||
|
|surface| {
|
||||||
|
// create an egl surface from the gbm one
|
||||||
|
debug!(logger_ref, "Creating EGLSurface");
|
||||||
|
let egl_surface = graphics.context.egl.create_surface(&surface)?;
|
||||||
|
|
||||||
|
// make it active for the first `crtc::set`
|
||||||
|
// (which is needed before the first page_flip)
|
||||||
|
unsafe {
|
||||||
|
egl_surface
|
||||||
|
.make_current()
|
||||||
|
.chain_err(|| ErrorKind::FailedToSwap)?
|
||||||
|
};
|
||||||
|
egl_surface
|
||||||
|
.swap_buffers()
|
||||||
|
.chain_err(|| ErrorKind::FailedToSwap)?;
|
||||||
|
|
||||||
|
let mut front_bo = surface
|
||||||
|
.lock_front_buffer()
|
||||||
|
.chain_err(|| ErrorKind::FailedToSwap)?;
|
||||||
|
debug!(
|
||||||
|
logger_ref,
|
||||||
|
"FrontBuffer color format: {:?}",
|
||||||
|
front_bo.format()
|
||||||
|
);
|
||||||
|
// we need a framebuffer per front_buffer
|
||||||
|
let fb = framebuffer::create(graphics.context.devices.drm, &*front_bo).chain_err(|| {
|
||||||
|
ErrorKind::DrmDev(format!("{:?}", graphics.context.devices.drm))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
debug!(logger_ref, "Initialize screen");
|
||||||
|
crtc::set(
|
||||||
|
graphics.context.devices.drm,
|
||||||
|
crtc,
|
||||||
|
fb.handle(),
|
||||||
|
connectors_ref,
|
||||||
|
(0, 0),
|
||||||
|
Some(mode),
|
||||||
|
).chain_err(|| {
|
||||||
|
ErrorKind::DrmDev(format!("{:?}", graphics.context.devices.drm))
|
||||||
|
})?;
|
||||||
|
front_bo.set_userdata(fb);
|
||||||
|
|
||||||
|
Ok(EGL {
|
||||||
|
surface: egl_surface,
|
||||||
|
buffers: GbmBuffers {
|
||||||
|
front_buffer: Cell::new(front_bo),
|
||||||
|
next_buffer: Cell::new(None),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
info!(self.logger, "Setting new mode: {:?}", mode.name());
|
||||||
|
self.mode = mode;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for DrmBackend {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// Drop framebuffers attached to the userdata of the gbm surface buffers.
|
||||||
|
// (They don't implement drop, as they need the device)
|
||||||
|
self.graphics.rent_all_mut(|graphics| {
|
||||||
|
if let Some(fb) = graphics.gbm.surface.rent(|egl| {
|
||||||
|
if let Some(mut next) = egl.buffers.next_buffer.take() {
|
||||||
|
return next.take_userdata();
|
||||||
|
} else {
|
||||||
|
if let Ok(mut next) = graphics.gbm.surface.head().lock_front_buffer() {
|
||||||
|
return next.take_userdata();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}) {
|
||||||
|
// ignore failure at this point
|
||||||
|
let _ = framebuffer::destroy(&*graphics.context.devices.drm, fb.handle());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(fb) = graphics.gbm.surface.rent_mut(|egl| {
|
||||||
|
let first = egl.buffers.front_buffer.get_mut();
|
||||||
|
first.take_userdata()
|
||||||
|
}) {
|
||||||
|
// ignore failure at this point
|
||||||
|
let _ = framebuffer::destroy(&*graphics.context.devices.drm, fb.handle());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GraphicsBackend for DrmBackend {
|
||||||
|
type CursorFormat = ImageBuffer<Rgba<u8>, Vec<u8>>;
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn set_cursor_position(&self, x: u32, y: u32) -> Result<()> {
|
||||||
|
trace!(self.logger, "Move the cursor to {},{}", x, y);
|
||||||
|
crtc::move_cursor(
|
||||||
|
self.graphics.head().head().head(),
|
||||||
|
self.crtc,
|
||||||
|
(x as i32, y as i32),
|
||||||
|
).chain_err(|| {
|
||||||
|
ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_cursor_representation(&self, buffer: ImageBuffer<Rgba<u8>, Vec<u8>>, hotspot: (u32, u32))
|
||||||
|
-> Result<()> {
|
||||||
|
let (w, h) = buffer.dimensions();
|
||||||
|
|
||||||
|
debug!(self.logger, "Importing cursor");
|
||||||
|
|
||||||
|
self.graphics.rent_all(|graphics| -> Result<()> {
|
||||||
|
graphics.gbm.cursor.set({
|
||||||
|
// import the cursor into a buffer we can render
|
||||||
|
let mut cursor = graphics
|
||||||
|
.context
|
||||||
|
.devices
|
||||||
|
.gbm
|
||||||
|
.create_buffer_object(
|
||||||
|
w,
|
||||||
|
h,
|
||||||
|
GbmFormat::ARGB8888,
|
||||||
|
&[BufferObjectFlags::Cursor, BufferObjectFlags::Write],
|
||||||
|
)
|
||||||
|
.chain_err(|| ErrorKind::GbmInitFailed)?;
|
||||||
|
cursor
|
||||||
|
.write(&*buffer.into_raw())
|
||||||
|
.chain_err(|| ErrorKind::GbmInitFailed)?;
|
||||||
|
|
||||||
|
trace!(self.logger, "Set the new imported cursor");
|
||||||
|
|
||||||
|
// and set it
|
||||||
|
if crtc::set_cursor2(
|
||||||
|
self.graphics.head().head().head(),
|
||||||
|
self.crtc,
|
||||||
|
Buffer::handle(&cursor),
|
||||||
|
(w, h),
|
||||||
|
(hotspot.0 as i32, hotspot.1 as i32),
|
||||||
|
).is_err()
|
||||||
|
{
|
||||||
|
crtc::set_cursor(
|
||||||
|
self.graphics.head().head().head(),
|
||||||
|
self.crtc,
|
||||||
|
Buffer::handle(&cursor),
|
||||||
|
(w, h),
|
||||||
|
).chain_err(|| {
|
||||||
|
ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head()))
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// and store it
|
||||||
|
cursor
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EGLGraphicsBackend for DrmBackend {
|
||||||
|
fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
|
||||||
|
self.graphics.rent_all(|graphics| {
|
||||||
|
// We cannot call lock_front_buffer anymore without releasing the previous buffer, which will happen when the page flip is done
|
||||||
|
if graphics.gbm.surface.rent(|egl| {
|
||||||
|
let next = egl.buffers.next_buffer.take();
|
||||||
|
let res = next.is_some();
|
||||||
|
egl.buffers.next_buffer.set(next);
|
||||||
|
res
|
||||||
|
}) {
|
||||||
|
warn!(self.logger, "Tried to swap a DrmBackend with a queued flip");
|
||||||
|
return Err(SwapBuffersError::AlreadySwapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
// flip normally
|
||||||
|
graphics.gbm.surface.rent(|egl| egl.surface.swap_buffers())?;
|
||||||
|
|
||||||
|
graphics.gbm.surface.rent_all(|surface| {
|
||||||
|
// supporting this error would cause a lot of inconvinience and
|
||||||
|
// would most likely result in a lot of flickering.
|
||||||
|
// neither weston, wlc or wlroots bother with that as well.
|
||||||
|
// so we just assume we got at least two buffers to do flipping
|
||||||
|
let mut next_bo = surface.gbm.lock_front_buffer().expect("Surface only has one front buffer. Not supported by smithay");
|
||||||
|
|
||||||
|
// create a framebuffer if the front buffer does not have one already
|
||||||
|
// (they are reused by gbm)
|
||||||
|
let maybe_fb = next_bo.userdata().cloned();
|
||||||
|
let fb = if let Some(info) = maybe_fb {
|
||||||
|
info
|
||||||
|
} else {
|
||||||
|
let fb = framebuffer::create(graphics.context.devices.drm, &*next_bo).map_err(|_| SwapBuffersError::ContextLost)?;
|
||||||
|
next_bo.set_userdata(fb);
|
||||||
|
fb
|
||||||
|
};
|
||||||
|
surface.egl.buffers.next_buffer.set(Some(next_bo));
|
||||||
|
|
||||||
|
trace!(self.logger, "Queueing Page flip");
|
||||||
|
|
||||||
|
// and flip
|
||||||
|
crtc::page_flip(graphics.context.devices.drm, self.crtc, fb.handle(), &[crtc::PageFlipFlags::PageFlipEvent], self.crtc).map_err(|_| SwapBuffersError::ContextLost)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void {
|
||||||
|
self.graphics
|
||||||
|
.head()
|
||||||
|
.rent(|context| context.get_proc_address(symbol))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_framebuffer_dimensions(&self) -> (u32, u32) {
|
||||||
|
let (w, h) = self.mode.size();
|
||||||
|
(w as u32, h as u32)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_current(&self) -> bool {
|
||||||
|
self.graphics.head().rent(|context| context.is_current())
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
|
||||||
|
self.graphics
|
||||||
|
.rent(|gbm| gbm.surface.rent(|egl| egl.surface.make_current()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_pixel_format(&self) -> PixelFormat {
|
||||||
|
self.graphics
|
||||||
|
.head()
|
||||||
|
.rent(|context| context.get_pixel_format())
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
//!
|
||||||
|
//! Errors thrown by the `DrmDevice` and `DrmBackend`
|
||||||
|
//!
|
||||||
|
|
||||||
|
use backend::graphics::egl;
|
||||||
|
use drm::control::{connector, crtc, Mode};
|
||||||
|
use rental::TryNewError;
|
||||||
|
|
||||||
|
error_chain! {
|
||||||
|
errors {
|
||||||
|
#[doc = "The `DrmDevice` encountered an access error"]
|
||||||
|
DrmDev(dev: String) {
|
||||||
|
description("The drm device encountered an access error"),
|
||||||
|
display("The drm device ({:?}) encountered an access error", dev),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "Creation of gbm resource failed"]
|
||||||
|
GbmInitFailed {
|
||||||
|
description("Creation of gbm resource failed"),
|
||||||
|
display("Creation of gbm resource failed"),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "Swapping front buffers failed"]
|
||||||
|
FailedToSwap {
|
||||||
|
description("Swapping front buffers failed"),
|
||||||
|
display("Swapping front buffers failed"),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "mode is not compatible with all given connectors"]
|
||||||
|
ModeNotSuitable(mode: Mode) {
|
||||||
|
description("Mode is not compatible with all given connectors"),
|
||||||
|
display("Mode ({:?}) is not compatible with all given connectors", mode),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "The given crtc is already in use by another backend"]
|
||||||
|
CrtcAlreadyInUse(crtc: crtc::Handle) {
|
||||||
|
description("The given crtc is already in use by another backend"),
|
||||||
|
display("The given crtc ({:?}) is already in use by another backend", crtc),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "No encoder was found for a given connector on the set crtc"]
|
||||||
|
NoSuitableEncoder(connector: connector::Info, crtc: crtc::Handle) {
|
||||||
|
description("No encoder found for given connector on set crtc"),
|
||||||
|
display("No encoder found for the given connector '{:?}' on the set crtc ({:?})", connector.connector_type(), crtc),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
links {
|
||||||
|
EGL(egl::Error, egl::ErrorKind) #[doc = "EGL error"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H> From<TryNewError<Error, H>> for Error {
|
||||||
|
fn from(err: TryNewError<Error, H>) -> Error {
|
||||||
|
err.0
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,545 @@
|
||||||
|
//! Drm/Kms types and backend implementations
|
||||||
|
//!
|
||||||
|
//! This module provide a `DrmDevice` which acts as a reprensentation for any drm
|
||||||
|
//! device and can be used to create the second provided structure a `DrmBackend`.
|
||||||
|
//!
|
||||||
|
//! Initialization happens through the types provided by [`drm-rs`](https://docs.rs/drm/).
|
||||||
|
//!
|
||||||
|
//! Three entities are relevant for the initialization procedure.
|
||||||
|
//!
|
||||||
|
//! "Crtc"s represent scanout engines of the device pointer to one framebuffer. There responsibility
|
||||||
|
//! is to read the data of the framebuffer and export it into an "Encoder". The number of crtc's
|
||||||
|
//! represent the number of independant output devices the hardware may handle.
|
||||||
|
//!
|
||||||
|
//! An "Encoder" encodes the data of connected crtcs into a video signal for a fixed set
|
||||||
|
//! of connectors. E.g. you might have an analog encoder based on a DAG for VGA ports, but another
|
||||||
|
//! one for digital ones. Also not every encoder might be connected to every crtc.
|
||||||
|
//!
|
||||||
|
//! The last entity the "Connector" represents a port on your computer, possibly with a connected
|
||||||
|
//! monitor, TV, capture card, etc.
|
||||||
|
//!
|
||||||
|
//! The `DrmBackend` created from a `DrmDevice` represents a crtc of the device you can render to
|
||||||
|
//! and that feeds a given set of connectors, that can be manipulated at runtime.
|
||||||
|
//!
|
||||||
|
//! From these circumstances it becomes clear, that one crtc might only send it's data to a connector,
|
||||||
|
//! that is attached to any encoder that is attached to the crtc itself. It is the responsibility of the
|
||||||
|
//! user to ensure that a given set of a crtc with it's connectors is valid or an error will be thrown.
|
||||||
|
//!
|
||||||
|
//! For more details refer to the [`drm-rs` documentation](https://docs.rs/drm).
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//! ## How to use it
|
||||||
|
//!
|
||||||
|
//! ### Initialization
|
||||||
|
//!
|
||||||
|
//! To initialize the `DrmDevice` you need either a `RawFd` or a `File` of
|
||||||
|
//! your drm node. The `File` is recommended as it represents the save api.
|
||||||
|
//!
|
||||||
|
//! Once you got your `DrmDevice` you can then use it to create `DrmBackend`s.
|
||||||
|
//! You will need to use the `drm` crate to provide the required types to create
|
||||||
|
//! a backend.
|
||||||
|
//!
|
||||||
|
//! ```rust,no_run
|
||||||
|
//! extern crate drm;
|
||||||
|
//! extern crate smithay;
|
||||||
|
//! # extern crate wayland_server;
|
||||||
|
//!
|
||||||
|
//! use drm::control::{Device as ControlDevice, ResourceInfo};
|
||||||
|
//! use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState};
|
||||||
|
//! use drm::control::encoder::{Info as EncoderInfo};
|
||||||
|
//! # use std::io::Error as IoError;
|
||||||
|
//! use std::fs::OpenOptions;
|
||||||
|
//! # use std::time::Duration;
|
||||||
|
//! use smithay::backend::drm::DrmDevice;
|
||||||
|
//! # use smithay::backend::drm::{DrmHandler, Id};
|
||||||
|
//! # use wayland_server::EventLoopHandle;
|
||||||
|
//!
|
||||||
|
//! # fn main() {
|
||||||
|
//! // Open the drm device
|
||||||
|
//! let mut options = OpenOptions::new();
|
||||||
|
//! options.read(true);
|
||||||
|
//! options.write(true);
|
||||||
|
//! let mut device = DrmDevice::new_from_file(
|
||||||
|
//! options.open("/dev/dri/card0").unwrap(), // try to detect it properly
|
||||||
|
//! None /*put a logger here*/
|
||||||
|
//! ).unwrap();
|
||||||
|
//!
|
||||||
|
//! // Get a set of all modesetting resource handles
|
||||||
|
//! let res_handles = device.resource_handles().unwrap();
|
||||||
|
//!
|
||||||
|
//! // Use first connected connector for this example
|
||||||
|
//! let connector_info = res_handles.connectors().iter()
|
||||||
|
//! .map(|conn| ConnectorInfo::load_from_device(&device, *conn).unwrap())
|
||||||
|
//! .find(|conn| conn.connection_state() == ConnectorState::Connected)
|
||||||
|
//! .unwrap();
|
||||||
|
//!
|
||||||
|
//! // Use the first encoder
|
||||||
|
//! let encoder_info = EncoderInfo::load_from_device(&device, connector_info.encoders()[0]).unwrap();
|
||||||
|
//!
|
||||||
|
//! // use the connected crtc if any
|
||||||
|
//! let crtc = encoder_info.current_crtc()
|
||||||
|
//! // or use the first one that is compatible with the encoder
|
||||||
|
//! .unwrap_or_else(||
|
||||||
|
//! *res_handles.crtcs()
|
||||||
|
//! .iter()
|
||||||
|
//! .find(|crtc| encoder_info.supports_crtc(**crtc))
|
||||||
|
//! .unwrap());
|
||||||
|
//!
|
||||||
|
//! // Use first mode (usually the highest resolution)
|
||||||
|
//! let mode = connector_info.modes()[0];
|
||||||
|
//!
|
||||||
|
//! // Create the backend
|
||||||
|
//! let backend = device.create_backend(
|
||||||
|
//! crtc,
|
||||||
|
//! mode,
|
||||||
|
//! vec![connector_info.handle()]
|
||||||
|
//! ).unwrap();
|
||||||
|
//! # struct MyDrmHandler;
|
||||||
|
//! #
|
||||||
|
//! # impl DrmHandler for MyDrmHandler {
|
||||||
|
//! # fn ready(&mut self, _: &mut EventLoopHandle, id: Id, _frame: u32, _duration: Duration) {}
|
||||||
|
//! # fn error(&mut self, _: &mut EventLoopHandle, error: IoError) {}
|
||||||
|
//! # }
|
||||||
|
//! #
|
||||||
|
//! # device.set_handler(MyDrmHandler);
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ### Page Flips / Tear-free video
|
||||||
|
//! Calling the usual `EglGraphicsBackend::swap_buffers` function on a
|
||||||
|
//! `DrmBackend` works the same to finish the rendering, but will return
|
||||||
|
//! `SwapBuffersError::AlreadySwapped` for any new calls until the page flip of the
|
||||||
|
//! crtc has happened.
|
||||||
|
//!
|
||||||
|
//! You can monitor the page flips by registering the `DrmDevice` as and
|
||||||
|
//! `FdEventSourceHandler` and setting a `DrmHandler` on it. You will be notified
|
||||||
|
//! whenever a page flip has happend, so you can render the next frame immediately
|
||||||
|
//! and get a tear-free reprensentation on the display.
|
||||||
|
//!
|
||||||
|
//! You need to render at least once to successfully trigger the first event.
|
||||||
|
//!
|
||||||
|
//! ```rust,no_run
|
||||||
|
//! # extern crate drm;
|
||||||
|
//! # extern crate smithay;
|
||||||
|
//! # extern crate wayland_server;
|
||||||
|
//! #
|
||||||
|
//! # use drm::control::{Device as ControlDevice, ResourceInfo};
|
||||||
|
//! # use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState};
|
||||||
|
//! use std::io::Error as IoError;
|
||||||
|
//! use std::os::unix::io::AsRawFd;
|
||||||
|
//! # use std::fs::OpenOptions;
|
||||||
|
//! # use std::time::Duration;
|
||||||
|
//! use smithay::backend::drm::{DrmDevice, DrmBackend, DrmHandler, Id};
|
||||||
|
//! use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
||||||
|
//! use wayland_server::sources::READ;
|
||||||
|
//! # use wayland_server::EventLoopHandle;
|
||||||
|
//! #
|
||||||
|
//! # fn main() {
|
||||||
|
//! #
|
||||||
|
//! # let (_display, mut event_loop) = wayland_server::create_display();
|
||||||
|
//! #
|
||||||
|
//! # let mut options = OpenOptions::new();
|
||||||
|
//! # options.read(true);
|
||||||
|
//! # options.write(true);
|
||||||
|
//! # let mut device = DrmDevice::new_from_file(
|
||||||
|
//! # options.open("/dev/dri/card0").unwrap(), // try to detect it properly
|
||||||
|
//! # None /*put a logger here*/
|
||||||
|
//! # ).unwrap();
|
||||||
|
//! #
|
||||||
|
//! # let res_handles = device.resource_handles().unwrap();
|
||||||
|
//! # let connector_info = res_handles.connectors().iter()
|
||||||
|
//! # .map(|conn| ConnectorInfo::load_from_device(&device, *conn).unwrap())
|
||||||
|
//! # .find(|conn| conn.connection_state() == ConnectorState::Connected)
|
||||||
|
//! # .unwrap();
|
||||||
|
//! # let crtc = res_handles.crtcs()[0];
|
||||||
|
//! # let mode = connector_info.modes()[0];
|
||||||
|
//! # let backend = device.create_backend(
|
||||||
|
//! # crtc,
|
||||||
|
//! # mode,
|
||||||
|
//! # vec![connector_info.handle()]
|
||||||
|
//! # ).unwrap();
|
||||||
|
//!
|
||||||
|
//! struct MyDrmHandler(DrmBackend);
|
||||||
|
//!
|
||||||
|
//! impl DrmHandler for MyDrmHandler {
|
||||||
|
//! fn ready(&mut self, _: &mut EventLoopHandle, id: Id, _frame: u32, _duration: Duration) {
|
||||||
|
//! if self.0.is(id) { // check id in case you got multiple backends
|
||||||
|
//! // ... render surfaces ...
|
||||||
|
//! self.0.swap_buffers().unwrap(); // trigger the swap
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! fn error(&mut self, _: &mut EventLoopHandle, error: IoError) {
|
||||||
|
//! panic!("DrmDevice errored: {}", error);
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! // render something (like clear_color)
|
||||||
|
//! backend.swap_buffers().unwrap();
|
||||||
|
//!
|
||||||
|
//! device.set_handler(MyDrmHandler(backend));
|
||||||
|
//! let fd = device.as_raw_fd();
|
||||||
|
//! let drm_device_id = event_loop.add_handler(device);
|
||||||
|
//! let _drm_event_source = event_loop.add_fd_event_source::<DrmDevice<MyDrmHandler>>(fd, drm_device_id, READ);
|
||||||
|
//!
|
||||||
|
//! event_loop.run().unwrap();
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
use backend::graphics::egl::{EGLContext, GlAttributes, PixelFormatRequirements};
|
||||||
|
use drm::Device as BasicDevice;
|
||||||
|
use drm::control::{connector, crtc, encoder, Mode, ResourceInfo};
|
||||||
|
use drm::control::Device as ControlDevice;
|
||||||
|
use gbm::Device as GbmDevice;
|
||||||
|
use nix;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{Error as IoError, Result as IoResult};
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::ops::Deref;
|
||||||
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::time::Duration;
|
||||||
|
use wayland_server::{EventLoopHandle, StateToken};
|
||||||
|
use wayland_server::sources::{FdEventSource, FdEventSourceImpl, READ};
|
||||||
|
|
||||||
|
mod backend;
|
||||||
|
pub mod error;
|
||||||
|
|
||||||
|
pub use self::backend::DrmBackend;
|
||||||
|
use self::error::*;
|
||||||
|
|
||||||
|
/// Internal struct as required by the drm crate
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct DrmDev(File);
|
||||||
|
|
||||||
|
impl AsRawFd for DrmDev {
|
||||||
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
|
self.0.as_raw_fd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl BasicDevice for DrmDev {}
|
||||||
|
impl ControlDevice for DrmDev {}
|
||||||
|
|
||||||
|
impl DrmDev {
|
||||||
|
unsafe fn new_from_fd(fd: RawFd) -> Self {
|
||||||
|
use std::os::unix::io::FromRawFd;
|
||||||
|
DrmDev(File::from_raw_fd(fd))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_from_file(file: File) -> Self {
|
||||||
|
DrmDev(file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rental! {
|
||||||
|
mod devices {
|
||||||
|
use drm::control::framebuffer;
|
||||||
|
use gbm::{Device as GbmDevice, Surface as GbmSurface};
|
||||||
|
|
||||||
|
use ::backend::graphics::egl::EGLContext;
|
||||||
|
use super::DrmDev;
|
||||||
|
|
||||||
|
#[rental]
|
||||||
|
pub(crate) struct Context {
|
||||||
|
#[subrental(arity = 2)]
|
||||||
|
devices: Box<Devices>,
|
||||||
|
egl: EGLContext<'devices_1, GbmSurface<'devices_1, framebuffer::Info>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rental]
|
||||||
|
pub(crate) struct Devices {
|
||||||
|
drm: Box<DrmDev>,
|
||||||
|
gbm: GbmDevice<'drm>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
use self::devices::{Context, Devices};
|
||||||
|
|
||||||
|
/// Representation of an open drm device node to create rendering backends
|
||||||
|
pub struct DrmDevice<B: Deref<Target = DrmBackend> + 'static> {
|
||||||
|
context: Rc<Context>,
|
||||||
|
backends: HashMap<crtc::Handle, StateToken<B>>,
|
||||||
|
logger: ::slog::Logger,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: From<DrmBackend> + Deref<Target = DrmBackend> + 'static> DrmDevice<B> {
|
||||||
|
/// Create a new `DrmDevice` from a raw file descriptor
|
||||||
|
///
|
||||||
|
/// Returns an error of opening the device failed or context creation was not
|
||||||
|
/// successful.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// The file descriptor might not be valid and needs to be owned by smithay,
|
||||||
|
/// make sure not to share it. Otherwise undefined behavior might occur.
|
||||||
|
pub unsafe fn new_from_fd<L>(fd: RawFd, logger: L) -> Result<Self>
|
||||||
|
where
|
||||||
|
L: Into<Option<::slog::Logger>>,
|
||||||
|
{
|
||||||
|
DrmDevice::new(
|
||||||
|
DrmDev::new_from_fd(fd),
|
||||||
|
GlAttributes {
|
||||||
|
version: None,
|
||||||
|
profile: None,
|
||||||
|
debug: cfg!(debug_assertions),
|
||||||
|
vsync: true,
|
||||||
|
},
|
||||||
|
logger,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new `DrmDevice` from a raw file descriptor and given `GlAttributes`
|
||||||
|
///
|
||||||
|
/// Returns an error of opening the device failed or context creation was not
|
||||||
|
/// successful.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// The file descriptor might not be valid and needs to be owned by smithay,
|
||||||
|
/// make sure not to share it. Otherwise undefined behavior might occur.
|
||||||
|
pub unsafe fn new_from_fd_with_gl_attr<L>(fd: RawFd, attributes: GlAttributes, logger: L) -> Result<Self>
|
||||||
|
where
|
||||||
|
L: Into<Option<::slog::Logger>>,
|
||||||
|
{
|
||||||
|
DrmDevice::new(DrmDev::new_from_fd(fd), attributes, logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new `DrmDevice` from a `File` of an open drm node
|
||||||
|
///
|
||||||
|
/// Returns an error if the file is no valid drm node or context creation was not
|
||||||
|
/// successful.
|
||||||
|
pub fn new_from_file<L>(file: File, logger: L) -> Result<Self>
|
||||||
|
where
|
||||||
|
L: Into<Option<::slog::Logger>>,
|
||||||
|
{
|
||||||
|
DrmDevice::new(
|
||||||
|
DrmDev::new_from_file(file),
|
||||||
|
GlAttributes {
|
||||||
|
version: None,
|
||||||
|
profile: None,
|
||||||
|
debug: cfg!(debug_assertions),
|
||||||
|
vsync: true,
|
||||||
|
},
|
||||||
|
logger,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new `DrmDevice` from a `File` of an open drm node and given `GlAttributes`
|
||||||
|
///
|
||||||
|
/// Returns an error if the file is no valid drm node or context creation was not
|
||||||
|
/// successful.
|
||||||
|
pub fn new_from_file_with_gl_attr<L>(file: File, attributes: GlAttributes, logger: L) -> Result<Self>
|
||||||
|
where
|
||||||
|
L: Into<Option<::slog::Logger>>,
|
||||||
|
{
|
||||||
|
DrmDevice::new(DrmDev::new_from_file(file), attributes, logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new<L>(drm: DrmDev, attributes: GlAttributes, logger: L) -> Result<Self>
|
||||||
|
where
|
||||||
|
L: Into<Option<::slog::Logger>>,
|
||||||
|
{
|
||||||
|
let log = ::slog_or_stdlog(logger).new(o!("smithay_module" => "backend_drm"));
|
||||||
|
|
||||||
|
/* GBM will load a dri driver, but even though they need symbols from
|
||||||
|
* libglapi, in some version of Mesa they are not linked to it. Since
|
||||||
|
* only the gl-renderer module links to it, these symbols won't be globally available,
|
||||||
|
* and loading the DRI driver fails.
|
||||||
|
* Workaround this by dlopen()'ing libglapi with RTLD_GLOBAL.
|
||||||
|
*/
|
||||||
|
unsafe {
|
||||||
|
nix::libc::dlopen(
|
||||||
|
"libglapi.so.0".as_ptr() as *const _,
|
||||||
|
nix::libc::RTLD_LAZY | nix::libc::RTLD_GLOBAL,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
info!(log, "DrmDevice initializing");
|
||||||
|
|
||||||
|
// Open the gbm device from the drm device and create a context based on that
|
||||||
|
Ok(DrmDevice {
|
||||||
|
context: Rc::new(Context::try_new(
|
||||||
|
Box::new(Devices::try_new(Box::new(drm), |drm| {
|
||||||
|
debug!(log, "Creating gbm device");
|
||||||
|
GbmDevice::new_from_drm::<DrmDevice<B>>(drm).chain_err(|| ErrorKind::GbmInitFailed)
|
||||||
|
})?),
|
||||||
|
|devices| {
|
||||||
|
debug!(log, "Creating egl context from gbm device");
|
||||||
|
EGLContext::new_from_gbm(
|
||||||
|
devices.gbm,
|
||||||
|
attributes,
|
||||||
|
PixelFormatRequirements {
|
||||||
|
hardware_accelerated: Some(true),
|
||||||
|
color_bits: Some(24),
|
||||||
|
alpha_bits: Some(8),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
log.clone(),
|
||||||
|
).map_err(Error::from)
|
||||||
|
},
|
||||||
|
)?),
|
||||||
|
backends: HashMap::new(),
|
||||||
|
logger: log,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new backend on a given crtc with a given `Mode` for a given amount
|
||||||
|
/// of `connectors` (mirroring).
|
||||||
|
///
|
||||||
|
/// Errors if initialization fails or the mode is not available on all given
|
||||||
|
/// connectors.
|
||||||
|
pub fn create_backend<I>(&mut self, evlh: &mut EventLoopHandle, crtc: crtc::Handle, mode: Mode,
|
||||||
|
connectors: I)
|
||||||
|
-> Result<StateToken<B>>
|
||||||
|
where
|
||||||
|
I: Into<Vec<connector::Handle>>,
|
||||||
|
{
|
||||||
|
if self.backends.contains_key(&crtc) {
|
||||||
|
bail!(ErrorKind::CrtcAlreadyInUse(crtc));
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the given connectors and crtc match
|
||||||
|
let connectors = connectors.into();
|
||||||
|
|
||||||
|
// check if we have an encoder for every connector and the mode mode
|
||||||
|
for connector in connectors.iter() {
|
||||||
|
let con_info = connector::Info::load_from_device(self.context.head().head(), *connector)
|
||||||
|
.chain_err(|| {
|
||||||
|
ErrorKind::DrmDev(format!("{:?}", self.context.head().head()))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// check the mode
|
||||||
|
if !con_info.modes().contains(&mode) {
|
||||||
|
bail!(ErrorKind::ModeNotSuitable(mode));
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for every connector which encoders it does support
|
||||||
|
let encoders = con_info
|
||||||
|
.encoders()
|
||||||
|
.iter()
|
||||||
|
.map(|encoder| {
|
||||||
|
encoder::Info::load_from_device(self.context.head().head(), *encoder).chain_err(|| {
|
||||||
|
ErrorKind::DrmDev(format!("{:?}", self.context.head().head()))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<encoder::Info>>>()?;
|
||||||
|
|
||||||
|
// and if any encoder supports the selected crtc
|
||||||
|
if !encoders.iter().any(|encoder| encoder.supports_crtc(crtc)) {
|
||||||
|
bail!(ErrorKind::NoSuitableEncoder(con_info, crtc))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// configuration is valid, the kernel will figure out the rest
|
||||||
|
|
||||||
|
let logger = self.logger.new(o!("crtc" => format!("{:?}", crtc)));
|
||||||
|
let backend = DrmBackend::new(self.context.clone(), crtc, mode, connectors, logger)?;
|
||||||
|
let token = evlh.state().insert(backend.into());
|
||||||
|
self.backends.insert(crtc, token.clone());
|
||||||
|
|
||||||
|
Ok(token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// for users convinience and FdEventSource registering
|
||||||
|
impl<B: Deref<Target = DrmBackend> + 'static> AsRawFd for DrmDevice<B> {
|
||||||
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
|
self.context.head().head().as_raw_fd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<B: Deref<Target = DrmBackend> + 'static> BasicDevice for DrmDevice<B> {}
|
||||||
|
impl<B: Deref<Target = DrmBackend> + 'static> ControlDevice for DrmDevice<B> {}
|
||||||
|
|
||||||
|
/// Handler for drm node events
|
||||||
|
///
|
||||||
|
/// See module-level documentation for its use
|
||||||
|
pub trait DrmHandler<B: Deref<Target = DrmBackend> + 'static> {
|
||||||
|
/// A `DrmBackend` has finished swapping buffers and new frame can now
|
||||||
|
/// (and should be immediately) be rendered.
|
||||||
|
///
|
||||||
|
/// The `id` argument is the `Id` of the `DrmBackend` that finished rendering,
|
||||||
|
/// check using `DrmBackend::is`.
|
||||||
|
fn ready(&mut self, evlh: &mut EventLoopHandle, device: &mut DrmDevice<B>, backend: &StateToken<B>,
|
||||||
|
frame: u32, duration: Duration);
|
||||||
|
/// The `DrmDevice` has thrown an error.
|
||||||
|
///
|
||||||
|
/// The related backends are most likely *not* usable anymore and
|
||||||
|
/// the whole stack has to be recreated.
|
||||||
|
fn error(&mut self, evlh: &mut EventLoopHandle, device: &mut DrmDevice<B>, error: IoError);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bind a `DrmDevice` to an EventLoop,
|
||||||
|
///
|
||||||
|
/// This will cause it to recieve events and feed them into an `DrmHandler`
|
||||||
|
pub fn drm_device_bind<B, H>(evlh: &mut EventLoopHandle, device: DrmDevice<B>, handler: H)
|
||||||
|
-> IoResult<FdEventSource<(DrmDevice<B>, H)>>
|
||||||
|
where
|
||||||
|
B: Deref<Target = DrmBackend> + 'static,
|
||||||
|
H: DrmHandler<B> + 'static,
|
||||||
|
{
|
||||||
|
evlh.add_fd_event_source(
|
||||||
|
device.as_raw_fd(),
|
||||||
|
fd_event_source_implementation(),
|
||||||
|
(device, handler),
|
||||||
|
READ,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fd_event_source_implementation<B, H>() -> FdEventSourceImpl<(DrmDevice<B>, H)>
|
||||||
|
where
|
||||||
|
B: Deref<Target = DrmBackend> + 'static,
|
||||||
|
H: DrmHandler<B> + 'static,
|
||||||
|
{
|
||||||
|
FdEventSourceImpl {
|
||||||
|
ready: |evlh, id, _, _| {
|
||||||
|
use std::any::Any;
|
||||||
|
|
||||||
|
let &mut (ref mut dev, ref mut handler) = id;
|
||||||
|
|
||||||
|
struct PageFlipHandler<
|
||||||
|
'a,
|
||||||
|
'b,
|
||||||
|
B: Deref<Target = DrmBackend> + 'static,
|
||||||
|
H: DrmHandler<B> + 'static,
|
||||||
|
> {
|
||||||
|
handler: &'a mut H,
|
||||||
|
evlh: &'b mut EventLoopHandle,
|
||||||
|
_marker: PhantomData<B>,
|
||||||
|
};
|
||||||
|
|
||||||
|
impl<'a, 'b, B, H> crtc::PageFlipHandler<DrmDevice<B>> for PageFlipHandler<'a, 'b, B, H>
|
||||||
|
where
|
||||||
|
B: Deref<Target = DrmBackend> + 'static,
|
||||||
|
H: DrmHandler<B> + 'static,
|
||||||
|
{
|
||||||
|
fn handle_event(&mut self, device: &mut DrmDevice<B>, frame: u32, duration: Duration,
|
||||||
|
userdata: Box<Any>) {
|
||||||
|
let crtc_id: crtc::Handle = *userdata.downcast().unwrap();
|
||||||
|
let token = device.backends.get(&crtc_id).cloned();
|
||||||
|
if let Some(token) = token {
|
||||||
|
// we can now unlock the buffer
|
||||||
|
(**self.evlh.state().get(&token)).unlock_buffer();
|
||||||
|
trace!(device.logger, "Handling event for backend {:?}", crtc_id);
|
||||||
|
// and then call the user to render the next frame
|
||||||
|
self.handler
|
||||||
|
.ready(self.evlh, device, &token, frame, duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
crtc::handle_event(
|
||||||
|
dev,
|
||||||
|
2,
|
||||||
|
None::<&mut ()>,
|
||||||
|
Some(&mut PageFlipHandler {
|
||||||
|
handler,
|
||||||
|
evlh,
|
||||||
|
_marker: PhantomData,
|
||||||
|
}),
|
||||||
|
None::<&mut ()>,
|
||||||
|
).unwrap();
|
||||||
|
},
|
||||||
|
error: |evlh, id, _, error| {
|
||||||
|
warn!(id.0.logger, "DrmDevice errored: {}", error);
|
||||||
|
id.1.error(evlh, &mut id.0, error);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,20 +5,26 @@
|
||||||
///
|
///
|
||||||
/// It therefore falls under glutin's Apache 2.0 license
|
/// It therefore falls under glutin's Apache 2.0 license
|
||||||
/// (see https://github.com/tomaka/glutin/tree/044e651edf67a2029eecc650dd42546af1501414/LICENSE)
|
/// (see https://github.com/tomaka/glutin/tree/044e651edf67a2029eecc650dd42546af1501414/LICENSE)
|
||||||
|
|
||||||
use super::GraphicsBackend;
|
use super::GraphicsBackend;
|
||||||
|
#[cfg(feature = "backend_drm")]
|
||||||
|
use gbm::{AsRaw, Device as GbmDevice, Surface as GbmSurface};
|
||||||
use libloading::Library;
|
use libloading::Library;
|
||||||
use nix::{c_int, c_void};
|
use nix::{c_int, c_void};
|
||||||
|
use rental::TryNewError;
|
||||||
use slog;
|
use slog;
|
||||||
use std::error::{self, Error};
|
use std::error;
|
||||||
|
|
||||||
use std::ffi::{CStr, CString};
|
use std::ffi::{CStr, CString};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io;
|
use std::marker::PhantomData;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ops::Deref;
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
#[cfg(feature = "backend_winit")]
|
||||||
|
use wayland_client::egl as wegl;
|
||||||
|
#[cfg(feature = "backend_winit")]
|
||||||
|
use winit::Window as WinitWindow;
|
||||||
|
#[cfg(feature = "backend_winit")]
|
||||||
|
use winit::os::unix::WindowExt;
|
||||||
|
|
||||||
#[allow(non_camel_case_types, dead_code)]
|
#[allow(non_camel_case_types, dead_code)]
|
||||||
mod ffi {
|
mod ffi {
|
||||||
|
@ -46,7 +52,7 @@ mod ffi {
|
||||||
/// Native types to create an `EGLContext` from.
|
/// Native types to create an `EGLContext` from.
|
||||||
/// Currently supported providers are X11, Wayland and GBM.
|
/// Currently supported providers are X11, Wayland and GBM.
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum NativeDisplay {
|
enum NativeDisplayPtr {
|
||||||
/// X11 Display to create an `EGLContext` upon.
|
/// X11 Display to create an `EGLContext` upon.
|
||||||
X11(ffi::NativeDisplayType),
|
X11(ffi::NativeDisplayType),
|
||||||
/// Wayland Display to create an `EGLContext` upon.
|
/// Wayland Display to create an `EGLContext` upon.
|
||||||
|
@ -58,7 +64,7 @@ pub enum NativeDisplay {
|
||||||
/// Native types to create an `EGLSurface` from.
|
/// Native types to create an `EGLSurface` from.
|
||||||
/// Currently supported providers are X11, Wayland and GBM.
|
/// Currently supported providers are X11, Wayland and GBM.
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum NativeSurface {
|
pub enum NativeSurfacePtr {
|
||||||
/// X11 Window to create an `EGLSurface` upon.
|
/// X11 Window to create an `EGLSurface` upon.
|
||||||
X11(ffi::NativeWindowType),
|
X11(ffi::NativeWindowType),
|
||||||
/// Wayland Surface to create an `EGLSurface` upon.
|
/// Wayland Surface to create an `EGLSurface` upon.
|
||||||
|
@ -67,76 +73,156 @@ pub enum NativeSurface {
|
||||||
Gbm(ffi::NativeWindowType),
|
Gbm(ffi::NativeWindowType),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
/// Enumerates all supported backends
|
||||||
enum NativeType {
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub enum NativeType {
|
||||||
|
/// X11 window & surface
|
||||||
X11,
|
X11,
|
||||||
|
/// Wayland surface
|
||||||
Wayland,
|
Wayland,
|
||||||
|
/// Gbm surface
|
||||||
Gbm,
|
Gbm,
|
||||||
|
/// Unknown
|
||||||
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Error that can happen while creating an `EGLContext` or `EGLSurface`
|
error_chain! {
|
||||||
#[derive(Debug)]
|
errors {
|
||||||
pub enum CreationError {
|
#[doc = "The requested OpenGL version is not supported"]
|
||||||
/// I/O error from the underlying system
|
OpenGlVersionNotSupported(version: (u8, u8)) {
|
||||||
IoError(io::Error),
|
description("The requested OpenGL version is not supported."),
|
||||||
/// Operating System error
|
display("The requested OpenGL version {:?} is not supported.", version),
|
||||||
OsError(String),
|
}
|
||||||
/// The requested OpenGl version is not supported by the graphics system
|
|
||||||
OpenGlVersionNotSupported,
|
|
||||||
/// There is no pixel format available that fulfills all requirements
|
|
||||||
NoAvailablePixelFormat,
|
|
||||||
/// Surface creation from an unsupport combination
|
|
||||||
///
|
|
||||||
/// E.g creating a surface from an X11 window on a context created from a wayland display
|
|
||||||
NonMatchingSurfaceType,
|
|
||||||
/// Context creation is not supported on this system
|
|
||||||
NotSupported,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<io::Error> for CreationError {
|
#[doc = "The EGL implementation does not support creating OpenGL ES contexts"]
|
||||||
fn from(err: io::Error) -> Self {
|
OpenGlesNotSupported {
|
||||||
CreationError::IoError(err)
|
description("The EGL implementation does not support creating OpenGL ES contexts")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "No available pixel format matched the criteria"]
|
||||||
|
NoAvailablePixelFormat {
|
||||||
|
description("No available pixel format matched the criteria.")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "Surface type does not match the context type"]
|
||||||
|
NonMatchingSurfaceType(context: NativeType, surface: NativeType) {
|
||||||
|
description("Surface type does not match the context type."),
|
||||||
|
display("Surface type '{:?}' does not match the context type '{:?}'.", surface, context),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "Context creation is not supported on the current window system"]
|
||||||
|
NotSupported {
|
||||||
|
description("Context creation is not supported on the current window system.")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "Loading libEGL failed"]
|
||||||
|
LoadingEGLFailed {
|
||||||
|
description("Loading libEGL failed"),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "EGL was unable to optain a valid EGL Display"]
|
||||||
|
DisplayNotSupported {
|
||||||
|
description("EGL was unable to optain a valid EGL Display")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "eglInitialize returned an error"]
|
||||||
|
InitFailed {
|
||||||
|
description("Failed to initialize EGL")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "Failed to configure the EGL context"]
|
||||||
|
ConfigFailed {
|
||||||
|
description("Failed to configure the EGL context")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "Context creation failed as one or more requirements could not be met. Try removing some gl attributes or pixel format requirements"]
|
||||||
|
CreationFailed {
|
||||||
|
description("Context creation failed as one or more requirements could not be met. Try removing some gl attributes or pixel format requirements")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "eglCreateWindowSurface failed"]
|
||||||
|
SurfaceCreationFailed {
|
||||||
|
description("Failed to create a new EGLSurface")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "The reason of failure could not be determined"]
|
||||||
|
Unknown(err_no: u32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for CreationError {
|
impl<H> From<TryNewError<Error, H>> for Error {
|
||||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn from(err: TryNewError<Error, H>) -> Error {
|
||||||
formatter.write_str(self.description())?;
|
err.0
|
||||||
if let Some(err) = error::Error::cause(self) {
|
|
||||||
write!(formatter, ": {}", err)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl error::Error for CreationError {
|
/// Trait for supported types returning valid surface pointers for initializing egl
|
||||||
fn description(&self) -> &str {
|
///
|
||||||
match *self {
|
/// # Safety
|
||||||
CreationError::IoError(ref err) => err.description(),
|
/// The returned `NativeSurfacePtr` must be valid for egl
|
||||||
CreationError::OsError(ref text) => text,
|
/// and there is no way to test that.
|
||||||
CreationError::OpenGlVersionNotSupported => {
|
pub unsafe trait NativeSurface {
|
||||||
"The requested OpenGL version is not \
|
/// Type to keep the surface valid, if needed
|
||||||
supported."
|
type Keep: 'static;
|
||||||
}
|
|
||||||
CreationError::NoAvailablePixelFormat => {
|
|
||||||
"Couldn't find any pixel format that matches \
|
|
||||||
the criterias."
|
|
||||||
}
|
|
||||||
CreationError::NonMatchingSurfaceType => "Surface type does not match the context type.",
|
|
||||||
CreationError::NotSupported => "Context creation is not supported on the current window system",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cause(&self) -> Option<&error::Error> {
|
/// Return a surface for the given type if possible
|
||||||
match *self {
|
fn surface(&self, backend: NativeType) -> Result<(NativeSurfacePtr, Self::Keep)>;
|
||||||
CreationError::IoError(ref err) => Some(err),
|
}
|
||||||
_ => None,
|
|
||||||
|
#[cfg(feature = "backend_winit")]
|
||||||
|
unsafe impl NativeSurface for WinitWindow {
|
||||||
|
type Keep = Option<wegl::WlEglSurface>;
|
||||||
|
|
||||||
|
fn surface(&self, backend_type: NativeType) -> Result<(NativeSurfacePtr, Option<wegl::WlEglSurface>)> {
|
||||||
|
match backend_type {
|
||||||
|
NativeType::X11 => if let Some(window) = self.get_xlib_window() {
|
||||||
|
Ok((NativeSurfacePtr::X11(window), None))
|
||||||
|
} else {
|
||||||
|
bail!(ErrorKind::NonMatchingSurfaceType(
|
||||||
|
NativeType::Wayland,
|
||||||
|
NativeType::X11
|
||||||
|
));
|
||||||
|
},
|
||||||
|
NativeType::Wayland => if let Some(surface) = self.get_wayland_surface() {
|
||||||
|
let (w, h) = self.get_inner_size().unwrap();
|
||||||
|
let egl_surface =
|
||||||
|
unsafe { wegl::WlEglSurface::new_from_raw(surface as *mut _, w as i32, h as i32) };
|
||||||
|
Ok((
|
||||||
|
NativeSurfacePtr::Wayland(egl_surface.ptr() as *const _),
|
||||||
|
Some(egl_surface),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
bail!(ErrorKind::NonMatchingSurfaceType(
|
||||||
|
NativeType::X11,
|
||||||
|
NativeType::Wayland
|
||||||
|
));
|
||||||
|
},
|
||||||
|
x => bail!(ErrorKind::NonMatchingSurfaceType(NativeType::Unknown, x)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "backend_drm")]
|
||||||
|
unsafe impl<'a, T: 'static> NativeSurface for GbmSurface<'a, T> {
|
||||||
|
type Keep = ();
|
||||||
|
|
||||||
|
fn surface(&self, backend: NativeType) -> Result<(NativeSurfacePtr, Self::Keep)> {
|
||||||
|
match backend {
|
||||||
|
NativeType::Gbm => Ok((NativeSurfacePtr::Gbm(self.as_raw() as *const _), ())),
|
||||||
|
x => bail!(ErrorKind::NonMatchingSurfaceType(NativeType::Gbm, x)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl NativeSurface for () {
|
||||||
|
type Keep = ();
|
||||||
|
fn surface(&self, _backend: NativeType) -> Result<(NativeSurfacePtr, ())> {
|
||||||
|
bail!(ErrorKind::NotSupported)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// EGL context for rendering
|
/// EGL context for rendering
|
||||||
pub struct EGLContext {
|
pub struct EGLContext<'a, T: NativeSurface> {
|
||||||
_lib: Library,
|
_lib: Library,
|
||||||
context: ffi::egl::types::EGLContext,
|
context: ffi::egl::types::EGLContext,
|
||||||
display: ffi::egl::types::EGLDisplay,
|
display: ffi::egl::types::EGLDisplay,
|
||||||
|
@ -146,38 +232,69 @@ pub struct EGLContext {
|
||||||
pixel_format: PixelFormat,
|
pixel_format: PixelFormat,
|
||||||
backend_type: NativeType,
|
backend_type: NativeType,
|
||||||
logger: slog::Logger,
|
logger: slog::Logger,
|
||||||
|
_lifetime: PhantomData<&'a ()>,
|
||||||
|
_type: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// EGL surface of a given egl context for rendering
|
impl<'a> EGLContext<'a, ()> {
|
||||||
pub struct EGLSurface<'a> {
|
/// Create a new context from a given `winit`-`Window`
|
||||||
context: &'a EGLContext,
|
#[cfg(feature = "backend_winit")]
|
||||||
surface: ffi::egl::types::EGLSurface,
|
pub fn new_from_winit<L>(window: &'a WinitWindow, attributes: GlAttributes,
|
||||||
}
|
reqs: PixelFormatRequirements, logger: L)
|
||||||
|
-> Result<EGLContext<'a, WinitWindow>>
|
||||||
impl<'a> Deref for EGLSurface<'a> {
|
|
||||||
type Target = EGLContext;
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
self.context
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EGLContext {
|
|
||||||
/// Create a new EGL context
|
|
||||||
///
|
|
||||||
/// # Unsafety
|
|
||||||
///
|
|
||||||
/// This method is marked unsafe, because the contents of `NativeDisplay` cannot be verified and may
|
|
||||||
/// contain dangling pointers or similar unsafe content
|
|
||||||
pub unsafe fn new<L>(native: NativeDisplay, mut attributes: GlAttributes, reqs: PixelFormatRequirements,
|
|
||||||
logger: L)
|
|
||||||
-> Result<EGLContext, CreationError>
|
|
||||||
where
|
where
|
||||||
L: Into<Option<::slog::Logger>>,
|
L: Into<Option<::slog::Logger>>,
|
||||||
{
|
{
|
||||||
let logger = logger.into();
|
let log = ::slog_or_stdlog(logger.into()).new(o!("smithay_module" => "renderer_egl"));
|
||||||
let log = ::slog_or_stdlog(logger.clone()).new(o!("smithay_module" => "renderer_egl"));
|
info!(log, "Initializing from winit window");
|
||||||
trace!(log, "Loading libEGL");
|
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
EGLContext::new(
|
||||||
|
if let Some(display) = window.get_xlib_display() {
|
||||||
|
debug!(log, "Window is backed by X11");
|
||||||
|
NativeDisplayPtr::X11(display)
|
||||||
|
} else if let Some(display) = window.get_wayland_display() {
|
||||||
|
debug!(log, "Window is backed by Wayland");
|
||||||
|
NativeDisplayPtr::Wayland(display)
|
||||||
|
} else {
|
||||||
|
error!(log, "Window is backed by an unsupported graphics framework");
|
||||||
|
bail!(ErrorKind::NotSupported)
|
||||||
|
},
|
||||||
|
attributes,
|
||||||
|
reqs,
|
||||||
|
log,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new context from a given `gbm::Device`
|
||||||
|
#[cfg(feature = "backend_drm")]
|
||||||
|
pub fn new_from_gbm<L, U: 'static>(gbm: &'a GbmDevice<'a>, attributes: GlAttributes,
|
||||||
|
reqs: PixelFormatRequirements, logger: L)
|
||||||
|
-> Result<EGLContext<'a, GbmSurface<'a, U>>>
|
||||||
|
where
|
||||||
|
L: Into<Option<::slog::Logger>>,
|
||||||
|
{
|
||||||
|
let log = ::slog_or_stdlog(logger.into()).new(o!("smithay_module" => "renderer_egl"));
|
||||||
|
info!(log, "Initializing from gbm device");
|
||||||
|
unsafe {
|
||||||
|
EGLContext::new(
|
||||||
|
NativeDisplayPtr::Gbm(gbm.as_raw() as *const _),
|
||||||
|
attributes,
|
||||||
|
reqs,
|
||||||
|
log,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: NativeSurface> EGLContext<'a, T> {
|
||||||
|
unsafe fn new(native: NativeDisplayPtr, mut attributes: GlAttributes, reqs: PixelFormatRequirements,
|
||||||
|
log: ::slog::Logger)
|
||||||
|
-> Result<EGLContext<'a, T>>
|
||||||
|
where
|
||||||
|
T: NativeSurface,
|
||||||
|
{
|
||||||
// If no version is given, try OpenGLES 3.0, if available,
|
// If no version is given, try OpenGLES 3.0, if available,
|
||||||
// fallback to 2.0 otherwise
|
// fallback to 2.0 otherwise
|
||||||
let version = match attributes.version {
|
let version = match attributes.version {
|
||||||
|
@ -186,22 +303,22 @@ impl EGLContext {
|
||||||
None => {
|
None => {
|
||||||
debug!(log, "Trying to initialize EGL with OpenGLES 3.0");
|
debug!(log, "Trying to initialize EGL with OpenGLES 3.0");
|
||||||
attributes.version = Some((3, 0));
|
attributes.version = Some((3, 0));
|
||||||
match EGLContext::new(native, attributes, reqs, logger.clone()) {
|
match EGLContext::new(native, attributes, reqs, log.clone()) {
|
||||||
Ok(x) => return Ok(x),
|
Ok(x) => return Ok(x),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
warn!(log, "EGL OpenGLES 3.0 Initialization failed with {}", err);
|
warn!(log, "EGL OpenGLES 3.0 Initialization failed with {}", err);
|
||||||
debug!(log, "Trying to initialize EGL with OpenGLES 2.0");
|
debug!(log, "Trying to initialize EGL with OpenGLES 2.0");
|
||||||
attributes.version = Some((2, 0));
|
attributes.version = Some((2, 0));
|
||||||
return EGLContext::new(native, attributes, reqs, logger);
|
return EGLContext::new(native, attributes, reqs, log);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some((1, _)) => {
|
Some((1, x)) => {
|
||||||
error!(
|
error!(
|
||||||
log,
|
log,
|
||||||
"OpenGLES 1.* is not supported by the EGL renderer backend"
|
"OpenGLES 1.* is not supported by the EGL renderer backend"
|
||||||
);
|
);
|
||||||
return Err(CreationError::OpenGlVersionNotSupported);
|
bail!(ErrorKind::OpenGlVersionNotSupported((1, x)));
|
||||||
}
|
}
|
||||||
Some(version) => {
|
Some(version) => {
|
||||||
error!(
|
error!(
|
||||||
|
@ -209,11 +326,12 @@ impl EGLContext {
|
||||||
"OpenGLES {:?} is unknown and not supported by the EGL renderer backend",
|
"OpenGLES {:?} is unknown and not supported by the EGL renderer backend",
|
||||||
version
|
version
|
||||||
);
|
);
|
||||||
return Err(CreationError::OpenGlVersionNotSupported);
|
bail!(ErrorKind::OpenGlVersionNotSupported(version));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let lib = Library::new("libEGL.so.1")?;
|
trace!(log, "Loading libEGL");
|
||||||
|
let lib = Library::new("libEGL.so.1").chain_err(|| ErrorKind::LoadingEGLFailed)?;
|
||||||
let egl = ffi::egl::Egl::load_with(|sym| {
|
let egl = ffi::egl::Egl::load_with(|sym| {
|
||||||
let name = CString::new(sym).unwrap();
|
let name = CString::new(sym).unwrap();
|
||||||
let symbol = lib.get::<*mut c_void>(name.as_bytes());
|
let symbol = lib.get::<*mut c_void>(name.as_bytes());
|
||||||
|
@ -243,35 +361,42 @@ impl EGLContext {
|
||||||
let has_dp_extension = |e: &str| dp_extensions.iter().any(|s| s == e);
|
let has_dp_extension = |e: &str| dp_extensions.iter().any(|s| s == e);
|
||||||
|
|
||||||
let display = match native {
|
let display = match native {
|
||||||
NativeDisplay::X11(display)
|
NativeDisplayPtr::X11(display)
|
||||||
if has_dp_extension("EGL_KHR_platform_x11") && egl.GetPlatformDisplay.is_loaded() =>
|
if has_dp_extension("EGL_KHR_platform_x11") && egl.GetPlatformDisplay.is_loaded() =>
|
||||||
{
|
{
|
||||||
trace!(log, "EGL Display Initialization via EGL_KHR_platform_x11");
|
trace!(log, "EGL Display Initialization via EGL_KHR_platform_x11");
|
||||||
egl.GetPlatformDisplay(ffi::egl::PLATFORM_X11_KHR, display as *mut _, ptr::null())
|
egl.GetPlatformDisplay(ffi::egl::PLATFORM_X11_KHR, display as *mut _, ptr::null())
|
||||||
}
|
}
|
||||||
|
|
||||||
NativeDisplay::X11(display)
|
NativeDisplayPtr::X11(display)
|
||||||
if has_dp_extension("EGL_EXT_platform_x11") && egl.GetPlatformDisplayEXT.is_loaded() =>
|
if has_dp_extension("EGL_EXT_platform_x11") && egl.GetPlatformDisplayEXT.is_loaded() =>
|
||||||
{
|
{
|
||||||
trace!(log, "EGL Display Initialization via EGL_EXT_platform_x11");
|
trace!(log, "EGL Display Initialization via EGL_EXT_platform_x11");
|
||||||
egl.GetPlatformDisplayEXT(ffi::egl::PLATFORM_X11_EXT, display as *mut _, ptr::null())
|
egl.GetPlatformDisplayEXT(ffi::egl::PLATFORM_X11_EXT, display as *mut _, ptr::null())
|
||||||
}
|
}
|
||||||
|
|
||||||
NativeDisplay::Gbm(display)
|
NativeDisplayPtr::Gbm(display)
|
||||||
if has_dp_extension("EGL_KHR_platform_gbm") && egl.GetPlatformDisplay.is_loaded() =>
|
if has_dp_extension("EGL_KHR_platform_gbm") && egl.GetPlatformDisplay.is_loaded() =>
|
||||||
{
|
{
|
||||||
trace!(log, "EGL Display Initialization via EGL_KHR_platform_gbm");
|
trace!(log, "EGL Display Initialization via EGL_KHR_platform_gbm");
|
||||||
egl.GetPlatformDisplay(ffi::egl::PLATFORM_GBM_KHR, display as *mut _, ptr::null())
|
egl.GetPlatformDisplay(ffi::egl::PLATFORM_GBM_KHR, display as *mut _, ptr::null())
|
||||||
}
|
}
|
||||||
|
|
||||||
NativeDisplay::Gbm(display)
|
NativeDisplayPtr::Gbm(display)
|
||||||
if has_dp_extension("EGL_MESA_platform_gbm") && egl.GetPlatformDisplayEXT.is_loaded() =>
|
if has_dp_extension("EGL_MESA_platform_gbm") && egl.GetPlatformDisplayEXT.is_loaded() =>
|
||||||
{
|
{
|
||||||
trace!(log, "EGL Display Initialization via EGL_MESA_platform_gbm");
|
trace!(log, "EGL Display Initialization via EGL_MESA_platform_gbm");
|
||||||
egl.GetPlatformDisplayEXT(ffi::egl::PLATFORM_GBM_KHR, display as *mut _, ptr::null())
|
egl.GetPlatformDisplayEXT(ffi::egl::PLATFORM_GBM_MESA, display as *mut _, ptr::null())
|
||||||
}
|
}
|
||||||
|
|
||||||
NativeDisplay::Wayland(display)
|
NativeDisplayPtr::Gbm(display)
|
||||||
|
if has_dp_extension("EGL_MESA_platform_gbm") && egl.GetPlatformDisplay.is_loaded() =>
|
||||||
|
{
|
||||||
|
trace!(log, "EGL Display Initialization via EGL_MESA_platform_gbm");
|
||||||
|
egl.GetPlatformDisplay(ffi::egl::PLATFORM_GBM_MESA, display as *mut _, ptr::null())
|
||||||
|
}
|
||||||
|
|
||||||
|
NativeDisplayPtr::Wayland(display)
|
||||||
if has_dp_extension("EGL_KHR_platform_wayland") && egl.GetPlatformDisplay.is_loaded() =>
|
if has_dp_extension("EGL_KHR_platform_wayland") && egl.GetPlatformDisplay.is_loaded() =>
|
||||||
{
|
{
|
||||||
trace!(
|
trace!(
|
||||||
|
@ -285,7 +410,7 @@ impl EGLContext {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
NativeDisplay::Wayland(display)
|
NativeDisplayPtr::Wayland(display)
|
||||||
if has_dp_extension("EGL_EXT_platform_wayland") && egl.GetPlatformDisplayEXT.is_loaded() =>
|
if has_dp_extension("EGL_EXT_platform_wayland") && egl.GetPlatformDisplayEXT.is_loaded() =>
|
||||||
{
|
{
|
||||||
trace!(
|
trace!(
|
||||||
|
@ -299,18 +424,25 @@ impl EGLContext {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
NativeDisplay::X11(display) | NativeDisplay::Gbm(display) | NativeDisplay::Wayland(display) => {
|
NativeDisplayPtr::X11(display) |
|
||||||
|
NativeDisplayPtr::Gbm(display) |
|
||||||
|
NativeDisplayPtr::Wayland(display) => {
|
||||||
trace!(log, "Default EGL Display Initialization via GetDisplay");
|
trace!(log, "Default EGL Display Initialization via GetDisplay");
|
||||||
egl.GetDisplay(display as *mut _)
|
egl.GetDisplay(display as *mut _)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if display == ffi::egl::NO_DISPLAY {
|
||||||
|
error!(log, "EGL Display is not valid");
|
||||||
|
bail!(ErrorKind::DisplayNotSupported);
|
||||||
|
}
|
||||||
|
|
||||||
let egl_version = {
|
let egl_version = {
|
||||||
let mut major: ffi::egl::types::EGLint = mem::uninitialized();
|
let mut major: ffi::egl::types::EGLint = mem::uninitialized();
|
||||||
let mut minor: ffi::egl::types::EGLint = mem::uninitialized();
|
let mut minor: ffi::egl::types::EGLint = mem::uninitialized();
|
||||||
|
|
||||||
if egl.Initialize(display, &mut major, &mut minor) == 0 {
|
if egl.Initialize(display, &mut major, &mut minor) == 0 {
|
||||||
return Err(CreationError::OsError(String::from("eglInitialize failed")));
|
bail!(ErrorKind::InitFailed);
|
||||||
}
|
}
|
||||||
|
|
||||||
info!(log, "EGL Initialized");
|
info!(log, "EGL Initialized");
|
||||||
|
@ -336,7 +468,7 @@ impl EGLContext {
|
||||||
log,
|
log,
|
||||||
"OpenGLES not supported by the underlying EGL implementation"
|
"OpenGLES not supported by the underlying EGL implementation"
|
||||||
);
|
);
|
||||||
return Err(CreationError::OpenGlVersionNotSupported);
|
bail!(ErrorKind::OpenGlesNotSupported);
|
||||||
}
|
}
|
||||||
|
|
||||||
let descriptor = {
|
let descriptor = {
|
||||||
|
@ -362,7 +494,7 @@ impl EGLContext {
|
||||||
log,
|
log,
|
||||||
"OpenglES 3.* is not supported on EGL Versions lower then 1.3"
|
"OpenglES 3.* is not supported on EGL Versions lower then 1.3"
|
||||||
);
|
);
|
||||||
return Err(CreationError::NoAvailablePixelFormat);
|
bail!(ErrorKind::NoAvailablePixelFormat);
|
||||||
}
|
}
|
||||||
trace!(log, "Setting RENDERABLE_TYPE to OPENGL_ES3");
|
trace!(log, "Setting RENDERABLE_TYPE to OPENGL_ES3");
|
||||||
out.push(ffi::egl::RENDERABLE_TYPE as c_int);
|
out.push(ffi::egl::RENDERABLE_TYPE as c_int);
|
||||||
|
@ -377,7 +509,7 @@ impl EGLContext {
|
||||||
log,
|
log,
|
||||||
"OpenglES 2.* is not supported on EGL Versions lower then 1.3"
|
"OpenglES 2.* is not supported on EGL Versions lower then 1.3"
|
||||||
);
|
);
|
||||||
return Err(CreationError::NoAvailablePixelFormat);
|
bail!(ErrorKind::NoAvailablePixelFormat);
|
||||||
}
|
}
|
||||||
trace!(log, "Setting RENDERABLE_TYPE to OPENGL_ES2");
|
trace!(log, "Setting RENDERABLE_TYPE to OPENGL_ES2");
|
||||||
out.push(ffi::egl::RENDERABLE_TYPE as c_int);
|
out.push(ffi::egl::RENDERABLE_TYPE as c_int);
|
||||||
|
@ -446,7 +578,7 @@ impl EGLContext {
|
||||||
|
|
||||||
if reqs.stereoscopy {
|
if reqs.stereoscopy {
|
||||||
error!(log, "Stereoscopy is currently unsupported (sorry!)");
|
error!(log, "Stereoscopy is currently unsupported (sorry!)");
|
||||||
return Err(CreationError::NoAvailablePixelFormat);
|
bail!(ErrorKind::NoAvailablePixelFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
out.push(ffi::egl::NONE as c_int);
|
out.push(ffi::egl::NONE as c_int);
|
||||||
|
@ -464,13 +596,11 @@ impl EGLContext {
|
||||||
&mut num_configs,
|
&mut num_configs,
|
||||||
) == 0
|
) == 0
|
||||||
{
|
{
|
||||||
return Err(CreationError::OsError(
|
bail!(ErrorKind::ConfigFailed);
|
||||||
String::from("eglChooseConfig failed"),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
if num_configs == 0 {
|
if num_configs == 0 {
|
||||||
error!(log, "No matching color format found");
|
error!(log, "No matching color format found");
|
||||||
return Err(CreationError::NoAvailablePixelFormat);
|
bail!(ErrorKind::NoAvailablePixelFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
// analyzing each config
|
// analyzing each config
|
||||||
|
@ -481,7 +611,7 @@ impl EGLContext {
|
||||||
let res = $egl.GetConfigAttrib($display, $config,
|
let res = $egl.GetConfigAttrib($display, $config,
|
||||||
$attr as ffi::egl::types::EGLint, &mut value);
|
$attr as ffi::egl::types::EGLint, &mut value);
|
||||||
if res == 0 {
|
if res == 0 {
|
||||||
return Err(CreationError::OsError(String::from("eglGetConfigAttrib failed")));
|
bail!(ErrorKind::ConfigFailed);
|
||||||
}
|
}
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
|
@ -539,14 +669,8 @@ impl EGLContext {
|
||||||
|
|
||||||
if context.is_null() {
|
if context.is_null() {
|
||||||
match egl.GetError() as u32 {
|
match egl.GetError() as u32 {
|
||||||
ffi::egl::BAD_ATTRIBUTE => {
|
ffi::egl::BAD_ATTRIBUTE => bail!(ErrorKind::CreationFailed),
|
||||||
error!(
|
err_no => bail!(ErrorKind::Unknown(err_no)),
|
||||||
log,
|
|
||||||
"Context creation failed as one or more requirements could not be met. Try removing some gl attributes or pixel format requirements"
|
|
||||||
);
|
|
||||||
return Err(CreationError::OpenGlVersionNotSupported);
|
|
||||||
}
|
|
||||||
e => panic!("eglCreateContext failed: 0x{:x}", e),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
debug!(log, "EGL context successfully created");
|
debug!(log, "EGL context successfully created");
|
||||||
|
@ -583,51 +707,46 @@ impl EGLContext {
|
||||||
surface_attributes: surface_attributes,
|
surface_attributes: surface_attributes,
|
||||||
pixel_format: desc,
|
pixel_format: desc,
|
||||||
backend_type: match native {
|
backend_type: match native {
|
||||||
NativeDisplay::X11(_) => NativeType::X11,
|
NativeDisplayPtr::X11(_) => NativeType::X11,
|
||||||
NativeDisplay::Wayland(_) => NativeType::Wayland,
|
NativeDisplayPtr::Wayland(_) => NativeType::Wayland,
|
||||||
NativeDisplay::Gbm(_) => NativeType::Gbm,
|
NativeDisplayPtr::Gbm(_) => NativeType::Gbm,
|
||||||
},
|
},
|
||||||
logger: log,
|
logger: log,
|
||||||
|
_lifetime: PhantomData,
|
||||||
|
_type: PhantomData,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a surface bound to the given egl context for rendering
|
/// Creates a surface bound to the given egl context for rendering
|
||||||
///
|
pub fn create_surface<'b>(&'a self, native: &'b T) -> Result<EGLSurface<'a, 'b, T>> {
|
||||||
/// # Unsafety
|
|
||||||
///
|
|
||||||
/// This method is marked unsafe, because the contents of `NativeSurface` cannot be verified and may
|
|
||||||
/// contain dangling pointers or similar unsafe content
|
|
||||||
pub unsafe fn create_surface<'a>(&'a self, native: NativeSurface)
|
|
||||||
-> Result<EGLSurface<'a>, CreationError> {
|
|
||||||
trace!(self.logger, "Creating EGL window surface...");
|
trace!(self.logger, "Creating EGL window surface...");
|
||||||
|
|
||||||
let surface = {
|
let (surface, keep) = native.surface(self.backend_type)?;
|
||||||
let surface = match (native, self.backend_type) {
|
|
||||||
(NativeSurface::X11(window), NativeType::X11) |
|
let egl_surface = unsafe {
|
||||||
(NativeSurface::Wayland(window), NativeType::Wayland) |
|
self.egl.CreateWindowSurface(
|
||||||
(NativeSurface::Gbm(window), NativeType::Gbm) => self.egl.CreateWindowSurface(
|
|
||||||
self.display,
|
self.display,
|
||||||
self.config_id,
|
self.config_id,
|
||||||
window,
|
match surface {
|
||||||
|
NativeSurfacePtr::X11(ptr) => ptr,
|
||||||
|
NativeSurfacePtr::Wayland(ptr) => ptr,
|
||||||
|
NativeSurfacePtr::Gbm(ptr) => ptr,
|
||||||
|
},
|
||||||
self.surface_attributes.as_ptr(),
|
self.surface_attributes.as_ptr(),
|
||||||
),
|
)
|
||||||
_ => return Err(CreationError::NonMatchingSurfaceType),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if surface.is_null() {
|
if egl_surface.is_null() {
|
||||||
return Err(CreationError::OsError(
|
bail!(ErrorKind::SurfaceCreationFailed);
|
||||||
String::from("eglCreateWindowSurface failed"),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
surface
|
debug!(self.logger, "EGL surface successfully created");
|
||||||
};
|
|
||||||
|
|
||||||
debug!(self.logger, "EGL window surface successfully created");
|
|
||||||
|
|
||||||
Ok(EGLSurface {
|
Ok(EGLSurface {
|
||||||
context: &self,
|
context: &self,
|
||||||
surface: surface,
|
surface: egl_surface,
|
||||||
|
keep,
|
||||||
|
_lifetime_surface: PhantomData,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -651,9 +770,45 @@ impl EGLContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> EGLSurface<'a> {
|
unsafe impl<'a, T: NativeSurface> Send for EGLContext<'a, T> {}
|
||||||
|
unsafe impl<'a, T: NativeSurface> Sync for EGLContext<'a, T> {}
|
||||||
|
|
||||||
|
impl<'a, T: NativeSurface> Drop for EGLContext<'a, T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
// we don't call MakeCurrent(0, 0) because we are not sure that the context
|
||||||
|
// is still the current one
|
||||||
|
self.egl
|
||||||
|
.DestroyContext(self.display as *const _, self.context as *const _);
|
||||||
|
self.egl.Terminate(self.display as *const _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// EGL surface of a given egl context for rendering
|
||||||
|
pub struct EGLSurface<'context, 'surface, T: NativeSurface + 'context> {
|
||||||
|
context: &'context EGLContext<'context, T>,
|
||||||
|
surface: ffi::egl::types::EGLSurface,
|
||||||
|
keep: T::Keep,
|
||||||
|
_lifetime_surface: PhantomData<&'surface ()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b, T: NativeSurface> Deref for EGLSurface<'a, 'b, T> {
|
||||||
|
type Target = T::Keep;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.keep
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b, T: NativeSurface> DerefMut for EGLSurface<'a, 'b, T> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.keep
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'context, 'surface, T: NativeSurface> EGLSurface<'context, 'surface, T> {
|
||||||
/// Swaps buffers at the end of a frame.
|
/// Swaps buffers at the end of a frame.
|
||||||
pub fn swap_buffers(&self) -> Result<(), SwapBuffersError> {
|
pub fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
self.context
|
self.context
|
||||||
.egl
|
.egl
|
||||||
|
@ -676,7 +831,7 @@ impl<'a> EGLSurface<'a> {
|
||||||
///
|
///
|
||||||
/// This function is marked unsafe, because the context cannot be made current
|
/// This function is marked unsafe, because the context cannot be made current
|
||||||
/// on multiple threads.
|
/// on multiple threads.
|
||||||
pub unsafe fn make_current(&self) -> Result<(), SwapBuffersError> {
|
pub unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
|
||||||
let ret = self.context.egl.MakeCurrent(
|
let ret = self.context.egl.MakeCurrent(
|
||||||
self.context.display as *const _,
|
self.context.display as *const _,
|
||||||
self.surface as *const _,
|
self.surface as *const _,
|
||||||
|
@ -695,24 +850,10 @@ impl<'a> EGLSurface<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for EGLContext {}
|
unsafe impl<'a, 'b, T: NativeSurface> Send for EGLSurface<'a, 'b, T> {}
|
||||||
unsafe impl Sync for EGLContext {}
|
unsafe impl<'a, 'b, T: NativeSurface> Sync for EGLSurface<'a, 'b, T> {}
|
||||||
unsafe impl<'a> Send for EGLSurface<'a> {}
|
|
||||||
unsafe impl<'a> Sync for EGLSurface<'a> {}
|
|
||||||
|
|
||||||
impl Drop for EGLContext {
|
impl<'a, 'b, T: NativeSurface> Drop for EGLSurface<'a, 'b, T> {
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
// we don't call MakeCurrent(0, 0) because we are not sure that the context
|
|
||||||
// is still the current one
|
|
||||||
self.egl
|
|
||||||
.DestroyContext(self.display as *const _, self.context as *const _);
|
|
||||||
self.egl.Terminate(self.display as *const _);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Drop for EGLSurface<'a> {
|
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.context
|
self.context
|
||||||
|
@ -744,6 +885,28 @@ pub enum SwapBuffersError {
|
||||||
AlreadySwapped,
|
AlreadySwapped,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for SwapBuffersError {
|
||||||
|
fn fmt(&self, formatter: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
|
||||||
|
use std::error::Error;
|
||||||
|
write!(formatter, "{}", self.description())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl error::Error for SwapBuffersError {
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
&SwapBuffersError::ContextLost => "The context has been lost, it needs to be recreated",
|
||||||
|
&SwapBuffersError::AlreadySwapped => {
|
||||||
|
"Buffers are already swapped, swap_buffers was called too many times"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cause(&self) -> Option<&error::Error> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Attributes to use when creating an OpenGL context.
|
/// Attributes to use when creating an OpenGL context.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct GlAttributes {
|
pub struct GlAttributes {
|
||||||
|
@ -828,7 +991,7 @@ pub struct PixelFormat {
|
||||||
/// and can be used to render upon
|
/// and can be used to render upon
|
||||||
pub trait EGLGraphicsBackend: GraphicsBackend {
|
pub trait EGLGraphicsBackend: GraphicsBackend {
|
||||||
/// Swaps buffers at the end of a frame.
|
/// Swaps buffers at the end of a frame.
|
||||||
fn swap_buffers(&self) -> Result<(), SwapBuffersError>;
|
fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError>;
|
||||||
|
|
||||||
/// Returns the address of an OpenGL function.
|
/// Returns the address of an OpenGL function.
|
||||||
///
|
///
|
||||||
|
@ -851,7 +1014,7 @@ pub trait EGLGraphicsBackend: GraphicsBackend {
|
||||||
///
|
///
|
||||||
/// This function is marked unsafe, because the context cannot be made current
|
/// This function is marked unsafe, because the context cannot be made current
|
||||||
/// on multiple threads.
|
/// on multiple threads.
|
||||||
unsafe fn make_current(&self) -> Result<(), SwapBuffersError>;
|
unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError>;
|
||||||
|
|
||||||
/// Returns the pixel format of the main framebuffer of the context.
|
/// Returns the pixel format of the main framebuffer of the context.
|
||||||
fn get_pixel_format(&self) -> PixelFormat;
|
fn get_pixel_format(&self) -> PixelFormat;
|
||||||
|
|
|
@ -7,7 +7,6 @@ use glium::backend::{Backend, Context, Facade};
|
||||||
use glium::debug::DebugCallbackBehavior;
|
use glium::debug::DebugCallbackBehavior;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
impl From<SwapBuffersError> for GliumSwapBuffersError {
|
impl From<SwapBuffersError> for GliumSwapBuffersError {
|
||||||
|
@ -34,7 +33,7 @@ impl<T: EGLGraphicsBackend + 'static> GliumGraphicsBackend<T> {
|
||||||
GliumGraphicsBackend {
|
GliumGraphicsBackend {
|
||||||
// cannot fail
|
// cannot fail
|
||||||
context: unsafe {
|
context: unsafe {
|
||||||
Context::new::<_, ()>(internal.clone(), false, DebugCallbackBehavior::default()).unwrap()
|
Context::new(internal.clone(), true, DebugCallbackBehavior::default()).unwrap()
|
||||||
},
|
},
|
||||||
backend: internal,
|
backend: internal,
|
||||||
}
|
}
|
||||||
|
@ -56,10 +55,10 @@ impl<T: EGLGraphicsBackend + 'static> GliumGraphicsBackend<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: EGLGraphicsBackend> Deref for GliumGraphicsBackend<T> {
|
impl<T: EGLGraphicsBackend> Deref for GliumGraphicsBackend<T> {
|
||||||
type Target = Context;
|
type Target = T;
|
||||||
|
|
||||||
fn deref(&self) -> &Context {
|
fn deref(&self) -> &T {
|
||||||
&self.context
|
&self.backend.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,15 +68,9 @@ impl<T: EGLGraphicsBackend> Facade for GliumGraphicsBackend<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converter trait to expose `glium` compatibility for all `EGLGraphicsBackend`s
|
impl<T: EGLGraphicsBackend + 'static> From<T> for GliumGraphicsBackend<T> {
|
||||||
pub trait IntoGlium: EGLGraphicsBackend + Sized {
|
fn from(backend: T) -> Self {
|
||||||
/// Wrap the given `EGLGraphicsBackend` to a `GliumGraphicBackend`
|
GliumGraphicsBackend::new(backend)
|
||||||
fn into_glium(self) -> GliumGraphicsBackend<Self>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: EGLGraphicsBackend + 'static> IntoGlium for T {
|
|
||||||
fn into_glium(self) -> GliumGraphicsBackend<Self> {
|
|
||||||
GliumGraphicsBackend::new(self)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,9 @@ pub trait GraphicsBackend {
|
||||||
/// Format representing the image drawn for the cursor.
|
/// Format representing the image drawn for the cursor.
|
||||||
type CursorFormat;
|
type CursorFormat;
|
||||||
|
|
||||||
|
/// Error the underlying backend throws if operations fail
|
||||||
|
type Error;
|
||||||
|
|
||||||
/// Sets the cursor position and therefor updates the drawn cursors position.
|
/// Sets the cursor position and therefor updates the drawn cursors position.
|
||||||
/// Useful as well for e.g. pointer wrapping.
|
/// Useful as well for e.g. pointer wrapping.
|
||||||
///
|
///
|
||||||
|
@ -19,14 +22,15 @@ pub trait GraphicsBackend {
|
||||||
/// by the higher compositor and not by the backend. It is still good practice to update
|
/// by the higher compositor and not by the backend. It is still good practice to update
|
||||||
/// the position after every recieved event, but don't rely on pointer wrapping working.
|
/// the position after every recieved event, but don't rely on pointer wrapping working.
|
||||||
///
|
///
|
||||||
fn set_cursor_position(&mut self, x: u32, y: u32) -> Result<(), ()>;
|
fn set_cursor_position(&self, x: u32, y: u32) -> Result<(), Self::Error>;
|
||||||
|
|
||||||
/// Set the cursor drawn on the `GraphicsBackend`.
|
/// Set the cursor drawn on the `GraphicsBackend`.
|
||||||
///
|
///
|
||||||
/// The format is entirely dictated by the concrete implementation and might range
|
/// The format is entirely dictated by the concrete implementation and might range
|
||||||
/// from raw image buffers over a fixed list of possible cursor types to simply the
|
/// from raw image buffers over a fixed list of possible cursor types to simply the
|
||||||
/// void type () to represent no possible customization of the cursor itself.
|
/// void type () to represent no possible customization of the cursor itself.
|
||||||
fn set_cursor_representation(&mut self, cursor: Self::CursorFormat);
|
fn set_cursor_representation(&self, cursor: Self::CursorFormat, hotspot: (u32, u32))
|
||||||
|
-> Result<(), Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod software;
|
pub mod software;
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
//! Common traits for input backends to receive input from.
|
//! Common traits for input backends to receive input from.
|
||||||
|
|
||||||
use backend::{SeatInternal, TouchSlotInternal};
|
use backend::{SeatInternal, TouchSlotInternal};
|
||||||
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
/// A seat describes a group of input devices and at least one
|
/// A seat describes a group of input devices and at least one
|
||||||
|
|
|
@ -6,7 +6,6 @@ use input as libinput;
|
||||||
use input::event;
|
use input::event;
|
||||||
use std::collections::hash_map::{DefaultHasher, Entry, HashMap};
|
use std::collections::hash_map::{DefaultHasher, Entry, HashMap};
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
use std::io::Error as IoError;
|
use std::io::Error as IoError;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
//! Supported graphics backends:
|
//! Supported graphics backends:
|
||||||
//!
|
//!
|
||||||
//! - winit
|
//! - winit
|
||||||
|
//! - drm
|
||||||
//!
|
//!
|
||||||
//! Supported input backends:
|
//! Supported input backends:
|
||||||
//!
|
//!
|
||||||
|
@ -18,6 +19,8 @@ pub mod graphics;
|
||||||
|
|
||||||
#[cfg(feature = "backend_winit")]
|
#[cfg(feature = "backend_winit")]
|
||||||
pub mod winit;
|
pub mod winit;
|
||||||
|
#[cfg(feature = "backend_drm")]
|
||||||
|
pub mod drm;
|
||||||
#[cfg(feature = "backend_libinput")]
|
#[cfg(feature = "backend_libinput")]
|
||||||
pub mod libinput;
|
pub mod libinput;
|
||||||
|
|
||||||
|
|
|
@ -2,43 +2,65 @@
|
||||||
|
|
||||||
use backend::{SeatInternal, TouchSlotInternal};
|
use backend::{SeatInternal, TouchSlotInternal};
|
||||||
use backend::graphics::GraphicsBackend;
|
use backend::graphics::GraphicsBackend;
|
||||||
use backend::graphics::egl::{CreationError, EGLContext, EGLGraphicsBackend, GlAttributes, NativeDisplay,
|
use backend::graphics::egl::{self, EGLContext, EGLGraphicsBackend, GlAttributes, PixelFormat,
|
||||||
NativeSurface, PixelFormat, PixelFormatRequirements, SwapBuffersError};
|
PixelFormatRequirements, SwapBuffersError};
|
||||||
use backend::input::{Axis, AxisSource, Event as BackendEvent, InputBackend, InputHandler, KeyState,
|
use backend::input::{Axis, AxisSource, Event as BackendEvent, InputBackend, InputHandler, KeyState,
|
||||||
KeyboardKeyEvent, MouseButton, MouseButtonState, PointerAxisEvent, PointerButtonEvent,
|
KeyboardKeyEvent, MouseButton, MouseButtonState, PointerAxisEvent, PointerButtonEvent,
|
||||||
PointerMotionAbsoluteEvent, Seat, SeatCapabilities, TouchCancelEvent, TouchDownEvent,
|
PointerMotionAbsoluteEvent, Seat, SeatCapabilities, TouchCancelEvent, TouchDownEvent,
|
||||||
TouchMotionEvent, TouchSlot, TouchUpEvent, UnusedEvent};
|
TouchMotionEvent, TouchSlot, TouchUpEvent, UnusedEvent};
|
||||||
use nix::c_void;
|
use nix::c_void;
|
||||||
|
use rental::TryNewError;
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::error::Error;
|
use std::error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use wayland_client::egl as wegl;
|
use winit::{ElementState, Event, EventsLoop, KeyboardInput, MouseButton as WinitMouseButton, MouseCursor,
|
||||||
use winit::{CreationError as WinitCreationError, ElementState, Event, EventsLoop, KeyboardInput,
|
MouseScrollDelta, Touch, TouchPhase, WindowBuilder, WindowEvent};
|
||||||
MouseButton as WinitMouseButton, MouseCursor, MouseScrollDelta, Touch, TouchPhase, Window,
|
|
||||||
WindowBuilder, WindowEvent};
|
|
||||||
use winit::os::unix::{WindowExt, get_x11_xconnection};
|
|
||||||
|
|
||||||
rental! {
|
rental! {
|
||||||
mod egl {
|
mod rental {
|
||||||
use std::boxed::Box;
|
use std::boxed::Box;
|
||||||
|
use ::winit::{Window as WinitWindow};
|
||||||
use ::backend::graphics::egl::{EGLContext, EGLSurface};
|
use ::backend::graphics::egl::{EGLContext, EGLSurface};
|
||||||
|
|
||||||
|
#[rental]
|
||||||
|
pub struct Window {
|
||||||
|
window: Box<WinitWindow>,
|
||||||
|
egl: EGL<'window>,
|
||||||
|
}
|
||||||
|
|
||||||
#[rental(deref_suffix)]
|
#[rental]
|
||||||
pub struct RentEGL {
|
pub struct EGL<'a> {
|
||||||
context: Box<EGLContext>,
|
context: Box<EGLContext<'a, WinitWindow>>,
|
||||||
surface: EGLSurface<'context>,
|
surface: EGLSurface<'context, 'a, WinitWindow>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use self::rental::{Window, EGL};
|
||||||
|
|
||||||
|
error_chain! {
|
||||||
|
errors {
|
||||||
|
#[doc = "Failed to initialize a window"]
|
||||||
|
InitFailed {
|
||||||
|
description("Failed to initialize a window")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
links {
|
||||||
|
EGL(egl::Error, egl::ErrorKind) #[doc = "EGL error"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H> From<TryNewError<Error, H>> for Error {
|
||||||
|
fn from(err: TryNewError<Error, H>) -> Error {
|
||||||
|
err.0
|
||||||
|
}
|
||||||
|
}
|
||||||
/// Window with an active EGL Context created by `winit`. Implements the
|
/// Window with an active EGL Context created by `winit`. Implements the
|
||||||
/// `EGLGraphicsBackend` graphics backend trait
|
/// `EGLGraphicsBackend` graphics backend trait
|
||||||
pub struct WinitGraphicsBackend {
|
pub struct WinitGraphicsBackend {
|
||||||
window: Rc<Window>,
|
window: Rc<Window>,
|
||||||
context: egl::RentEGL,
|
|
||||||
logger: ::slog::Logger,
|
logger: ::slog::Logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +70,6 @@ pub struct WinitGraphicsBackend {
|
||||||
pub struct WinitInputBackend {
|
pub struct WinitInputBackend {
|
||||||
events_loop: EventsLoop,
|
events_loop: EventsLoop,
|
||||||
window: Rc<Window>,
|
window: Rc<Window>,
|
||||||
surface: Option<wegl::WlEglSurface>,
|
|
||||||
time_counter: u32,
|
time_counter: u32,
|
||||||
key_counter: u32,
|
key_counter: u32,
|
||||||
seat: Seat,
|
seat: Seat,
|
||||||
|
@ -60,7 +81,7 @@ pub struct WinitInputBackend {
|
||||||
/// Create a new `WinitGraphicsBackend`, which implements the `EGLGraphicsBackend`
|
/// Create a new `WinitGraphicsBackend`, which implements the `EGLGraphicsBackend`
|
||||||
/// graphics backend trait and a corresponding `WinitInputBackend`, which implements
|
/// graphics backend trait and a corresponding `WinitInputBackend`, which implements
|
||||||
/// the `InputBackend` trait
|
/// the `InputBackend` trait
|
||||||
pub fn init<L>(logger: L) -> Result<(WinitGraphicsBackend, WinitInputBackend), CreationError>
|
pub fn init<L>(logger: L) -> Result<(WinitGraphicsBackend, WinitInputBackend)>
|
||||||
where
|
where
|
||||||
L: Into<Option<::slog::Logger>>,
|
L: Into<Option<::slog::Logger>>,
|
||||||
{
|
{
|
||||||
|
@ -77,7 +98,7 @@ where
|
||||||
/// graphics backend trait, from a given `WindowBuilder` struct and a corresponding
|
/// graphics backend trait, from a given `WindowBuilder` struct and a corresponding
|
||||||
/// `WinitInputBackend`, which implements the `InputBackend` trait
|
/// `WinitInputBackend`, which implements the `InputBackend` trait
|
||||||
pub fn init_from_builder<L>(builder: WindowBuilder, logger: L)
|
pub fn init_from_builder<L>(builder: WindowBuilder, logger: L)
|
||||||
-> Result<(WinitGraphicsBackend, WinitInputBackend), CreationError>
|
-> Result<(WinitGraphicsBackend, WinitInputBackend)>
|
||||||
where
|
where
|
||||||
L: Into<Option<::slog::Logger>>,
|
L: Into<Option<::slog::Logger>>,
|
||||||
{
|
{
|
||||||
|
@ -97,9 +118,8 @@ where
|
||||||
/// graphics backend trait, from a given `WindowBuilder` struct, as well as given
|
/// graphics backend trait, from a given `WindowBuilder` struct, as well as given
|
||||||
/// `GlAttributes` for further customization of the rendering pipeline and a
|
/// `GlAttributes` for further customization of the rendering pipeline and a
|
||||||
/// corresponding `WinitInputBackend`, which implements the `InputBackend` trait.
|
/// corresponding `WinitInputBackend`, which implements the `InputBackend` trait.
|
||||||
pub fn init_from_builder_with_gl_attr<L>(
|
pub fn init_from_builder_with_gl_attr<L>(builder: WindowBuilder, attributes: GlAttributes, logger: L)
|
||||||
builder: WindowBuilder, attributes: GlAttributes, logger: L)
|
-> Result<(WinitGraphicsBackend, WinitInputBackend)>
|
||||||
-> Result<(WinitGraphicsBackend, WinitInputBackend), CreationError>
|
|
||||||
where
|
where
|
||||||
L: Into<Option<::slog::Logger>>,
|
L: Into<Option<::slog::Logger>>,
|
||||||
{
|
{
|
||||||
|
@ -107,37 +127,15 @@ where
|
||||||
info!(log, "Initializing a winit backend");
|
info!(log, "Initializing a winit backend");
|
||||||
|
|
||||||
let events_loop = EventsLoop::new();
|
let events_loop = EventsLoop::new();
|
||||||
let window = Rc::new(builder.build(&events_loop)?);
|
let winit_window = builder
|
||||||
|
.build(&events_loop)
|
||||||
|
.chain_err(|| ErrorKind::InitFailed)?;
|
||||||
debug!(log, "Window created");
|
debug!(log, "Window created");
|
||||||
|
|
||||||
let (native_display, native_surface, surface) = if let (Some(conn), Some(window)) =
|
let window = Rc::new(Window::try_new(Box::new(winit_window), |window| {
|
||||||
(get_x11_xconnection(), window.get_xlib_window())
|
EGL::try_new(
|
||||||
{
|
Box::new(match EGLContext::new_from_winit(
|
||||||
debug!(log, "Window is backed by X11");
|
&*window,
|
||||||
(
|
|
||||||
NativeDisplay::X11(conn.display as *const _),
|
|
||||||
NativeSurface::X11(window),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
} else if let (Some(display), Some(surface)) =
|
|
||||||
(window.get_wayland_display(), window.get_wayland_surface())
|
|
||||||
{
|
|
||||||
debug!(log, "Window is backed by Wayland");
|
|
||||||
let (w, h) = window.get_inner_size().unwrap();
|
|
||||||
let egl_surface = unsafe { wegl::WlEglSurface::new_from_raw(surface as *mut _, w as i32, h as i32) };
|
|
||||||
(
|
|
||||||
NativeDisplay::Wayland(display),
|
|
||||||
NativeSurface::Wayland(egl_surface.ptr() as *const _),
|
|
||||||
Some(egl_surface),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
error!(log, "Window is backed by an unsupported graphics framework");
|
|
||||||
return Err(CreationError::NotSupported);
|
|
||||||
};
|
|
||||||
|
|
||||||
let context = unsafe {
|
|
||||||
match EGLContext::new(
|
|
||||||
native_display,
|
|
||||||
attributes,
|
attributes,
|
||||||
PixelFormatRequirements {
|
PixelFormatRequirements {
|
||||||
hardware_accelerated: Some(true),
|
hardware_accelerated: Some(true),
|
||||||
|
@ -148,28 +146,21 @@ where
|
||||||
log.clone(),
|
log.clone(),
|
||||||
) {
|
) {
|
||||||
Ok(context) => context,
|
Ok(context) => context,
|
||||||
Err(err) => {
|
Err(err) => bail!(err),
|
||||||
error!(log, "EGLContext creation failed:\n {}", err);
|
}),
|
||||||
return Err(err);
|
|context| context.create_surface(window),
|
||||||
}
|
).map_err(egl::Error::from)
|
||||||
}
|
.map_err(Error::from)
|
||||||
};
|
})?);
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
WinitGraphicsBackend {
|
WinitGraphicsBackend {
|
||||||
window: window.clone(),
|
window: window.clone(),
|
||||||
context: match egl::RentEGL::try_new(Box::new(context), move |context| unsafe {
|
|
||||||
context.create_surface(native_surface)
|
|
||||||
}) {
|
|
||||||
Ok(x) => x,
|
|
||||||
Err(::rental::TryNewError(err, _)) => return Err(err),
|
|
||||||
},
|
|
||||||
logger: log.new(o!("smithay_winit_component" => "graphics")),
|
logger: log.new(o!("smithay_winit_component" => "graphics")),
|
||||||
},
|
},
|
||||||
WinitInputBackend {
|
WinitInputBackend {
|
||||||
events_loop: events_loop,
|
events_loop,
|
||||||
window: window,
|
window,
|
||||||
surface: surface,
|
|
||||||
time_counter: 0,
|
time_counter: 0,
|
||||||
key_counter: 0,
|
key_counter: 0,
|
||||||
seat: Seat::new(
|
seat: Seat::new(
|
||||||
|
@ -189,47 +180,53 @@ where
|
||||||
|
|
||||||
impl GraphicsBackend for WinitGraphicsBackend {
|
impl GraphicsBackend for WinitGraphicsBackend {
|
||||||
type CursorFormat = MouseCursor;
|
type CursorFormat = MouseCursor;
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
fn set_cursor_position(&mut self, x: u32, y: u32) -> Result<(), ()> {
|
fn set_cursor_position(&self, x: u32, y: u32) -> ::std::result::Result<(), ()> {
|
||||||
debug!(self.logger, "Setting cursor position to {:?}", (x, y));
|
debug!(self.logger, "Setting cursor position to {:?}", (x, y));
|
||||||
self.window.set_cursor_position(x as i32, y as i32)
|
self.window.head().set_cursor_position(x as i32, y as i32)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_cursor_representation(&mut self, cursor: Self::CursorFormat) {
|
fn set_cursor_representation(&self, cursor: Self::CursorFormat, _hotspot: (u32, u32))
|
||||||
|
-> ::std::result::Result<(), ()> {
|
||||||
// Cannot log this one, as `CursorFormat` is not `Debug` and should not be
|
// Cannot log this one, as `CursorFormat` is not `Debug` and should not be
|
||||||
debug!(self.logger, "Changing cursor representation");
|
debug!(self.logger, "Changing cursor representation");
|
||||||
self.window.set_cursor(cursor)
|
self.window.head().set_cursor(cursor);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EGLGraphicsBackend for WinitGraphicsBackend {
|
impl EGLGraphicsBackend for WinitGraphicsBackend {
|
||||||
fn swap_buffers(&self) -> Result<(), SwapBuffersError> {
|
fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
|
||||||
trace!(self.logger, "Swapping buffers");
|
trace!(self.logger, "Swapping buffers");
|
||||||
self.context.rent(|surface| surface.swap_buffers())
|
self.window
|
||||||
|
.rent(|egl| egl.rent(|surface| surface.swap_buffers()))
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void {
|
unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void {
|
||||||
trace!(self.logger, "Getting symbol for {:?}", symbol);
|
trace!(self.logger, "Getting symbol for {:?}", symbol);
|
||||||
self.context.get_proc_address(symbol)
|
self.window.rent(|egl| egl.head().get_proc_address(symbol))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_framebuffer_dimensions(&self) -> (u32, u32) {
|
fn get_framebuffer_dimensions(&self) -> (u32, u32) {
|
||||||
self.window
|
self.window
|
||||||
|
.head()
|
||||||
.get_inner_size_pixels()
|
.get_inner_size_pixels()
|
||||||
.expect("Window does not exist anymore")
|
.expect("Window does not exist anymore")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_current(&self) -> bool {
|
fn is_current(&self) -> bool {
|
||||||
self.context.is_current()
|
self.window.rent(|egl| egl.head().is_current())
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn make_current(&self) -> Result<(), SwapBuffersError> {
|
unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
|
||||||
debug!(self.logger, "Setting EGL context to be the current context");
|
debug!(self.logger, "Setting EGL context to be the current context");
|
||||||
self.context.rent(|surface| surface.make_current())
|
self.window
|
||||||
|
.rent(|egl| egl.rent(|surface| surface.make_current()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_pixel_format(&self) -> PixelFormat {
|
fn get_pixel_format(&self) -> PixelFormat {
|
||||||
self.context.get_pixel_format()
|
self.window.rent(|egl| egl.head().get_pixel_format())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,7 +239,7 @@ pub enum WinitInputError {
|
||||||
WindowClosed,
|
WindowClosed,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error for WinitInputError {
|
impl error::Error for WinitInputError {
|
||||||
fn description(&self) -> &str {
|
fn description(&self) -> &str {
|
||||||
match *self {
|
match *self {
|
||||||
WinitInputError::WindowClosed => "Glutin Window was closed",
|
WinitInputError::WindowClosed => "Glutin Window was closed",
|
||||||
|
@ -252,6 +249,7 @@ impl Error for WinitInputError {
|
||||||
|
|
||||||
impl fmt::Display for WinitInputError {
|
impl fmt::Display for WinitInputError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
use std::error::Error;
|
||||||
write!(f, "{}", self.description())
|
write!(f, "{}", self.description())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -310,16 +308,24 @@ impl PointerMotionAbsoluteEvent for WinitMouseMovedEvent {
|
||||||
|
|
||||||
fn x_transformed(&self, width: u32) -> u32 {
|
fn x_transformed(&self, width: u32) -> u32 {
|
||||||
cmp::min(
|
cmp::min(
|
||||||
(self.x * width as f64 / self.window.get_inner_size_points().unwrap_or((width, 0)).0 as f64) as
|
(self.x * width as f64 /
|
||||||
u32,
|
self.window
|
||||||
|
.head()
|
||||||
|
.get_inner_size_points()
|
||||||
|
.unwrap_or((width, 0))
|
||||||
|
.0 as f64) as u32,
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn y_transformed(&self, height: u32) -> u32 {
|
fn y_transformed(&self, height: u32) -> u32 {
|
||||||
cmp::min(
|
cmp::min(
|
||||||
(self.y * height as f64 / self.window.get_inner_size_points().unwrap_or((0, height)).1 as f64) as
|
(self.y * height as f64 /
|
||||||
u32,
|
self.window
|
||||||
|
.head()
|
||||||
|
.get_inner_size_points()
|
||||||
|
.unwrap_or((0, height))
|
||||||
|
.1 as f64) as u32,
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -416,7 +422,11 @@ impl TouchDownEvent for WinitTouchStartedEvent {
|
||||||
fn x_transformed(&self, width: u32) -> u32 {
|
fn x_transformed(&self, width: u32) -> u32 {
|
||||||
cmp::min(
|
cmp::min(
|
||||||
self.location.0 as i32 * width as i32 /
|
self.location.0 as i32 * width as i32 /
|
||||||
self.window.get_inner_size_points().unwrap_or((width, 0)).0 as i32,
|
self.window
|
||||||
|
.head()
|
||||||
|
.get_inner_size_points()
|
||||||
|
.unwrap_or((width, 0))
|
||||||
|
.0 as i32,
|
||||||
0,
|
0,
|
||||||
) as u32
|
) as u32
|
||||||
}
|
}
|
||||||
|
@ -424,7 +434,11 @@ impl TouchDownEvent for WinitTouchStartedEvent {
|
||||||
fn y_transformed(&self, height: u32) -> u32 {
|
fn y_transformed(&self, height: u32) -> u32 {
|
||||||
cmp::min(
|
cmp::min(
|
||||||
self.location.1 as i32 * height as i32 /
|
self.location.1 as i32 * height as i32 /
|
||||||
self.window.get_inner_size_points().unwrap_or((0, height)).1 as i32,
|
self.window
|
||||||
|
.head()
|
||||||
|
.get_inner_size_points()
|
||||||
|
.unwrap_or((0, height))
|
||||||
|
.1 as i32,
|
||||||
0,
|
0,
|
||||||
) as u32
|
) as u32
|
||||||
}
|
}
|
||||||
|
@ -459,11 +473,21 @@ impl TouchMotionEvent for WinitTouchMovedEvent {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn x_transformed(&self, width: u32) -> u32 {
|
fn x_transformed(&self, width: u32) -> u32 {
|
||||||
self.location.0 as u32 * width / self.window.get_inner_size_points().unwrap_or((width, 0)).0
|
self.location.0 as u32 * width /
|
||||||
|
self.window
|
||||||
|
.head()
|
||||||
|
.get_inner_size_points()
|
||||||
|
.unwrap_or((width, 0))
|
||||||
|
.0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn y_transformed(&self, height: u32) -> u32 {
|
fn y_transformed(&self, height: u32) -> u32 {
|
||||||
self.location.1 as u32 * height / self.window.get_inner_size_points().unwrap_or((0, height)).1
|
self.location.1 as u32 * height /
|
||||||
|
self.window
|
||||||
|
.head()
|
||||||
|
.get_inner_size_points()
|
||||||
|
.unwrap_or((0, height))
|
||||||
|
.1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -564,7 +588,7 @@ impl InputBackend for WinitInputBackend {
|
||||||
///
|
///
|
||||||
/// The linked `WinitGraphicsBackend` will error with a lost Context and should
|
/// The linked `WinitGraphicsBackend` will error with a lost Context and should
|
||||||
/// not be used anymore as well.
|
/// not be used anymore as well.
|
||||||
fn dispatch_new_events(&mut self) -> Result<(), WinitInputError> {
|
fn dispatch_new_events(&mut self) -> ::std::result::Result<(), WinitInputError> {
|
||||||
let mut closed = false;
|
let mut closed = false;
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -578,7 +602,6 @@ impl InputBackend for WinitInputBackend {
|
||||||
let mut time_counter = &mut self.time_counter;
|
let mut time_counter = &mut self.time_counter;
|
||||||
let seat = &self.seat;
|
let seat = &self.seat;
|
||||||
let window = &self.window;
|
let window = &self.window;
|
||||||
let surface = &self.surface;
|
|
||||||
let mut handler = self.handler.as_mut();
|
let mut handler = self.handler.as_mut();
|
||||||
let logger = &self.logger;
|
let logger = &self.logger;
|
||||||
|
|
||||||
|
@ -587,10 +610,12 @@ impl InputBackend for WinitInputBackend {
|
||||||
match (event, handler.as_mut()) {
|
match (event, handler.as_mut()) {
|
||||||
(WindowEvent::Resized(x, y), _) => {
|
(WindowEvent::Resized(x, y), _) => {
|
||||||
trace!(logger, "Resizing window to {:?}", (x, y));
|
trace!(logger, "Resizing window to {:?}", (x, y));
|
||||||
window.set_inner_size(x, y);
|
window.head().set_inner_size(x, y);
|
||||||
if let Some(wl_egl_surface) = surface.as_ref() {
|
window.rent(|egl| {
|
||||||
wl_egl_surface.resize(x as i32, y as i32, 0, 0);
|
egl.rent(|surface| if let Some(wegl_surface) = (**surface).as_ref() {
|
||||||
}
|
wegl_surface.resize(x as i32, y as i32, 0, 0)
|
||||||
|
})
|
||||||
|
});
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
WindowEvent::KeyboardInput {
|
WindowEvent::KeyboardInput {
|
||||||
|
@ -818,12 +843,3 @@ impl From<ElementState> for MouseButtonState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<WinitCreationError> for CreationError {
|
|
||||||
fn from(error: WinitCreationError) -> Self {
|
|
||||||
match error {
|
|
||||||
WinitCreationError::OsError(x) => CreationError::OsError(x),
|
|
||||||
WinitCreationError::NotSupported => CreationError::NotSupported,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -11,8 +11,9 @@ pub struct RegionData {
|
||||||
impl RegionData {
|
impl RegionData {
|
||||||
/// Initialize the user_data of a region, must be called right when the surface is created
|
/// Initialize the user_data of a region, must be called right when the surface is created
|
||||||
pub unsafe fn init(region: &wl_region::WlRegion) {
|
pub unsafe fn init(region: &wl_region::WlRegion) {
|
||||||
region.set_user_data(Box::into_raw(Box::new(Mutex::new(RegionData::default())))
|
region.set_user_data(
|
||||||
as *mut _)
|
Box::into_raw(Box::new(Mutex::new(RegionData::default()))) as *mut _,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cleans the user_data of that surface, must be called when it is destroyed
|
/// Cleans the user_data of that surface, must be called when it is destroyed
|
||||||
|
|
|
@ -21,14 +21,10 @@ use backend::input::KeyState;
|
||||||
use std::io::{Error as IoError, Write};
|
use std::io::{Error as IoError, Write};
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use tempfile::tempfile;
|
use tempfile::tempfile;
|
||||||
|
|
||||||
use wayland_server::{Liveness, Resource};
|
use wayland_server::{Liveness, Resource};
|
||||||
use wayland_server::protocol::{wl_keyboard, wl_surface};
|
use wayland_server::protocol::{wl_keyboard, wl_surface};
|
||||||
|
|
||||||
use xkbcommon::xkb;
|
use xkbcommon::xkb;
|
||||||
|
|
||||||
pub use xkbcommon::xkb::{keysyms, Keysym};
|
pub use xkbcommon::xkb::{keysyms, Keysym};
|
||||||
|
|
||||||
/// Represents the current state of the keyboard modifiers
|
/// Represents the current state of the keyboard modifiers
|
||||||
|
|
10
src/lib.rs
10
src/lib.rs
|
@ -6,7 +6,10 @@
|
||||||
|
|
||||||
#![cfg_attr(feature = "clippy", feature(plugin))]
|
#![cfg_attr(feature = "clippy", feature(plugin))]
|
||||||
#![cfg_attr(feature = "clippy", plugin(clippy))]
|
#![cfg_attr(feature = "clippy", plugin(clippy))]
|
||||||
|
// `error_chain!` can recurse deeply
|
||||||
|
#![recursion_limit = "1024"]
|
||||||
|
|
||||||
|
extern crate image;
|
||||||
extern crate nix;
|
extern crate nix;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate rental;
|
extern crate rental;
|
||||||
|
@ -15,6 +18,10 @@ extern crate wayland_protocols;
|
||||||
extern crate wayland_server;
|
extern crate wayland_server;
|
||||||
extern crate xkbcommon;
|
extern crate xkbcommon;
|
||||||
|
|
||||||
|
#[cfg(feature = "backend_drm")]
|
||||||
|
extern crate drm;
|
||||||
|
#[cfg(feature = "backend_drm")]
|
||||||
|
extern crate gbm;
|
||||||
#[cfg(feature = "backend_libinput")]
|
#[cfg(feature = "backend_libinput")]
|
||||||
extern crate input;
|
extern crate input;
|
||||||
#[cfg(feature = "backend_winit")]
|
#[cfg(feature = "backend_winit")]
|
||||||
|
@ -31,6 +38,9 @@ extern crate glium;
|
||||||
extern crate slog;
|
extern crate slog;
|
||||||
extern crate slog_stdlog;
|
extern crate slog_stdlog;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate error_chain;
|
||||||
|
|
||||||
pub mod backend;
|
pub mod backend;
|
||||||
pub mod compositor;
|
pub mod compositor;
|
||||||
pub mod shm;
|
pub mod shm;
|
||||||
|
|
|
@ -682,8 +682,8 @@ where
|
||||||
destroy: |evlh, idata, _, popup| {
|
destroy: |evlh, idata, _, popup| {
|
||||||
let ptr = popup.get_user_data();
|
let ptr = popup.get_user_data();
|
||||||
let &(ref surface, _, _) = unsafe {
|
let &(ref surface, _, _) = unsafe {
|
||||||
&*(ptr
|
&*(ptr as
|
||||||
as *mut (
|
*mut (
|
||||||
wl_surface::WlSurface,
|
wl_surface::WlSurface,
|
||||||
zxdg_shell_v6::ZxdgShellV6,
|
zxdg_shell_v6::ZxdgShellV6,
|
||||||
zxdg_surface_v6::ZxdgSurfaceV6,
|
zxdg_surface_v6::ZxdgSurfaceV6,
|
||||||
|
|
Loading…
Reference in New Issue