Merge pull request #57 from Smithay/feature/egl_buffer
Feature: EGL Buffer support / Fix #12
This commit is contained in:
commit
b483add6a5
|
@ -73,7 +73,6 @@ script:
|
||||||
vagga cargo-$TRAVIS_RUST_VERSION doc --no-deps
|
vagga cargo-$TRAVIS_RUST_VERSION doc --no-deps
|
||||||
;;
|
;;
|
||||||
"cargo-fmt")
|
"cargo-fmt")
|
||||||
vagga cargo-$TRAVIS_RUST_VERSION install -f rustfmt-nightly &&
|
|
||||||
vagga cargo-$TRAVIS_RUST_VERSION fmt -- --write-mode=diff
|
vagga cargo-$TRAVIS_RUST_VERSION fmt -- --write-mode=diff
|
||||||
;;
|
;;
|
||||||
"cargo-clippy")
|
"cargo-clippy")
|
||||||
|
|
25
Cargo.toml
25
Cargo.toml
|
@ -7,31 +7,32 @@ description = "Smithay is a library for writing wayland compositors."
|
||||||
repository = "https://github.com/Smithay/smithay"
|
repository = "https://github.com/Smithay/smithay"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
wayland-server = "0.12.0"
|
wayland-server = "0.12.5"
|
||||||
|
wayland-sys = "0.12.5"
|
||||||
nix = "0.9.0"
|
nix = "0.9.0"
|
||||||
xkbcommon = "0.2.1"
|
xkbcommon = "0.2.1"
|
||||||
tempfile = "2.1.5"
|
tempfile = "2.1.5"
|
||||||
slog = { version = "2.0.0" }
|
slog = { version = "2.1.1" }
|
||||||
slog-stdlog = "2.0.0-0.2"
|
slog-stdlog = "3.0.2"
|
||||||
libloading = "0.4.0"
|
libloading = "0.4.0"
|
||||||
wayland-client = { version = "0.9.9", optional = true }
|
wayland-client = { version = "0.12.5", optional = true }
|
||||||
winit = { version = "0.8.2", optional = true }
|
winit = { version = "0.10.0", optional = true }
|
||||||
drm = { version = "^0.3.1", optional = true }
|
drm = { version = "^0.3.1", optional = true }
|
||||||
gbm = { version = "^0.3.0", optional = true, default-features = false, features = ["drm-support"] }
|
gbm = { version = "^0.4.0", optional = true, default-features = false, features = ["drm-support"] }
|
||||||
glium = { version = "0.17.1", optional = true, default-features = false }
|
glium = { version = "0.19.0", optional = true, default-features = false }
|
||||||
input = { version = "0.4.0", optional = true }
|
input = { version = "0.4.0", optional = true }
|
||||||
udev = { version = "0.2.0", optional = true }
|
udev = { version = "0.2.0", optional = true }
|
||||||
rental = "0.4.11"
|
wayland-protocols = { version = "0.12.5", features = ["unstable_protocols", "server"] }
|
||||||
wayland-protocols = { version = "0.12.0", features = ["unstable_protocols", "server"] }
|
|
||||||
image = "0.17.0"
|
image = "0.17.0"
|
||||||
error-chain = "0.11.0"
|
error-chain = "0.11.0"
|
||||||
|
lazy_static = "1.0.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
gl_generator = "0.5"
|
gl_generator = "0.7"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
slog-term = "2.0"
|
slog-term = "2.3"
|
||||||
slog-async = "2.0"
|
slog-async = "2.2"
|
||||||
rand = "0.3"
|
rand = "0.3"
|
||||||
ctrlc = { version = "3.0", features = ["termination"] }
|
ctrlc = { version = "3.0", features = ["termination"] }
|
||||||
|
|
||||||
|
|
13
build.rs
13
build.rs
|
@ -29,7 +29,18 @@ fn main() {
|
||||||
"EGL_MESA_platform_gbm",
|
"EGL_MESA_platform_gbm",
|
||||||
"EGL_EXT_platform_wayland",
|
"EGL_EXT_platform_wayland",
|
||||||
"EGL_EXT_platform_device",
|
"EGL_EXT_platform_device",
|
||||||
|
"EGL_KHR_image_base",
|
||||||
],
|
],
|
||||||
).write_bindings(gl_generator::StructGenerator, &mut file)
|
).write_bindings(gl_generator::GlobalGenerator, &mut file)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut file = File::create(&dest.join("gl_bindings.rs")).unwrap();
|
||||||
|
Registry::new(
|
||||||
|
Api::Gles2,
|
||||||
|
(3, 2),
|
||||||
|
Profile::Compatibility,
|
||||||
|
Fallbacks::None,
|
||||||
|
["GL_OES_EGL_image"],
|
||||||
|
).write_bindings(gl_generator::GlobalGenerator, &mut file)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
125
examples/drm.rs
125
examples/drm.rs
|
@ -13,25 +13,39 @@ extern crate slog_term;
|
||||||
|
|
||||||
mod helpers;
|
mod helpers;
|
||||||
|
|
||||||
|
use drm::Device as BasicDevice;
|
||||||
use drm::control::{Device as ControlDevice, ResourceInfo};
|
use drm::control::{Device as ControlDevice, ResourceInfo};
|
||||||
use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState};
|
use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState};
|
||||||
use drm::control::crtc;
|
use drm::control::crtc;
|
||||||
use drm::control::encoder::Info as EncoderInfo;
|
use drm::control::encoder::Info as EncoderInfo;
|
||||||
use drm::result::Error as DrmError;
|
use drm::result::Error as DrmError;
|
||||||
use glium::Surface;
|
use glium::{Blend, Surface};
|
||||||
use helpers::{init_shell, GliumDrawer, MyWindowMap, Roles, SurfaceData};
|
use helpers::{init_shell, Buffer, GliumDrawer, MyWindowMap, Roles, SurfaceData};
|
||||||
use slog::{Drain, Logger};
|
use slog::{Drain, Logger};
|
||||||
use smithay::backend::drm::{drm_device_bind, DrmBackend, DrmDevice, DrmHandler};
|
use smithay::backend::drm::{drm_device_bind, DrmBackend, DrmDevice, DrmHandler};
|
||||||
use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
||||||
|
use smithay::backend::graphics::egl::wayland::{EGLDisplay, EGLWaylandExtensions, Format};
|
||||||
use smithay::wayland::compositor::{CompositorToken, SubsurfaceRole, TraversalAction};
|
use smithay::wayland::compositor::{CompositorToken, SubsurfaceRole, TraversalAction};
|
||||||
use smithay::wayland::compositor::roles::Role;
|
use smithay::wayland::compositor::roles::Role;
|
||||||
use smithay::wayland::shell::ShellState;
|
|
||||||
use smithay::wayland::shm::init_shm_global;
|
use smithay::wayland::shm::init_shm_global;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::fs::OpenOptions;
|
use std::fs::{File, OpenOptions};
|
||||||
|
use std::os::unix::io::AsRawFd;
|
||||||
|
use std::os::unix::io::RawFd;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use wayland_server::{StateToken, StateProxy};
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Card(File);
|
||||||
|
|
||||||
|
impl AsRawFd for Card {
|
||||||
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
|
self.0.as_raw_fd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BasicDevice for Card {}
|
||||||
|
impl ControlDevice for Card {}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// A logger facility, here we use the terminal for this example
|
// A logger facility, here we use the terminal for this example
|
||||||
|
@ -50,8 +64,10 @@ fn main() {
|
||||||
let mut options = OpenOptions::new();
|
let mut options = OpenOptions::new();
|
||||||
options.read(true);
|
options.read(true);
|
||||||
options.write(true);
|
options.write(true);
|
||||||
let mut device: DrmDevice<GliumDrawer<DrmBackend>> =
|
let mut device = DrmDevice::new(
|
||||||
DrmDevice::new_from_file(options.clone().open("/dev/dri/card0").unwrap(), log.clone()).unwrap();
|
Card(options.clone().open("/dev/dri/card0").unwrap()),
|
||||||
|
log.clone(),
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
// Get a set of all modesetting resource handles (excluding planes):
|
// Get a set of all modesetting resource handles (excluding planes):
|
||||||
let res_handles = device.resource_handles().unwrap();
|
let res_handles = device.resource_handles().unwrap();
|
||||||
|
@ -60,9 +76,7 @@ fn main() {
|
||||||
let connector_info = res_handles
|
let connector_info = res_handles
|
||||||
.connectors()
|
.connectors()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|conn| {
|
.map(|conn| ConnectorInfo::load_from_device(&device, *conn).unwrap())
|
||||||
ConnectorInfo::load_from_device(&device, *conn).unwrap()
|
|
||||||
})
|
|
||||||
.find(|conn| conn.connection_state() == ConnectorState::Connected)
|
.find(|conn| conn.connection_state() == ConnectorState::Connected)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -81,27 +95,38 @@ fn main() {
|
||||||
// Assuming we found a good connector and loaded the info into `connector_info`
|
// Assuming we found a good connector and loaded the info into `connector_info`
|
||||||
let mode = connector_info.modes()[0]; // Use first mode (usually highest resoltion, but in reality you should filter and sort and check and match with other connectors, if you use more then one.)
|
let mode = connector_info.modes()[0]; // Use first mode (usually highest resoltion, but in reality you should filter and sort and check and match with other connectors, if you use more then one.)
|
||||||
|
|
||||||
{
|
|
||||||
// Initialize the hardware backend
|
// Initialize the hardware backend
|
||||||
let renderer = device
|
let renderer = GliumDrawer::from(
|
||||||
.create_backend(event_loop.state(), crtc, mode, vec![connector_info.handle()])
|
device
|
||||||
.unwrap();
|
.create_backend(crtc, mode, vec![connector_info.handle()])
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
{
|
||||||
/*
|
/*
|
||||||
* Initialize glium
|
* Initialize glium
|
||||||
*/
|
*/
|
||||||
let mut frame = event_loop.state().get(renderer).draw();
|
let mut frame = renderer.draw();
|
||||||
frame.clear_color(0.8, 0.8, 0.9, 1.0);
|
frame.clear_color(0.8, 0.8, 0.9, 1.0);
|
||||||
frame.finish().unwrap();
|
frame.finish().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let egl_display = Rc::new(RefCell::new(
|
||||||
|
if let Ok(egl_display) = renderer.bind_wl_display(&display) {
|
||||||
|
info!(log, "EGL hardware-acceleration enabled");
|
||||||
|
Some(egl_display)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize the globals
|
* Initialize the globals
|
||||||
*/
|
*/
|
||||||
|
|
||||||
init_shm_global(&mut event_loop, vec![], log.clone());
|
init_shm_global(&mut event_loop, vec![], log.clone());
|
||||||
|
|
||||||
let (compositor_token, shell_state_token, window_map) = init_shell(&mut event_loop, log.clone());
|
let (compositor_token, _shell_state_token, window_map) =
|
||||||
|
init_shell(&mut event_loop, log.clone(), egl_display.clone());
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add a listening socket:
|
* Add a listening socket:
|
||||||
|
@ -117,9 +142,9 @@ fn main() {
|
||||||
&mut event_loop,
|
&mut event_loop,
|
||||||
device_token,
|
device_token,
|
||||||
DrmHandlerImpl {
|
DrmHandlerImpl {
|
||||||
shell_state_token,
|
|
||||||
compositor_token,
|
compositor_token,
|
||||||
window_map: window_map.clone(),
|
window_map: window_map.clone(),
|
||||||
|
drawer: renderer,
|
||||||
logger: log,
|
logger: log,
|
||||||
},
|
},
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
@ -133,22 +158,21 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DrmHandlerImpl {
|
pub struct DrmHandlerImpl {
|
||||||
shell_state_token: StateToken<ShellState<SurfaceData, Roles, (), ()>>,
|
compositor_token: CompositorToken<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>>,
|
||||||
compositor_token: CompositorToken<SurfaceData, Roles, ()>,
|
|
||||||
window_map: Rc<RefCell<MyWindowMap>>,
|
window_map: Rc<RefCell<MyWindowMap>>,
|
||||||
|
drawer: GliumDrawer<DrmBackend<Card>>,
|
||||||
logger: ::slog::Logger,
|
logger: ::slog::Logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DrmHandler<GliumDrawer<DrmBackend>> for DrmHandlerImpl {
|
impl DrmHandler<Card> for DrmHandlerImpl {
|
||||||
fn ready<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, _device: &mut DrmDevice<GliumDrawer<DrmBackend>>,
|
fn ready(
|
||||||
backend: &StateToken<GliumDrawer<DrmBackend>>, _crtc: crtc::Handle, _frame: u32, _duration: Duration) {
|
&mut self, _device: &mut DrmDevice<Card>, _crtc: crtc::Handle, _frame: u32, _duration: Duration
|
||||||
let state = state.into();
|
) {
|
||||||
let drawer = state.get(backend);
|
let mut frame = self.drawer.draw();
|
||||||
let mut frame = drawer.draw();
|
|
||||||
frame.clear_color(0.8, 0.8, 0.9, 1.0);
|
frame.clear_color(0.8, 0.8, 0.9, 1.0);
|
||||||
// redraw the frame, in a simple but inneficient way
|
// redraw the frame, in a simple but inneficient way
|
||||||
{
|
{
|
||||||
let screen_dimensions = drawer.get_framebuffer_dimensions();
|
let screen_dimensions = self.drawer.get_framebuffer_dimensions();
|
||||||
self.window_map
|
self.window_map
|
||||||
.borrow()
|
.borrow()
|
||||||
.with_windows_from_bottom_to_top(|toplevel_surface, initial_place| {
|
.with_windows_from_bottom_to_top(|toplevel_surface, initial_place| {
|
||||||
|
@ -159,18 +183,54 @@ impl DrmHandler<GliumDrawer<DrmBackend>> for DrmHandlerImpl {
|
||||||
wl_surface,
|
wl_surface,
|
||||||
initial_place,
|
initial_place,
|
||||||
|_surface, attributes, role, &(mut x, mut y)| {
|
|_surface, attributes, role, &(mut x, mut y)| {
|
||||||
if let Some((ref contents, (w, h))) = attributes.user_data.buffer {
|
|
||||||
// there is actually something to draw !
|
// there is actually something to draw !
|
||||||
|
if attributes.user_data.texture.is_none() {
|
||||||
|
let mut remove = false;
|
||||||
|
match attributes.user_data.buffer {
|
||||||
|
Some(Buffer::Egl { ref images }) => {
|
||||||
|
match images.format {
|
||||||
|
Format::RGB | Format::RGBA => {
|
||||||
|
attributes.user_data.texture =
|
||||||
|
self.drawer.texture_from_egl(&images);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// we don't handle the more complex formats here.
|
||||||
|
attributes.user_data.texture = None;
|
||||||
|
remove = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Some(Buffer::Shm { ref data, ref size }) => {
|
||||||
|
attributes.user_data.texture =
|
||||||
|
Some(self.drawer.texture_from_mem(data, *size));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
if remove {
|
||||||
|
attributes.user_data.buffer = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ref texture) = attributes.user_data.texture {
|
||||||
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
|
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
|
||||||
x += subdata.x;
|
x += subdata.x;
|
||||||
y += subdata.y;
|
y += subdata.y;
|
||||||
}
|
}
|
||||||
drawer.render(
|
info!(self.logger, "Render window");
|
||||||
|
self.drawer.render_texture(
|
||||||
&mut frame,
|
&mut frame,
|
||||||
contents,
|
texture,
|
||||||
(w, h),
|
match *attributes.user_data.buffer.as_ref().unwrap() {
|
||||||
|
Buffer::Egl { ref images } => images.y_inverted,
|
||||||
|
Buffer::Shm { .. } => false,
|
||||||
|
},
|
||||||
|
match *attributes.user_data.buffer.as_ref().unwrap() {
|
||||||
|
Buffer::Egl { ref images } => (images.width, images.height),
|
||||||
|
Buffer::Shm { ref size, .. } => *size,
|
||||||
|
},
|
||||||
(x, y),
|
(x, y),
|
||||||
screen_dimensions,
|
screen_dimensions,
|
||||||
|
Blend::alpha_blending(),
|
||||||
);
|
);
|
||||||
TraversalAction::DoChildren((x, y))
|
TraversalAction::DoChildren((x, y))
|
||||||
} else {
|
} else {
|
||||||
|
@ -186,8 +246,7 @@ impl DrmHandler<GliumDrawer<DrmBackend>> for DrmHandlerImpl {
|
||||||
frame.finish().unwrap();
|
frame.finish().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn error<'a, S: Into<StateProxy<'a>>>(&mut self, _state: S, _device: &mut DrmDevice<GliumDrawer<DrmBackend>>,
|
fn error(&mut self, _device: &mut DrmDevice<Card>, error: DrmError) {
|
||||||
error: DrmError) {
|
|
||||||
panic!("{:?}", error);
|
panic!("{:?}", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
use glium;
|
use glium;
|
||||||
use glium::{Frame, Surface};
|
use glium::{Frame, GlObject, Surface};
|
||||||
use glium::index::PrimitiveType;
|
use glium::index::PrimitiveType;
|
||||||
|
use glium::texture::{MipmapsOption, Texture2d, UncompressedFloatFormat};
|
||||||
use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
||||||
|
use smithay::backend::graphics::egl::error::Result as EGLResult;
|
||||||
|
use smithay::backend::graphics::egl::wayland::{EGLDisplay, EGLImages, EGLWaylandExtensions, Format};
|
||||||
use smithay::backend::graphics::glium::GliumGraphicsBackend;
|
use smithay::backend::graphics::glium::GliumGraphicsBackend;
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
use wayland_server::Display;
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
struct Vertex {
|
struct Vertex {
|
||||||
|
@ -106,21 +110,53 @@ impl<T: Into<GliumGraphicsBackend<T>> + EGLGraphicsBackend + 'static> From<T> fo
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: EGLGraphicsBackend + 'static> GliumDrawer<F> {
|
impl<F: EGLGraphicsBackend + 'static> GliumDrawer<F> {
|
||||||
pub fn render(&self, target: &mut glium::Frame, contents: &[u8], surface_dimensions: (u32, u32),
|
pub fn texture_from_mem(&self, contents: &[u8], surface_dimensions: (u32, u32)) -> Texture2d {
|
||||||
surface_location: (i32, i32), screen_size: (u32, u32)) {
|
|
||||||
let image = glium::texture::RawImage2d {
|
let image = glium::texture::RawImage2d {
|
||||||
data: contents.into(),
|
data: contents.into(),
|
||||||
width: surface_dimensions.0,
|
width: surface_dimensions.0,
|
||||||
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();
|
Texture2d::new(&self.display, image).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn texture_from_egl(&self, images: &EGLImages) -> Option<Texture2d> {
|
||||||
|
let format = match images.format {
|
||||||
|
Format::RGB => UncompressedFloatFormat::U8U8U8,
|
||||||
|
Format::RGBA => UncompressedFloatFormat::U8U8U8U8,
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let opengl_texture = Texture2d::empty_with_format(
|
||||||
|
&self.display,
|
||||||
|
format,
|
||||||
|
MipmapsOption::NoMipmap,
|
||||||
|
images.width,
|
||||||
|
images.height,
|
||||||
|
).unwrap();
|
||||||
|
unsafe {
|
||||||
|
images
|
||||||
|
.bind_to_texture(0, opengl_texture.get_id())
|
||||||
|
.expect("Failed to bind to texture");
|
||||||
|
}
|
||||||
|
Some(opengl_texture)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_texture(
|
||||||
|
&self, target: &mut glium::Frame, texture: &Texture2d, y_inverted: bool,
|
||||||
|
surface_dimensions: (u32, u32), surface_location: (i32, i32), screen_size: (u32, u32),
|
||||||
|
blending: glium::Blend,
|
||||||
|
) {
|
||||||
let xscale = 2.0 * (surface_dimensions.0 as f32) / (screen_size.0 as f32);
|
let xscale = 2.0 * (surface_dimensions.0 as f32) / (screen_size.0 as f32);
|
||||||
let yscale = -2.0 * (surface_dimensions.1 as f32) / (screen_size.1 as f32);
|
let mut yscale = -2.0 * (surface_dimensions.1 as f32) / (screen_size.1 as f32);
|
||||||
|
|
||||||
let x = 2.0 * (surface_location.0 as f32) / (screen_size.0 as f32) - 1.0;
|
let x = 2.0 * (surface_location.0 as f32) / (screen_size.0 as f32) - 1.0;
|
||||||
let y = 1.0 - 2.0 * (surface_location.1 as f32) / (screen_size.1 as f32);
|
let mut y = 1.0 - 2.0 * (surface_location.1 as f32) / (screen_size.1 as f32);
|
||||||
|
|
||||||
|
if y_inverted {
|
||||||
|
yscale = -yscale;
|
||||||
|
y -= surface_dimensions.1 as f32;
|
||||||
|
}
|
||||||
|
|
||||||
let uniforms = uniform! {
|
let uniforms = uniform! {
|
||||||
matrix: [
|
matrix: [
|
||||||
|
@ -129,7 +165,7 @@ impl<F: EGLGraphicsBackend + 'static> GliumDrawer<F> {
|
||||||
[ 0.0 , 0.0 , 1.0, 0.0],
|
[ 0.0 , 0.0 , 1.0, 0.0],
|
||||||
[ x , y , 0.0, 1.0]
|
[ x , y , 0.0, 1.0]
|
||||||
],
|
],
|
||||||
tex: &opengl_texture
|
tex: texture,
|
||||||
};
|
};
|
||||||
|
|
||||||
target
|
target
|
||||||
|
@ -138,7 +174,10 @@ impl<F: EGLGraphicsBackend + 'static> GliumDrawer<F> {
|
||||||
&self.index_buffer,
|
&self.index_buffer,
|
||||||
&self.program,
|
&self.program,
|
||||||
&uniforms,
|
&uniforms,
|
||||||
&Default::default(),
|
&glium::DrawParameters {
|
||||||
|
blend: blending,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -148,3 +187,9 @@ impl<F: EGLGraphicsBackend + 'static> GliumDrawer<F> {
|
||||||
self.display.draw()
|
self.display.draw()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<G: EGLWaylandExtensions + EGLGraphicsBackend + 'static> EGLWaylandExtensions for GliumDrawer<G> {
|
||||||
|
fn bind_wl_display(&self, display: &Display) -> EGLResult<EGLDisplay> {
|
||||||
|
self.display.bind_wl_display(display)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
use super::WindowMap;
|
use super::WindowMap;
|
||||||
|
use glium::texture::Texture2d;
|
||||||
use rand;
|
use rand;
|
||||||
|
use smithay::backend::graphics::egl::wayland::{BufferAccessError, Format};
|
||||||
|
use smithay::backend::graphics::egl::wayland::{EGLDisplay, EGLImages};
|
||||||
use smithay::wayland::compositor::{compositor_init, CompositorToken, SurfaceAttributes,
|
use smithay::wayland::compositor::{compositor_init, CompositorToken, SurfaceAttributes,
|
||||||
SurfaceUserImplementation};
|
SurfaceUserImplementation};
|
||||||
use smithay::wayland::shell::{shell_init, PopupConfigure, ShellState, ShellSurfaceRole,
|
use smithay::wayland::shell::{shell_init, PopupConfigure, ShellState, ShellSurfaceRole,
|
||||||
ShellSurfaceUserImplementation, ToplevelConfigure};
|
ShellSurfaceUserImplementation, ToplevelConfigure};
|
||||||
use smithay::wayland::shm::with_buffer_contents;
|
use smithay::wayland::shm::with_buffer_contents as shm_buffer_contents;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use wayland_server::{EventLoop, StateToken};
|
use wayland_server::{EventLoop, StateToken};
|
||||||
|
@ -13,18 +16,45 @@ define_roles!(Roles => [ ShellSurface, ShellSurfaceRole ] );
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct SurfaceData {
|
pub struct SurfaceData {
|
||||||
pub buffer: Option<(Vec<u8>, (u32, u32))>,
|
pub buffer: Option<Buffer>,
|
||||||
|
pub texture: Option<Texture2d>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn surface_implementation() -> SurfaceUserImplementation<SurfaceData, Roles, ()> {
|
pub enum Buffer {
|
||||||
|
Egl { images: EGLImages },
|
||||||
|
Shm { data: Vec<u8>, size: (u32, u32) },
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn surface_implementation(
|
||||||
|
) -> SurfaceUserImplementation<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>> {
|
||||||
SurfaceUserImplementation {
|
SurfaceUserImplementation {
|
||||||
commit: |_, _, surface, token| {
|
commit: |_, display, surface, token| {
|
||||||
// we retrieve the contents of the associated buffer and copy it
|
// we retrieve the contents of the associated buffer and copy it
|
||||||
token.with_surface_data(surface, |attributes| {
|
token.with_surface_data(surface, |attributes| {
|
||||||
match attributes.buffer.take() {
|
match attributes.buffer.take() {
|
||||||
Some(Some((buffer, (_x, _y)))) => {
|
Some(Some((buffer, (_x, _y)))) => {
|
||||||
// we ignore hotspot coordinates in this simple example
|
// we ignore hotspot coordinates in this simple example
|
||||||
with_buffer_contents(&buffer, |slice, data| {
|
match if let Some(display) = display.borrow().as_ref() {
|
||||||
|
display.egl_buffer_contents(buffer)
|
||||||
|
} else {
|
||||||
|
Err(BufferAccessError::NotManaged(buffer))
|
||||||
|
} {
|
||||||
|
Ok(images) => {
|
||||||
|
match images.format {
|
||||||
|
Format::RGB => {}
|
||||||
|
Format::RGBA => {}
|
||||||
|
_ => {
|
||||||
|
// we don't handle the more complex formats here.
|
||||||
|
attributes.user_data.buffer = None;
|
||||||
|
attributes.user_data.texture = None;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
attributes.user_data.texture = None;
|
||||||
|
attributes.user_data.buffer = Some(Buffer::Egl { images });
|
||||||
|
}
|
||||||
|
Err(BufferAccessError::NotManaged(buffer)) => {
|
||||||
|
shm_buffer_contents(&buffer, |slice, data| {
|
||||||
let offset = data.offset as usize;
|
let offset = data.offset as usize;
|
||||||
let stride = data.stride as usize;
|
let stride = data.stride as usize;
|
||||||
let width = data.width as usize;
|
let width = data.width as usize;
|
||||||
|
@ -34,14 +64,18 @@ pub fn surface_implementation() -> SurfaceUserImplementation<SurfaceData, Roles,
|
||||||
new_vec
|
new_vec
|
||||||
.extend(&slice[(offset + i * stride)..(offset + i * stride + width * 4)]);
|
.extend(&slice[(offset + i * stride)..(offset + i * stride + width * 4)]);
|
||||||
}
|
}
|
||||||
attributes.user_data.buffer =
|
attributes.user_data.texture = None;
|
||||||
Some((new_vec, (data.width as u32, data.height as u32)));
|
attributes.user_data.buffer = Some(Buffer::Shm { data: new_vec, size: (data.width as u32, data.height as u32) });
|
||||||
}).unwrap();
|
}).expect("Got EGL buffer with no set EGLDisplay. You need to unbind your EGLContexts before dropping them!");
|
||||||
buffer.release();
|
buffer.release();
|
||||||
}
|
}
|
||||||
|
Err(err) => panic!("EGL error: {}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
Some(None) => {
|
Some(None) => {
|
||||||
// erase the contents
|
// erase the contents
|
||||||
attributes.user_data.buffer = None;
|
attributes.user_data.buffer = None;
|
||||||
|
attributes.user_data.texture = None;
|
||||||
}
|
}
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
|
@ -54,11 +88,12 @@ pub fn surface_implementation() -> SurfaceUserImplementation<SurfaceData, Roles,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ShellIData<F> {
|
pub struct ShellIData<F> {
|
||||||
pub token: CompositorToken<SurfaceData, Roles, ()>,
|
pub token: CompositorToken<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>>,
|
||||||
pub window_map: Rc<RefCell<super::WindowMap<SurfaceData, Roles, (), (), F>>>,
|
pub window_map: Rc<RefCell<super::WindowMap<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>, (), F>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn shell_implementation<F>() -> ShellSurfaceUserImplementation<SurfaceData, Roles, (), ShellIData<F>, ()>
|
pub fn shell_implementation<F>(
|
||||||
|
) -> ShellSurfaceUserImplementation<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>, ShellIData<F>, ()>
|
||||||
where
|
where
|
||||||
F: Fn(&SurfaceAttributes<SurfaceData>) -> Option<(i32, i32)>,
|
F: Fn(&SurfaceAttributes<SurfaceData>) -> Option<(i32, i32)>,
|
||||||
{
|
{
|
||||||
|
@ -79,22 +114,18 @@ where
|
||||||
serial: 42,
|
serial: 42,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new_popup: |_, _, _| {
|
new_popup: |_, _, _| PopupConfigure {
|
||||||
PopupConfigure {
|
|
||||||
size: (10, 10),
|
size: (10, 10),
|
||||||
position: (10, 10),
|
position: (10, 10),
|
||||||
serial: 42,
|
serial: 42,
|
||||||
}
|
|
||||||
},
|
},
|
||||||
move_: |_, _, _, _, _| {},
|
move_: |_, _, _, _, _| {},
|
||||||
resize: |_, _, _, _, _, _| {},
|
resize: |_, _, _, _, _, _| {},
|
||||||
grab: |_, _, _, _, _| {},
|
grab: |_, _, _, _, _| {},
|
||||||
change_display_state: |_, _, _, _, _, _, _| {
|
change_display_state: |_, _, _, _, _, _, _| ToplevelConfigure {
|
||||||
ToplevelConfigure {
|
|
||||||
size: None,
|
size: None,
|
||||||
states: vec![],
|
states: vec![],
|
||||||
serial: 42,
|
serial: 42,
|
||||||
}
|
|
||||||
},
|
},
|
||||||
show_window_menu: |_, _, _, _, _, _, _| {},
|
show_window_menu: |_, _, _, _, _, _, _| {},
|
||||||
}
|
}
|
||||||
|
@ -105,25 +136,29 @@ fn get_size(attrs: &SurfaceAttributes<SurfaceData>) -> Option<(i32, i32)> {
|
||||||
.user_data
|
.user_data
|
||||||
.buffer
|
.buffer
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|&(_, (w, h))| (w as i32, h as i32))
|
.map(|ref buffer| match **buffer {
|
||||||
|
Buffer::Shm { ref size, .. } => *size,
|
||||||
|
Buffer::Egl { ref images } => (images.width, images.height),
|
||||||
|
})
|
||||||
|
.map(|(x, y)| (x as i32, y as i32))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type MyWindowMap = WindowMap<
|
pub type MyWindowMap = WindowMap<
|
||||||
SurfaceData,
|
SurfaceData,
|
||||||
Roles,
|
Roles,
|
||||||
(),
|
Rc<RefCell<Option<EGLDisplay>>>,
|
||||||
(),
|
(),
|
||||||
fn(&SurfaceAttributes<SurfaceData>) -> Option<(i32, i32)>,
|
fn(&SurfaceAttributes<SurfaceData>) -> Option<(i32, i32)>,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
pub fn init_shell(
|
pub fn init_shell(
|
||||||
evl: &mut EventLoop, log: ::slog::Logger)
|
evl: &mut EventLoop, log: ::slog::Logger, data: Rc<RefCell<Option<EGLDisplay>>>
|
||||||
-> (
|
) -> (
|
||||||
CompositorToken<SurfaceData, Roles, ()>,
|
CompositorToken<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>>,
|
||||||
StateToken<ShellState<SurfaceData, Roles, (), ()>>,
|
StateToken<ShellState<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>, ()>>,
|
||||||
Rc<RefCell<MyWindowMap>>,
|
Rc<RefCell<MyWindowMap>>,
|
||||||
) {
|
) {
|
||||||
let (compositor_token, _, _) = compositor_init(evl, surface_implementation(), (), log.clone());
|
let (compositor_token, _, _) = compositor_init(evl, surface_implementation(), data, log.clone());
|
||||||
|
|
||||||
let window_map = Rc::new(RefCell::new(WindowMap::<_, _, _, (), _>::new(
|
let window_map = Rc::new(RefCell::new(WindowMap::<_, _, _, (), _>::new(
|
||||||
compositor_token,
|
compositor_token,
|
||||||
|
|
|
@ -19,8 +19,9 @@ where
|
||||||
SD: 'static,
|
SD: 'static,
|
||||||
{
|
{
|
||||||
// Find the topmost surface under this point if any and the location of this point in the surface
|
// Find the topmost surface under this point if any and the location of this point in the surface
|
||||||
fn matching<F>(&self, point: (f64, f64), ctoken: CompositorToken<U, R, CID>, get_size: F)
|
fn matching<F>(
|
||||||
-> Option<(wl_surface::WlSurface, (f64, f64))>
|
&self, point: (f64, f64), ctoken: CompositorToken<U, R, CID>, get_size: F
|
||||||
|
) -> Option<(wl_surface::WlSurface, (f64, f64))>
|
||||||
where
|
where
|
||||||
F: Fn(&SurfaceAttributes<U>) -> Option<(i32, i32)>,
|
F: Fn(&SurfaceAttributes<U>) -> Option<(i32, i32)>,
|
||||||
{
|
{
|
||||||
|
@ -33,7 +34,8 @@ where
|
||||||
let _ = ctoken.with_surface_tree_downward(
|
let _ = ctoken.with_surface_tree_downward(
|
||||||
wl_surface,
|
wl_surface,
|
||||||
self.location,
|
self.location,
|
||||||
|wl_surface, attributes, role, &(mut x, mut y)| if let Some((w, h)) = get_size(attributes) {
|
|wl_surface, attributes, role, &(mut x, mut y)| {
|
||||||
|
if let Some((w, h)) = get_size(attributes) {
|
||||||
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
|
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
|
||||||
x += subdata.x;
|
x += subdata.x;
|
||||||
y += subdata.y;
|
y += subdata.y;
|
||||||
|
@ -45,15 +47,16 @@ where
|
||||||
height: h,
|
height: h,
|
||||||
};
|
};
|
||||||
if my_rect.contains((point.0 as i32, point.1 as i32)) {
|
if my_rect.contains((point.0 as i32, point.1 as i32)) {
|
||||||
found = wl_surface.clone().map(|s| {
|
found = wl_surface
|
||||||
(s, (point.0 - my_rect.x as f64, point.1 - my_rect.y as f64))
|
.clone()
|
||||||
});
|
.map(|s| (s, (point.0 - my_rect.x as f64, point.1 - my_rect.y as f64)));
|
||||||
TraversalAction::Break
|
TraversalAction::Break
|
||||||
} else {
|
} else {
|
||||||
TraversalAction::DoChildren((x, y))
|
TraversalAction::DoChildren((x, y))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
TraversalAction::SkipChildren
|
TraversalAction::SkipChildren
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -151,8 +154,9 @@ where
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_surface_and_bring_to_top(&mut self, point: (f64, f64))
|
pub fn get_surface_and_bring_to_top(
|
||||||
-> Option<(wl_surface::WlSurface, (f64, f64))> {
|
&mut self, point: (f64, f64)
|
||||||
|
) -> Option<(wl_surface::WlSurface, (f64, f64))> {
|
||||||
let mut found = None;
|
let mut found = None;
|
||||||
for (i, w) in self.windows.iter().enumerate() {
|
for (i, w) in self.windows.iter().enumerate() {
|
||||||
if let Some(surface) = w.matching(point, self.ctoken, &self.get_size) {
|
if let Some(surface) = w.matching(point, self.ctoken, &self.get_size) {
|
||||||
|
|
268
examples/udev.rs
268
examples/udev.rs
|
@ -1,14 +1,14 @@
|
||||||
extern crate drm;
|
extern crate drm;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate glium;
|
extern crate glium;
|
||||||
extern crate rand;
|
|
||||||
extern crate input as libinput;
|
|
||||||
extern crate image;
|
extern crate image;
|
||||||
extern crate udev;
|
extern crate input as libinput;
|
||||||
|
extern crate rand;
|
||||||
#[macro_use(define_roles)]
|
#[macro_use(define_roles)]
|
||||||
extern crate smithay;
|
extern crate smithay;
|
||||||
extern crate xkbcommon;
|
extern crate udev;
|
||||||
extern crate wayland_server;
|
extern crate wayland_server;
|
||||||
|
extern crate xkbcommon;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate slog;
|
extern crate slog;
|
||||||
|
@ -21,41 +21,43 @@ mod helpers;
|
||||||
|
|
||||||
use drm::control::{Device as ControlDevice, ResourceInfo};
|
use drm::control::{Device as ControlDevice, ResourceInfo};
|
||||||
use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState};
|
use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState};
|
||||||
use drm::control::encoder::Info as EncoderInfo;
|
|
||||||
use drm::control::crtc;
|
use drm::control::crtc;
|
||||||
|
use drm::control::encoder::Info as EncoderInfo;
|
||||||
use drm::result::Error as DrmError;
|
use drm::result::Error as DrmError;
|
||||||
use glium::Surface;
|
use glium::{Blend, Surface};
|
||||||
|
use helpers::{init_shell, Buffer, GliumDrawer, MyWindowMap, Roles, SurfaceData};
|
||||||
use image::{ImageBuffer, Rgba};
|
use image::{ImageBuffer, Rgba};
|
||||||
use libinput::{Libinput, Device as LibinputDevice, event};
|
use libinput::{event, Device as LibinputDevice, Libinput};
|
||||||
use libinput::event::keyboard::KeyboardEventTrait;
|
use libinput::event::keyboard::KeyboardEventTrait;
|
||||||
use helpers::{init_shell, GliumDrawer, MyWindowMap, Roles, SurfaceData};
|
|
||||||
use slog::{Drain, Logger};
|
use slog::{Drain, Logger};
|
||||||
use smithay::backend::drm::{DrmBackend, DrmDevice, DrmHandler};
|
use smithay::backend::drm::{DevPath, DrmBackend, DrmDevice, DrmHandler};
|
||||||
use smithay::backend::graphics::GraphicsBackend;
|
use smithay::backend::graphics::GraphicsBackend;
|
||||||
use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
||||||
use smithay::backend::input::{self, Event, InputBackend, InputHandler, KeyboardKeyEvent, PointerButtonEvent,
|
use smithay::backend::graphics::egl::wayland::{EGLDisplay, EGLWaylandExtensions, Format};
|
||||||
PointerAxisEvent, KeyState};
|
use smithay::backend::input::{self, Event, InputBackend, InputHandler, KeyState, KeyboardKeyEvent,
|
||||||
use smithay::backend::libinput::{LibinputInputBackend, libinput_bind, PointerAxisEvent as LibinputPointerAxisEvent, LibinputSessionInterface};
|
PointerAxisEvent, PointerButtonEvent};
|
||||||
use smithay::backend::udev::{UdevBackend, UdevHandler, udev_backend_bind};
|
use smithay::backend::libinput::{libinput_bind, LibinputInputBackend, LibinputSessionInterface,
|
||||||
|
PointerAxisEvent as LibinputPointerAxisEvent};
|
||||||
use smithay::backend::session::{Session, SessionNotifier};
|
use smithay::backend::session::{Session, SessionNotifier};
|
||||||
use smithay::backend::session::direct::{direct_session_bind, DirectSession};
|
use smithay::backend::session::direct::{direct_session_bind, DirectSession};
|
||||||
|
use smithay::backend::udev::{primary_gpu, udev_backend_bind, SessionFdDrmDevice, UdevBackend, UdevHandler};
|
||||||
use smithay::wayland::compositor::{CompositorToken, SubsurfaceRole, TraversalAction};
|
use smithay::wayland::compositor::{CompositorToken, SubsurfaceRole, TraversalAction};
|
||||||
use smithay::wayland::compositor::roles::Role;
|
use smithay::wayland::compositor::roles::Role;
|
||||||
use smithay::wayland::output::{Mode, Output, PhysicalProperties};
|
use smithay::wayland::output::{Mode, Output, PhysicalProperties};
|
||||||
use smithay::wayland::seat::{KeyboardHandle, PointerHandle, Seat};
|
use smithay::wayland::seat::{KeyboardHandle, PointerHandle, Seat};
|
||||||
use smithay::wayland::shell::ShellState;
|
|
||||||
use smithay::wayland::shm::init_shm_global;
|
use smithay::wayland::shm::init_shm_global;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashMap;
|
||||||
use std::io::Error as IoError;
|
use std::io::Error as IoError;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::process::Command;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::process::Command;
|
use wayland_server::{Display, StateProxy, StateToken};
|
||||||
use xkbcommon::xkb::keysyms as xkb;
|
|
||||||
use wayland_server::{StateToken, StateProxy};
|
|
||||||
use wayland_server::protocol::{wl_output, wl_pointer};
|
use wayland_server::protocol::{wl_output, wl_pointer};
|
||||||
|
use xkbcommon::xkb::keysyms as xkb;
|
||||||
|
|
||||||
struct LibinputInputHandler {
|
struct LibinputInputHandler {
|
||||||
log: Logger,
|
log: Logger,
|
||||||
|
@ -91,7 +93,8 @@ impl InputHandler<LibinputInputBackend> for LibinputInputHandler {
|
||||||
debug!(self.log, "key"; "keycode" => keycode, "state" => format!("{:?}", state));
|
debug!(self.log, "key"; "keycode" => keycode, "state" => format!("{:?}", state));
|
||||||
|
|
||||||
let serial = self.next_serial();
|
let serial = self.next_serial();
|
||||||
self.keyboard.input(keycode, state, serial, |modifiers, keysym| {
|
self.keyboard
|
||||||
|
.input(keycode, state, serial, |modifiers, keysym| {
|
||||||
if modifiers.ctrl && modifiers.alt && keysym == xkb::KEY_BackSpace {
|
if modifiers.ctrl && modifiers.alt && keysym == xkb::KEY_BackSpace {
|
||||||
self.running.store(false, Ordering::SeqCst);
|
self.running.store(false, Ordering::SeqCst);
|
||||||
false
|
false
|
||||||
|
@ -109,7 +112,9 @@ impl InputHandler<LibinputInputBackend> for LibinputInputHandler {
|
||||||
let mut location = self.pointer_location.borrow_mut();
|
let mut location = self.pointer_location.borrow_mut();
|
||||||
location.0 += x;
|
location.0 += x;
|
||||||
location.1 += y;
|
location.1 += y;
|
||||||
let under = self.window_map.borrow().get_surface_under((location.0, location.1));
|
let under = self.window_map
|
||||||
|
.borrow()
|
||||||
|
.get_surface_under((location.0, location.1));
|
||||||
self.pointer.motion(
|
self.pointer.motion(
|
||||||
under.as_ref().map(|&(ref s, (x, y))| (s, x, y)),
|
under.as_ref().map(|&(ref s, (x, y))| (s, x, y)),
|
||||||
serial,
|
serial,
|
||||||
|
@ -117,7 +122,10 @@ impl InputHandler<LibinputInputBackend> for LibinputInputHandler {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
fn on_pointer_move_absolute(&mut self, _: &input::Seat, evt: event::pointer::PointerMotionAbsoluteEvent) {
|
fn on_pointer_move_absolute(&mut self, _: &input::Seat, evt: event::pointer::PointerMotionAbsoluteEvent) {
|
||||||
let (x, y) = (evt.absolute_x_transformed(self.screen_size.0), evt.absolute_y_transformed(self.screen_size.1));
|
let (x, y) = (
|
||||||
|
evt.absolute_x_transformed(self.screen_size.0),
|
||||||
|
evt.absolute_y_transformed(self.screen_size.1),
|
||||||
|
);
|
||||||
*self.pointer_location.borrow_mut() = (x, y);
|
*self.pointer_location.borrow_mut() = (x, y);
|
||||||
let serial = self.next_serial();
|
let serial = self.next_serial();
|
||||||
let under = self.window_map.borrow().get_surface_under((x, y));
|
let under = self.window_map.borrow().get_surface_under((x, y));
|
||||||
|
@ -172,21 +180,33 @@ impl InputHandler<LibinputInputBackend> for LibinputInputHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
let active_egl_context = Rc::new(RefCell::new(None));
|
||||||
|
|
||||||
// A logger facility, here we use the terminal for this example
|
// A logger facility, here we use the terminal for this example
|
||||||
let log = Logger::root(
|
let log = Logger::root(
|
||||||
slog_term::FullFormat::new(slog_term::PlainSyncDecorator::new(std::io::stdout())).build().fuse(),
|
slog_term::FullFormat::new(slog_term::PlainSyncDecorator::new(std::io::stdout()))
|
||||||
|
.build()
|
||||||
|
.fuse(),
|
||||||
o!(),
|
o!(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Initialize the wayland server
|
// Initialize the wayland server
|
||||||
let (mut display, mut event_loop) = wayland_server::create_display();
|
let (mut display, mut event_loop) = wayland_server::create_display();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add a listening socket
|
||||||
|
*/
|
||||||
|
let name = display.add_socket_auto().unwrap().into_string().unwrap();
|
||||||
|
println!("Listening on socket: {}", name);
|
||||||
|
let display = Rc::new(display);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize the compositor
|
* Initialize the compositor
|
||||||
*/
|
*/
|
||||||
init_shm_global(&mut event_loop, vec![], log.clone());
|
init_shm_global(&mut event_loop, vec![], log.clone());
|
||||||
|
|
||||||
let (compositor_token, shell_state_token, window_map) = init_shell(&mut event_loop, log.clone());
|
let (compositor_token, _shell_state_token, window_map) =
|
||||||
|
init_shell(&mut event_loop, log.clone(), active_egl_context.clone());
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize session on the current tty
|
* Initialize session on the current tty
|
||||||
|
@ -206,16 +226,28 @@ fn main() {
|
||||||
* Initialize the udev backend
|
* Initialize the udev backend
|
||||||
*/
|
*/
|
||||||
let context = udev::Context::new().unwrap();
|
let context = udev::Context::new().unwrap();
|
||||||
|
let seat = session.seat();
|
||||||
|
|
||||||
|
let primary_gpu = primary_gpu(&context, &seat).unwrap_or_default();
|
||||||
|
|
||||||
let bytes = include_bytes!("resources/cursor2.rgba");
|
let bytes = include_bytes!("resources/cursor2.rgba");
|
||||||
let udev_token
|
let udev_token = UdevBackend::new(
|
||||||
= UdevBackend::new(&mut event_loop, &context, session.clone(), UdevHandlerImpl {
|
&mut event_loop,
|
||||||
shell_state_token,
|
&context,
|
||||||
|
session.clone(),
|
||||||
|
UdevHandlerImpl {
|
||||||
compositor_token,
|
compositor_token,
|
||||||
|
active_egl_context,
|
||||||
|
backends: HashMap::new(),
|
||||||
|
display: display.clone(),
|
||||||
|
primary_gpu,
|
||||||
window_map: window_map.clone(),
|
window_map: window_map.clone(),
|
||||||
pointer_location: pointer_location.clone(),
|
pointer_location: pointer_location.clone(),
|
||||||
pointer_image: ImageBuffer::from_raw(64, 64, bytes.to_vec()).unwrap(),
|
pointer_image: ImageBuffer::from_raw(64, 64, bytes.to_vec()).unwrap(),
|
||||||
logger: log.clone(),
|
logger: log.clone(),
|
||||||
}, log.clone()).unwrap();
|
},
|
||||||
|
log.clone(),
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
let udev_session_id = notifier.register(udev_token.clone());
|
let udev_session_id = notifier.register(udev_token.clone());
|
||||||
|
|
||||||
|
@ -266,8 +298,9 @@ fn main() {
|
||||||
/*
|
/*
|
||||||
* Initialize libinput backend
|
* Initialize libinput backend
|
||||||
*/
|
*/
|
||||||
let seat = session.seat();
|
let mut libinput_context = Libinput::new_from_udev::<
|
||||||
let mut libinput_context = Libinput::new_from_udev::<LibinputSessionInterface<Rc<RefCell<DirectSession>>>>(session.into(), &context);
|
LibinputSessionInterface<Rc<RefCell<DirectSession>>>,
|
||||||
|
>(session.into(), &context);
|
||||||
let libinput_session_id = notifier.register(libinput_context.clone());
|
let libinput_session_id = notifier.register(libinput_context.clone());
|
||||||
libinput_context.udev_assign_seat(&seat).unwrap();
|
libinput_context.udev_assign_seat(&seat).unwrap();
|
||||||
let mut libinput_backend = LibinputInputBackend::new(libinput_context, log.clone());
|
let mut libinput_backend = LibinputInputBackend::new(libinput_context, log.clone());
|
||||||
|
@ -286,17 +319,14 @@ fn main() {
|
||||||
let session_event_source = direct_session_bind(notifier, &mut event_loop, log.clone()).unwrap();
|
let session_event_source = direct_session_bind(notifier, &mut event_loop, log.clone()).unwrap();
|
||||||
let udev_event_source = udev_backend_bind(&mut event_loop, udev_token).unwrap();
|
let udev_event_source = udev_backend_bind(&mut event_loop, udev_token).unwrap();
|
||||||
|
|
||||||
/*
|
|
||||||
* Add a listening socket
|
|
||||||
*/
|
|
||||||
let name = display.add_socket_auto().unwrap().into_string().unwrap();
|
|
||||||
println!("Listening on socket: {}", name);
|
|
||||||
|
|
||||||
while running.load(Ordering::SeqCst) {
|
while running.load(Ordering::SeqCst) {
|
||||||
event_loop.dispatch(Some(16));
|
if let Err(_) = event_loop.dispatch(Some(16)) {
|
||||||
|
running.store(false, Ordering::SeqCst);
|
||||||
|
} else {
|
||||||
display.flush_clients();
|
display.flush_clients();
|
||||||
window_map.borrow_mut().refresh();
|
window_map.borrow_mut().refresh();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
println!("Bye Bye");
|
println!("Bye Bye");
|
||||||
|
|
||||||
|
@ -312,8 +342,11 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct UdevHandlerImpl {
|
struct UdevHandlerImpl {
|
||||||
shell_state_token: StateToken<ShellState<SurfaceData, Roles, (), ()>>,
|
compositor_token: CompositorToken<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>>,
|
||||||
compositor_token: CompositorToken<SurfaceData, Roles, ()>,
|
active_egl_context: Rc<RefCell<Option<EGLDisplay>>>,
|
||||||
|
backends: HashMap<u64, Rc<RefCell<HashMap<crtc::Handle, GliumDrawer<DrmBackend<SessionFdDrmDevice>>>>>>,
|
||||||
|
display: Rc<Display>,
|
||||||
|
primary_gpu: Option<PathBuf>,
|
||||||
window_map: Rc<RefCell<MyWindowMap>>,
|
window_map: Rc<RefCell<MyWindowMap>>,
|
||||||
pointer_location: Rc<RefCell<(f64, f64)>>,
|
pointer_location: Rc<RefCell<(f64, f64)>>,
|
||||||
pointer_image: ImageBuffer<Rgba<u8>, Vec<u8>>,
|
pointer_image: ImageBuffer<Rgba<u8>, Vec<u8>>,
|
||||||
|
@ -321,7 +354,9 @@ struct UdevHandlerImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UdevHandlerImpl {
|
impl UdevHandlerImpl {
|
||||||
pub fn scan_connectors<'a, S: Into<StateProxy<'a>>>(&self, state: S, device: &mut DrmDevice<GliumDrawer<DrmBackend>>) {
|
pub fn scan_connectors(
|
||||||
|
&self, device: &mut DrmDevice<SessionFdDrmDevice>
|
||||||
|
) -> HashMap<crtc::Handle, GliumDrawer<DrmBackend<SessionFdDrmDevice>>> {
|
||||||
// Get a set of all modesetting resource handles (excluding planes):
|
// Get a set of all modesetting resource handles (excluding planes):
|
||||||
let res_handles = device.resource_handles().unwrap();
|
let res_handles = device.resource_handles().unwrap();
|
||||||
|
|
||||||
|
@ -329,79 +364,97 @@ impl UdevHandlerImpl {
|
||||||
let connector_infos: Vec<ConnectorInfo> = res_handles
|
let connector_infos: Vec<ConnectorInfo> = res_handles
|
||||||
.connectors()
|
.connectors()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|conn| {
|
.map(|conn| ConnectorInfo::load_from_device(device, *conn).unwrap())
|
||||||
ConnectorInfo::load_from_device(device, *conn).unwrap()
|
|
||||||
})
|
|
||||||
.filter(|conn| conn.connection_state() == ConnectorState::Connected)
|
.filter(|conn| conn.connection_state() == ConnectorState::Connected)
|
||||||
.inspect(|conn| info!(self.logger, "Connected: {:?}", conn.connector_type()))
|
.inspect(|conn| info!(self.logger, "Connected: {:?}", conn.connector_type()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut used_crtcs: HashSet<crtc::Handle> = HashSet::new();
|
let mut backends = HashMap::new();
|
||||||
|
|
||||||
let mut state = state.into();
|
|
||||||
|
|
||||||
// very naive way of finding good crtc/encoder/connector combinations. This problem is np-complete
|
// very naive way of finding good crtc/encoder/connector combinations. This problem is np-complete
|
||||||
for connector_info in connector_infos {
|
for connector_info in connector_infos {
|
||||||
let encoder_infos = connector_info.encoders().iter().flat_map(|encoder_handle| EncoderInfo::load_from_device(device, *encoder_handle)).collect::<Vec<EncoderInfo>>();
|
let encoder_infos = connector_info
|
||||||
|
.encoders()
|
||||||
|
.iter()
|
||||||
|
.flat_map(|encoder_handle| EncoderInfo::load_from_device(device, *encoder_handle))
|
||||||
|
.collect::<Vec<EncoderInfo>>();
|
||||||
for encoder_info in encoder_infos {
|
for encoder_info in encoder_infos {
|
||||||
for crtc in res_handles.filter_crtcs(encoder_info.possible_crtcs()) {
|
for crtc in res_handles.filter_crtcs(encoder_info.possible_crtcs()) {
|
||||||
if !used_crtcs.contains(&crtc) {
|
if !backends.contains_key(&crtc) {
|
||||||
let mode = connector_info.modes()[0]; // Use first mode (usually highest resoltion, but in reality you should filter and sort and check and match with other connectors, if you use more then one.)
|
let mode = connector_info.modes()[0]; // Use first mode (usually highest resoltion, but in reality you should filter and sort and check and match with other connectors, if you use more then one.)
|
||||||
// create a backend
|
// create a backend
|
||||||
let renderer_token = device.create_backend(&mut state, crtc, mode, vec![connector_info.handle()]).unwrap();
|
let renderer = GliumDrawer::from(
|
||||||
|
device
|
||||||
|
.create_backend(crtc, mode, vec![connector_info.handle()])
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
// create cursor
|
// create cursor
|
||||||
{
|
renderer
|
||||||
let renderer = state.get_mut(renderer_token);
|
.set_cursor_representation(&self.pointer_image, (2, 2))
|
||||||
renderer.set_cursor_representation(&self.pointer_image, (2, 2)).unwrap();
|
.unwrap();
|
||||||
}
|
|
||||||
|
|
||||||
// render first frame
|
// render first frame
|
||||||
{
|
{
|
||||||
let renderer = state.get_mut(renderer_token);
|
|
||||||
let mut frame = renderer.draw();
|
let mut frame = renderer.draw();
|
||||||
frame.clear_color(0.8, 0.8, 0.9, 1.0);
|
frame.clear_color(0.8, 0.8, 0.9, 1.0);
|
||||||
frame.finish().unwrap();
|
frame.finish().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
used_crtcs.insert(crtc);
|
backends.insert(crtc, renderer);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
backends
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UdevHandler<GliumDrawer<DrmBackend>, DrmHandlerImpl> for UdevHandlerImpl {
|
impl UdevHandler<DrmHandlerImpl> for UdevHandlerImpl {
|
||||||
fn device_added<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &mut DrmDevice<GliumDrawer<DrmBackend>>) -> Option<DrmHandlerImpl>
|
fn device_added<'a, S: Into<StateProxy<'a>>>(
|
||||||
{
|
&mut self, _state: S, device: &mut DrmDevice<SessionFdDrmDevice>
|
||||||
self.scan_connectors(state, device);
|
) -> Option<DrmHandlerImpl> {
|
||||||
|
// init hardware acceleration on the primary gpu.
|
||||||
|
if device.dev_path().and_then(|path| path.canonicalize().ok()) == self.primary_gpu {
|
||||||
|
*self.active_egl_context.borrow_mut() = device.bind_wl_display(&*self.display).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
let backends = Rc::new(RefCell::new(self.scan_connectors(device)));
|
||||||
|
self.backends.insert(device.device_id(), backends.clone());
|
||||||
|
|
||||||
Some(DrmHandlerImpl {
|
Some(DrmHandlerImpl {
|
||||||
shell_state_token: self.shell_state_token.clone(),
|
|
||||||
compositor_token: self.compositor_token.clone(),
|
compositor_token: self.compositor_token.clone(),
|
||||||
|
backends,
|
||||||
window_map: self.window_map.clone(),
|
window_map: self.window_map.clone(),
|
||||||
pointer_location: self.pointer_location.clone(),
|
pointer_location: self.pointer_location.clone(),
|
||||||
logger: self.logger.clone(),
|
logger: self.logger.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn device_changed<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &StateToken<DrmDevice<GliumDrawer<DrmBackend>>>) {
|
fn device_changed<'a, S: Into<StateProxy<'a>>>(
|
||||||
//quick and dirt, just re-init the device
|
&mut self, state: S, device: &StateToken<DrmDevice<SessionFdDrmDevice>>
|
||||||
|
) {
|
||||||
|
//quick and dirt, just re-init all backends
|
||||||
let mut state = state.into();
|
let mut state = state.into();
|
||||||
self.device_removed(&mut state, device);
|
let backends = self.backends.get(&state.get(device).device_id()).unwrap();
|
||||||
state.with_value(device, |state, device| self.scan_connectors(state, device));
|
*backends.borrow_mut() = self.scan_connectors(state.get_mut(device));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn device_removed<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &StateToken<DrmDevice<GliumDrawer<DrmBackend>>>) {
|
fn device_removed<'a, S: Into<StateProxy<'a>>>(
|
||||||
state.into().with_value(device, |state, device| {
|
&mut self, state: S, device: &StateToken<DrmDevice<SessionFdDrmDevice>>
|
||||||
let crtcs = device.current_backends().into_iter().map(|backend| state.get(backend).crtc()).collect::<Vec<crtc::Handle>>();
|
) {
|
||||||
let mut state: StateProxy = state.into();
|
let state = state.into();
|
||||||
for crtc in crtcs {
|
let device = state.get(device);
|
||||||
device.destroy_backend(&mut state, &crtc);
|
|
||||||
|
// drop the backends on this side
|
||||||
|
self.backends.remove(&device.device_id());
|
||||||
|
|
||||||
|
// don't use hardware acceleration anymore, if this was the primary gpu
|
||||||
|
if device.dev_path().and_then(|path| path.canonicalize().ok()) == self.primary_gpu {
|
||||||
|
*self.active_egl_context.borrow_mut() = None;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn error<'a, S: Into<StateProxy<'a>>>(&mut self, _state: S, error: IoError) {
|
fn error<'a, S: Into<StateProxy<'a>>>(&mut self, _state: S, error: IoError) {
|
||||||
|
@ -410,30 +463,30 @@ impl UdevHandler<GliumDrawer<DrmBackend>, DrmHandlerImpl> for UdevHandlerImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DrmHandlerImpl {
|
pub struct DrmHandlerImpl {
|
||||||
shell_state_token: StateToken<ShellState<SurfaceData, Roles, (), ()>>,
|
compositor_token: CompositorToken<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>>,
|
||||||
compositor_token: CompositorToken<SurfaceData, Roles, ()>,
|
backends: Rc<RefCell<HashMap<crtc::Handle, GliumDrawer<DrmBackend<SessionFdDrmDevice>>>>>,
|
||||||
window_map: Rc<RefCell<MyWindowMap>>,
|
window_map: Rc<RefCell<MyWindowMap>>,
|
||||||
pointer_location: Rc<RefCell<(f64, f64)>>,
|
pointer_location: Rc<RefCell<(f64, f64)>>,
|
||||||
logger: ::slog::Logger,
|
logger: ::slog::Logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DrmHandler<GliumDrawer<DrmBackend>> for DrmHandlerImpl {
|
impl DrmHandler<SessionFdDrmDevice> for DrmHandlerImpl {
|
||||||
fn ready<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, _device: &mut DrmDevice<GliumDrawer<DrmBackend>>,
|
fn ready(
|
||||||
backend: &StateToken<GliumDrawer<DrmBackend>>, _crtc: crtc::Handle, _frame: u32, _duration: Duration) {
|
&mut self, _device: &mut DrmDevice<SessionFdDrmDevice>, crtc: crtc::Handle, _frame: u32,
|
||||||
let state = state.into();
|
_duration: Duration,
|
||||||
let drawer = state.get(backend);
|
) {
|
||||||
|
if let Some(drawer) = self.backends.borrow().get(&crtc) {
|
||||||
{
|
{
|
||||||
let (x, y) = *self.pointer_location.borrow();
|
let (x, y) = *self.pointer_location.borrow();
|
||||||
let _ = (**drawer).set_cursor_position(x.trunc().abs() as u32, y.trunc().abs() as u32);
|
let _ = drawer.set_cursor_position(x.trunc().abs() as u32, y.trunc().abs() as u32);
|
||||||
}
|
}
|
||||||
let mut frame = drawer.draw();
|
let mut frame = drawer.draw();
|
||||||
frame.clear_color(0.8, 0.8, 0.9, 1.0);
|
frame.clear_color(0.8, 0.8, 0.9, 1.0);
|
||||||
// redraw the frame, in a simple but inneficient way
|
// redraw the frame, in a simple but inneficient way
|
||||||
{
|
{
|
||||||
let screen_dimensions = drawer.get_framebuffer_dimensions();
|
let screen_dimensions = drawer.get_framebuffer_dimensions();
|
||||||
self.window_map
|
self.window_map.borrow().with_windows_from_bottom_to_top(
|
||||||
.borrow()
|
|toplevel_surface, initial_place| {
|
||||||
.with_windows_from_bottom_to_top(|toplevel_surface, initial_place| {
|
|
||||||
if let Some(wl_surface) = toplevel_surface.get_surface() {
|
if let Some(wl_surface) = toplevel_surface.get_surface() {
|
||||||
// this surface is a root of a subsurface tree that needs to be drawn
|
// this surface is a root of a subsurface tree that needs to be drawn
|
||||||
self.compositor_token
|
self.compositor_token
|
||||||
|
@ -441,18 +494,56 @@ impl DrmHandler<GliumDrawer<DrmBackend>> for DrmHandlerImpl {
|
||||||
wl_surface,
|
wl_surface,
|
||||||
initial_place,
|
initial_place,
|
||||||
|_surface, attributes, role, &(mut x, mut y)| {
|
|_surface, attributes, role, &(mut x, mut y)| {
|
||||||
if let Some((ref contents, (w, h))) = attributes.user_data.buffer {
|
|
||||||
// there is actually something to draw !
|
// there is actually something to draw !
|
||||||
|
if attributes.user_data.texture.is_none() {
|
||||||
|
let mut remove = false;
|
||||||
|
match attributes.user_data.buffer {
|
||||||
|
Some(Buffer::Egl { ref images }) => {
|
||||||
|
match images.format {
|
||||||
|
Format::RGB | Format::RGBA => {
|
||||||
|
attributes.user_data.texture =
|
||||||
|
drawer.texture_from_egl(&images);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// we don't handle the more complex formats here.
|
||||||
|
attributes.user_data.texture = None;
|
||||||
|
remove = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Some(Buffer::Shm { ref data, ref size }) => {
|
||||||
|
attributes.user_data.texture =
|
||||||
|
Some(drawer.texture_from_mem(data, *size));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
if remove {
|
||||||
|
attributes.user_data.buffer = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ref texture) = attributes.user_data.texture {
|
||||||
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
|
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
|
||||||
x += subdata.x;
|
x += subdata.x;
|
||||||
y += subdata.y;
|
y += subdata.y;
|
||||||
}
|
}
|
||||||
drawer.render(
|
info!(self.logger, "Render window");
|
||||||
|
drawer.render_texture(
|
||||||
&mut frame,
|
&mut frame,
|
||||||
contents,
|
texture,
|
||||||
(w, h),
|
match *attributes.user_data.buffer.as_ref().unwrap() {
|
||||||
|
Buffer::Egl { ref images } => images.y_inverted,
|
||||||
|
Buffer::Shm { .. } => false,
|
||||||
|
},
|
||||||
|
match *attributes.user_data.buffer.as_ref().unwrap() {
|
||||||
|
Buffer::Egl { ref images } => {
|
||||||
|
(images.width, images.height)
|
||||||
|
}
|
||||||
|
Buffer::Shm { ref size, .. } => *size,
|
||||||
|
},
|
||||||
(x, y),
|
(x, y),
|
||||||
screen_dimensions,
|
screen_dimensions,
|
||||||
|
Blend::alpha_blending(),
|
||||||
);
|
);
|
||||||
TraversalAction::DoChildren((x, y))
|
TraversalAction::DoChildren((x, y))
|
||||||
} else {
|
} else {
|
||||||
|
@ -463,15 +554,16 @@ impl DrmHandler<GliumDrawer<DrmBackend>> for DrmHandlerImpl {
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if let Err(err) = frame.finish() {
|
if let Err(err) = frame.finish() {
|
||||||
error!(self.logger, "Error during rendering: {:?}", err);
|
error!(self.logger, "Error during rendering: {:?}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn error<'a, S: Into<StateProxy<'a>>>(&mut self, _state: S, _device: &mut DrmDevice<GliumDrawer<DrmBackend>>,
|
fn error(&mut self, _device: &mut DrmDevice<SessionFdDrmDevice>, error: DrmError) {
|
||||||
error: DrmError) {
|
|
||||||
error!(self.logger, "{:?}", error);
|
error!(self.logger, "{:?}", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,9 +12,10 @@ extern crate wayland_server;
|
||||||
mod helpers;
|
mod helpers;
|
||||||
|
|
||||||
use glium::Surface;
|
use glium::Surface;
|
||||||
use helpers::{init_shell, GliumDrawer, MyWindowMap};
|
use helpers::{init_shell, Buffer, GliumDrawer, MyWindowMap};
|
||||||
use slog::{Drain, Logger};
|
use slog::{Drain, Logger};
|
||||||
use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
||||||
|
use smithay::backend::graphics::egl::wayland::{EGLWaylandExtensions, Format};
|
||||||
use smithay::backend::input::{self, Event, InputBackend, InputHandler, KeyboardKeyEvent, PointerAxisEvent,
|
use smithay::backend::input::{self, Event, InputBackend, InputHandler, KeyboardKeyEvent, PointerAxisEvent,
|
||||||
PointerButtonEvent, PointerMotionAbsoluteEvent};
|
PointerButtonEvent, PointerMotionAbsoluteEvent};
|
||||||
use smithay::backend::winit;
|
use smithay::backend::winit;
|
||||||
|
@ -136,13 +137,26 @@ fn main() {
|
||||||
|
|
||||||
let (mut display, mut event_loop) = wayland_server::create_display();
|
let (mut display, mut event_loop) = wayland_server::create_display();
|
||||||
|
|
||||||
|
let egl_display = Rc::new(RefCell::new(
|
||||||
|
if let Ok(egl_display) = renderer.bind_wl_display(&display) {
|
||||||
|
info!(log, "EGL hardware-acceleration enabled");
|
||||||
|
Some(egl_display)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
|
let (w, h) = renderer.get_framebuffer_dimensions();
|
||||||
|
let drawer = GliumDrawer::from(renderer);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize the globals
|
* Initialize the globals
|
||||||
*/
|
*/
|
||||||
|
|
||||||
init_shm_global(&mut event_loop, vec![], log.clone());
|
init_shm_global(&mut event_loop, vec![], log.clone());
|
||||||
|
|
||||||
let (compositor_token, _shell_state_token, window_map) = init_shell(&mut event_loop, log.clone());
|
let (compositor_token, _shell_state_token, window_map) =
|
||||||
|
init_shell(&mut event_loop, log.clone(), egl_display);
|
||||||
|
|
||||||
let (seat_token, _) = Seat::new(&mut event_loop, "winit".into(), log.clone());
|
let (seat_token, _) = Seat::new(&mut event_loop, "winit".into(), log.clone());
|
||||||
|
|
||||||
|
@ -166,7 +180,6 @@ fn main() {
|
||||||
log.clone(),
|
log.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (w, h) = renderer.get_framebuffer_dimensions();
|
|
||||||
event_loop
|
event_loop
|
||||||
.state()
|
.state()
|
||||||
.get_mut(&output_token)
|
.get_mut(&output_token)
|
||||||
|
@ -188,11 +201,6 @@ fn main() {
|
||||||
refresh: 60_000,
|
refresh: 60_000,
|
||||||
});
|
});
|
||||||
|
|
||||||
/*
|
|
||||||
* Initialize glium
|
|
||||||
*/
|
|
||||||
let drawer = GliumDrawer::from(renderer);
|
|
||||||
|
|
||||||
input.set_handler(WinitInputHandler {
|
input.set_handler(WinitInputHandler {
|
||||||
log: log.clone(),
|
log: log.clone(),
|
||||||
pointer,
|
pointer,
|
||||||
|
@ -212,7 +220,7 @@ fn main() {
|
||||||
input.dispatch_new_events().unwrap();
|
input.dispatch_new_events().unwrap();
|
||||||
|
|
||||||
let mut frame = drawer.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, Some(1.0), None);
|
||||||
// redraw the frame, in a simple but inneficient way
|
// redraw the frame, in a simple but inneficient way
|
||||||
{
|
{
|
||||||
let screen_dimensions = drawer.get_framebuffer_dimensions();
|
let screen_dimensions = drawer.get_framebuffer_dimensions();
|
||||||
|
@ -226,18 +234,65 @@ fn main() {
|
||||||
wl_surface,
|
wl_surface,
|
||||||
initial_place,
|
initial_place,
|
||||||
|_surface, attributes, role, &(mut x, mut y)| {
|
|_surface, attributes, role, &(mut x, mut y)| {
|
||||||
if let Some((ref contents, (w, h))) = attributes.user_data.buffer {
|
|
||||||
// there is actually something to draw !
|
// there is actually something to draw !
|
||||||
|
if attributes.user_data.texture.is_none() {
|
||||||
|
let mut remove = false;
|
||||||
|
match attributes.user_data.buffer {
|
||||||
|
Some(Buffer::Egl { ref images }) => {
|
||||||
|
match images.format {
|
||||||
|
Format::RGB | Format::RGBA => {
|
||||||
|
attributes.user_data.texture =
|
||||||
|
drawer.texture_from_egl(&images);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// we don't handle the more complex formats here.
|
||||||
|
attributes.user_data.texture = None;
|
||||||
|
remove = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Some(Buffer::Shm { ref data, ref size }) => {
|
||||||
|
attributes.user_data.texture =
|
||||||
|
Some(drawer.texture_from_mem(data, *size));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
if remove {
|
||||||
|
attributes.user_data.buffer = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ref texture) = attributes.user_data.texture {
|
||||||
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
|
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
|
||||||
x += subdata.x;
|
x += subdata.x;
|
||||||
y += subdata.y;
|
y += subdata.y;
|
||||||
}
|
}
|
||||||
drawer.render(
|
drawer.render_texture(
|
||||||
&mut frame,
|
&mut frame,
|
||||||
contents,
|
texture,
|
||||||
(w, h),
|
match *attributes.user_data.buffer.as_ref().unwrap() {
|
||||||
|
Buffer::Egl { ref images } => images.y_inverted,
|
||||||
|
Buffer::Shm { .. } => false,
|
||||||
|
},
|
||||||
|
match *attributes.user_data.buffer.as_ref().unwrap() {
|
||||||
|
Buffer::Egl { ref images } => (images.width, images.height),
|
||||||
|
Buffer::Shm { ref size, .. } => *size,
|
||||||
|
},
|
||||||
(x, y),
|
(x, y),
|
||||||
screen_dimensions,
|
screen_dimensions,
|
||||||
|
glium::Blend {
|
||||||
|
color: glium::BlendingFunction::Addition {
|
||||||
|
source: glium::LinearBlendingFactor::One,
|
||||||
|
destination:
|
||||||
|
glium::LinearBlendingFactor::OneMinusSourceAlpha,
|
||||||
|
},
|
||||||
|
alpha: glium::BlendingFunction::Addition {
|
||||||
|
source: glium::LinearBlendingFactor::One,
|
||||||
|
destination:
|
||||||
|
glium::LinearBlendingFactor::OneMinusSourceAlpha,
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
);
|
);
|
||||||
TraversalAction::DoChildren((x, y))
|
TraversalAction::DoChildren((x, y))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,129 +1,65 @@
|
||||||
use super::devices;
|
use super::DevPath;
|
||||||
use super::error::*;
|
use super::error::*;
|
||||||
use backend::graphics::GraphicsBackend;
|
use backend::graphics::GraphicsBackend;
|
||||||
use backend::graphics::egl::{EGLGraphicsBackend, EGLSurface, PixelFormat, SwapBuffersError};
|
use backend::graphics::egl::{EGLContext, EGLGraphicsBackend, EGLSurface, PixelFormat, SwapBuffersError};
|
||||||
|
use backend::graphics::egl::error::Result as EGLResult;
|
||||||
|
use backend::graphics::egl::native::{Gbm, GbmSurfaceArguments};
|
||||||
|
use backend::graphics::egl::wayland::{EGLDisplay, EGLWaylandExtensions};
|
||||||
use drm::control::{Device, ResourceInfo};
|
use drm::control::{Device, ResourceInfo};
|
||||||
use drm::control::{connector, crtc, encoder, framebuffer, Mode};
|
use drm::control::{connector, crtc, encoder, framebuffer, Mode};
|
||||||
use gbm::{BufferObject, BufferObjectFlags, Format as GbmFormat, Surface as GbmSurface, SurfaceBufferHandle};
|
use gbm::{BufferObject, BufferObjectFlags, Device as GbmDevice, Format as GbmFormat, Surface as GbmSurface,
|
||||||
|
SurfaceBufferHandle};
|
||||||
use image::{ImageBuffer, Rgba};
|
use image::{ImageBuffer, Rgba};
|
||||||
use nix::libc::c_void;
|
use nix::libc::c_void;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::rc::Rc;
|
use std::rc::{Rc, Weak};
|
||||||
|
use wayland_server::Display;
|
||||||
/*
|
|
||||||
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
|
/// Backend based on a `DrmDevice` and a given crtc
|
||||||
pub struct DrmBackend {
|
pub struct DrmBackend<A: Device + 'static> {
|
||||||
graphics: Graphics,
|
backend: Rc<DrmBackendInternal<A>>,
|
||||||
crtc: crtc::Handle,
|
surface: EGLSurface<GbmSurface<framebuffer::Info>>,
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
connectors: Vec<connector::Handle>,
|
connectors: Vec<connector::Handle>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct DrmBackendInternal<A: Device + 'static> {
|
||||||
|
context: Rc<EGLContext<Gbm<framebuffer::Info>, GbmDevice<A>>>,
|
||||||
|
cursor: Cell<BufferObject<()>>,
|
||||||
|
current_frame_buffer: Cell<framebuffer::Info>,
|
||||||
|
front_buffer: Cell<SurfaceBufferHandle<framebuffer::Info>>,
|
||||||
|
next_buffer: Cell<Option<SurfaceBufferHandle<framebuffer::Info>>>,
|
||||||
|
crtc: crtc::Handle,
|
||||||
logger: ::slog::Logger,
|
logger: ::slog::Logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DrmBackend {
|
impl<A: Device + 'static> DrmBackend<A> {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
context: Rc<devices::Context>, crtc: crtc::Handle, mode: Mode, connectors: Vec<connector::Handle>,
|
context: Rc<EGLContext<Gbm<framebuffer::Info>, GbmDevice<A>>>, crtc: crtc::Handle, mode: Mode,
|
||||||
logger: ::slog::Logger,
|
connectors: Vec<connector::Handle>, log: ::slog::Logger,
|
||||||
) -> Result<DrmBackend> {
|
) -> Result<Self> {
|
||||||
// logger already initialized by the DrmDevice
|
// logger already initialized by the DrmDevice
|
||||||
let log = ::slog_or_stdlog(logger);
|
|
||||||
info!(log, "Initializing DrmBackend");
|
info!(log, "Initializing DrmBackend");
|
||||||
|
|
||||||
let (w, h) = mode.size();
|
let (w, h) = mode.size();
|
||||||
|
|
||||||
Ok(DrmBackend {
|
debug!(log, "Creating Surface");
|
||||||
graphics: Graphics::try_new(context, |context| {
|
let surface = context
|
||||||
Ok(GbmTypes {
|
.create_surface(GbmSurfaceArguments {
|
||||||
cursor: {
|
size: (w as u32, h as u32),
|
||||||
// Create an unused cursor buffer (we don't want an Option here)
|
format: GbmFormat::XRGB8888,
|
||||||
Cell::new(context
|
flags: BufferObjectFlags::SCANOUT | BufferObjectFlags::RENDERING,
|
||||||
.devices
|
})
|
||||||
.gbm
|
.chain_err(|| ErrorKind::GbmInitFailed)?;
|
||||||
.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`
|
// make it active for the first `crtc::set`
|
||||||
// (which is needed before the first page_flip)
|
// (which is needed before the first page_flip)
|
||||||
unsafe {
|
unsafe {
|
||||||
egl_surface
|
surface
|
||||||
.make_current()
|
.make_current()
|
||||||
.chain_err(|| ErrorKind::FailedToSwap)?
|
.chain_err(|| ErrorKind::FailedToSwap)?
|
||||||
};
|
};
|
||||||
egl_surface
|
surface
|
||||||
.swap_buffers()
|
.swap_buffers()
|
||||||
.chain_err(|| ErrorKind::FailedToSwap)?;
|
.chain_err(|| ErrorKind::FailedToSwap)?;
|
||||||
|
|
||||||
|
@ -132,54 +68,61 @@ impl DrmBackend {
|
||||||
let mut front_bo = surface
|
let mut front_bo = surface
|
||||||
.lock_front_buffer()
|
.lock_front_buffer()
|
||||||
.chain_err(|| ErrorKind::FailedToSwap)?;
|
.chain_err(|| ErrorKind::FailedToSwap)?;
|
||||||
|
|
||||||
debug!(log, "FrontBuffer color format: {:?}", front_bo.format());
|
debug!(log, "FrontBuffer color format: {:?}", front_bo.format());
|
||||||
// we need a framebuffer per front buffer
|
|
||||||
let fb = framebuffer::create(context.devices.drm, &*front_bo)
|
// we need a framebuffer for the front buffer
|
||||||
.chain_err(|| ErrorKind::DrmDev(format!("{:?}", context.devices.drm)))?;
|
let fb = framebuffer::create(&*context, &*front_bo).chain_err(|| {
|
||||||
|
ErrorKind::DrmDev(format!(
|
||||||
|
"Error creating framebuffer on {:?}",
|
||||||
|
context.dev_path()
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
debug!(log, "Initialize screen");
|
debug!(log, "Initialize screen");
|
||||||
crtc::set(
|
crtc::set(
|
||||||
context.devices.drm,
|
&*context,
|
||||||
crtc,
|
crtc,
|
||||||
fb.handle(),
|
fb.handle(),
|
||||||
&connectors,
|
&connectors,
|
||||||
(0, 0),
|
(0, 0),
|
||||||
Some(mode),
|
Some(mode),
|
||||||
).chain_err(|| ErrorKind::DrmDev(format!("{:?}", context.devices.drm)))?;
|
).chain_err(|| {
|
||||||
front_bo.set_userdata(fb);
|
ErrorKind::DrmDev(format!(
|
||||||
|
"Error setting crtc {:?} on {:?}",
|
||||||
|
crtc,
|
||||||
|
context.dev_path()
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
front_bo.set_userdata(fb).unwrap();
|
||||||
|
|
||||||
Ok(EGL {
|
let cursor = Cell::new(context
|
||||||
surface: egl_surface,
|
.create_buffer_object(
|
||||||
buffers: GbmBuffers {
|
1,
|
||||||
|
1,
|
||||||
|
GbmFormat::ARGB8888,
|
||||||
|
BufferObjectFlags::CURSOR | BufferObjectFlags::WRITE,
|
||||||
|
)
|
||||||
|
.chain_err(|| ErrorKind::GbmInitFailed)?);
|
||||||
|
|
||||||
|
Ok(DrmBackend {
|
||||||
|
backend: Rc::new(DrmBackendInternal {
|
||||||
|
context,
|
||||||
|
cursor,
|
||||||
|
current_frame_buffer: Cell::new(fb),
|
||||||
front_buffer: Cell::new(front_bo),
|
front_buffer: Cell::new(front_bo),
|
||||||
next_buffer: Cell::new(None),
|
next_buffer: Cell::new(None),
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
|
||||||
).map_err(Error::from)?,
|
|
||||||
})
|
|
||||||
})?,
|
|
||||||
crtc,
|
crtc,
|
||||||
|
logger: log,
|
||||||
|
}),
|
||||||
|
surface,
|
||||||
mode,
|
mode,
|
||||||
connectors,
|
connectors,
|
||||||
logger: log.clone(),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn unlock_buffer(&self) {
|
pub(crate) fn weak(&self) -> Weak<DrmBackendInternal<A>> {
|
||||||
// after the page swap is finished we need to release the rendered buffer.
|
Rc::downgrade(&self.backend)
|
||||||
// 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 old front buffer");
|
|
||||||
egl.buffers.front_buffer.set(next_buffer);
|
|
||||||
// drop and release the old buffer
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a connector to backend
|
/// Add a connector to backend
|
||||||
|
@ -188,8 +131,12 @@ impl DrmBackend {
|
||||||
///
|
///
|
||||||
/// Errors if the new connector does not support the currently set `Mode`
|
/// Errors if the new connector does not support the currently set `Mode`
|
||||||
pub fn add_connector(&mut self, connector: connector::Handle) -> Result<()> {
|
pub fn add_connector(&mut self, connector: connector::Handle) -> Result<()> {
|
||||||
let info = connector::Info::load_from_device(self.graphics.head().head().head(), connector)
|
let info = connector::Info::load_from_device(&*self.backend.context, connector).chain_err(|| {
|
||||||
.chain_err(|| ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head())))?;
|
ErrorKind::DrmDev(format!(
|
||||||
|
"Error loading connector info on {:?}",
|
||||||
|
self.backend.context.dev_path()
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
// check if the connector can handle the current mode
|
// check if the connector can handle the current mode
|
||||||
if info.modes().contains(&self.mode) {
|
if info.modes().contains(&self.mode) {
|
||||||
|
@ -197,31 +144,35 @@ impl DrmBackend {
|
||||||
let encoders = info.encoders()
|
let encoders = info.encoders()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|encoder| {
|
.map(|encoder| {
|
||||||
encoder::Info::load_from_device(self.graphics.head().head().head(), *encoder)
|
encoder::Info::load_from_device(&*self.backend.context, *encoder).chain_err(|| {
|
||||||
.chain_err(|| ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head())))
|
ErrorKind::DrmDev(format!(
|
||||||
|
"Error loading encoder info on {:?}",
|
||||||
|
self.backend.context.dev_path()
|
||||||
|
))
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<encoder::Info>>>()?;
|
.collect::<Result<Vec<encoder::Info>>>()?;
|
||||||
|
|
||||||
// and if any encoder supports the selected crtc
|
// and if any encoder supports the selected crtc
|
||||||
let resource_handles = self.graphics
|
let resource_handles = self.backend.context.resource_handles().chain_err(|| {
|
||||||
.head()
|
ErrorKind::DrmDev(format!(
|
||||||
.head()
|
"Error loading resources on {:?}",
|
||||||
.head()
|
self.backend.context.dev_path()
|
||||||
.resource_handles()
|
))
|
||||||
.chain_err(|| ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head())))?;
|
})?;
|
||||||
if !encoders
|
if !encoders
|
||||||
.iter()
|
.iter()
|
||||||
.map(|encoder| encoder.possible_crtcs())
|
.map(|encoder| encoder.possible_crtcs())
|
||||||
.all(|crtc_list| {
|
.all(|crtc_list| {
|
||||||
resource_handles
|
resource_handles
|
||||||
.filter_crtcs(crtc_list)
|
.filter_crtcs(crtc_list)
|
||||||
.contains(&self.crtc)
|
.contains(&self.backend.crtc)
|
||||||
}) {
|
}) {
|
||||||
bail!(ErrorKind::NoSuitableEncoder(info, self.crtc));
|
bail!(ErrorKind::NoSuitableEncoder(info, self.backend.crtc));
|
||||||
}
|
}
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
self.logger,
|
self.backend.logger,
|
||||||
"Adding new connector: {:?}",
|
"Adding new connector: {:?}",
|
||||||
info.connector_type()
|
info.connector_type()
|
||||||
);
|
);
|
||||||
|
@ -239,14 +190,14 @@ impl DrmBackend {
|
||||||
|
|
||||||
/// Removes a currently set connector
|
/// Removes a currently set connector
|
||||||
pub fn remove_connector(&mut self, connector: connector::Handle) {
|
pub fn remove_connector(&mut self, connector: connector::Handle) {
|
||||||
if let Ok(info) = connector::Info::load_from_device(self.graphics.head().head().head(), connector) {
|
if let Ok(info) = connector::Info::load_from_device(&*self.backend.context, connector) {
|
||||||
info!(
|
info!(
|
||||||
self.logger,
|
self.backend.logger,
|
||||||
"Removing connector: {:?}",
|
"Removing connector: {:?}",
|
||||||
info.connector_type()
|
info.connector_type()
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
info!(self.logger, "Removing unknown connector");
|
info!(self.backend.logger, "Removing unknown connector");
|
||||||
}
|
}
|
||||||
|
|
||||||
self.connectors.retain(|x| *x != connector);
|
self.connectors.retain(|x| *x != connector);
|
||||||
|
@ -262,8 +213,13 @@ impl DrmBackend {
|
||||||
pub fn use_mode(&mut self, mode: Mode) -> Result<()> {
|
pub fn use_mode(&mut self, mode: Mode) -> Result<()> {
|
||||||
// check the connectors
|
// check the connectors
|
||||||
for connector in &self.connectors {
|
for connector in &self.connectors {
|
||||||
if !connector::Info::load_from_device(self.graphics.head().head().head(), *connector)
|
if !connector::Info::load_from_device(&*self.backend.context, *connector)
|
||||||
.chain_err(|| ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head())))?
|
.chain_err(|| {
|
||||||
|
ErrorKind::DrmDev(format!(
|
||||||
|
"Error loading connector info on {:?}",
|
||||||
|
self.backend.context.dev_path()
|
||||||
|
))
|
||||||
|
})?
|
||||||
.modes()
|
.modes()
|
||||||
.contains(&mode)
|
.contains(&mode)
|
||||||
{
|
{
|
||||||
|
@ -271,160 +227,198 @@ impl DrmBackend {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// borrow & clone stuff because rust cannot figure out the upcoming
|
info!(self.backend.logger, "Setting new mode: {:?}", mode.name());
|
||||||
// closure otherwise.
|
|
||||||
let crtc = self.crtc;
|
|
||||||
let connectors_ref = &self.connectors;
|
|
||||||
let logger_ref = &self.logger;
|
|
||||||
|
|
||||||
let (w, h) = mode.size();
|
let (w, h) = mode.size();
|
||||||
|
|
||||||
self.graphics.rent_all_mut(|graphics| -> Result<()> {
|
|
||||||
// Recreate the surface and the related resources to match the new
|
// Recreate the surface and the related resources to match the new
|
||||||
// resolution.
|
// resolution.
|
||||||
debug!(
|
debug!(
|
||||||
logger_ref,
|
self.backend.logger,
|
||||||
"Reinitializing surface for new mode: {}:{}", w, h
|
"Reinitializing surface for new mode: {}:{}", w, h
|
||||||
);
|
);
|
||||||
graphics.gbm.surface = Surface::try_new(
|
let surface = self.backend
|
||||||
{
|
|
||||||
// create a new gbm surface
|
|
||||||
debug!(logger_ref, "Creating GbmSurface");
|
|
||||||
Box::new(graphics
|
|
||||||
.context
|
.context
|
||||||
.devices
|
.create_surface(GbmSurfaceArguments {
|
||||||
.gbm
|
size: (w as u32, h as u32),
|
||||||
.create_surface(
|
format: GbmFormat::XRGB8888,
|
||||||
w as u32,
|
flags: BufferObjectFlags::SCANOUT | BufferObjectFlags::RENDERING,
|
||||||
h as u32,
|
})
|
||||||
GbmFormat::XRGB8888,
|
.chain_err(|| ErrorKind::GbmInitFailed)?;
|
||||||
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`
|
// make it active for the first `crtc::set`
|
||||||
// (which is needed before the first page_flip)
|
// (which is needed before the first page_flip)
|
||||||
unsafe {
|
unsafe {
|
||||||
egl_surface
|
surface
|
||||||
.make_current()
|
.make_current()
|
||||||
.chain_err(|| ErrorKind::FailedToSwap)?
|
.chain_err(|| ErrorKind::FailedToSwap)?
|
||||||
};
|
};
|
||||||
egl_surface
|
surface
|
||||||
.swap_buffers()
|
.swap_buffers()
|
||||||
.chain_err(|| ErrorKind::FailedToSwap)?;
|
.chain_err(|| ErrorKind::FailedToSwap)?;
|
||||||
|
|
||||||
|
// Clean up next_buffer
|
||||||
|
{
|
||||||
|
if let Some(mut old_bo) = self.backend.next_buffer.take() {
|
||||||
|
if let Ok(Some(fb)) = old_bo.take_userdata() {
|
||||||
|
if let Err(err) = framebuffer::destroy(&*self.backend.context, fb.handle()) {
|
||||||
|
warn!(
|
||||||
|
self.backend.logger,
|
||||||
|
"Error releasing old back_buffer framebuffer: {:?}", err
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup front_buffer and init the first screen on the new front_buffer
|
||||||
|
// (must be done before calling page_flip for the first time)
|
||||||
|
let mut old_front_bo = self.backend.front_buffer.replace({
|
||||||
let mut front_bo = surface
|
let mut front_bo = surface
|
||||||
.lock_front_buffer()
|
.lock_front_buffer()
|
||||||
.chain_err(|| ErrorKind::FailedToSwap)?;
|
.chain_err(|| ErrorKind::FailedToSwap)?;
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
logger_ref,
|
self.backend.logger,
|
||||||
"FrontBuffer color format: {:?}",
|
"FrontBuffer color format: {:?}",
|
||||||
front_bo.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");
|
// we also need a new framebuffer for the front buffer
|
||||||
|
let dev_path = self.backend.context.dev_path();
|
||||||
|
let fb = framebuffer::create(&*self.backend.context, &*front_bo)
|
||||||
|
.chain_err(|| ErrorKind::DrmDev(format!("Error creating framebuffer on {:?}", dev_path)))?;
|
||||||
|
|
||||||
|
front_bo.set_userdata(fb).unwrap();
|
||||||
|
|
||||||
|
debug!(self.backend.logger, "Setting screen");
|
||||||
crtc::set(
|
crtc::set(
|
||||||
graphics.context.devices.drm,
|
&*self.backend.context,
|
||||||
crtc,
|
self.backend.crtc,
|
||||||
fb.handle(),
|
fb.handle(),
|
||||||
connectors_ref,
|
&self.connectors,
|
||||||
(0, 0),
|
(0, 0),
|
||||||
Some(mode),
|
Some(mode),
|
||||||
).chain_err(|| ErrorKind::DrmDev(format!("{:?}", graphics.context.devices.drm)))?;
|
).chain_err(|| {
|
||||||
front_bo.set_userdata(fb);
|
ErrorKind::DrmDev(format!(
|
||||||
|
"Error setting crtc {:?} on {:?}",
|
||||||
Ok(EGL {
|
self.backend.crtc,
|
||||||
surface: egl_surface,
|
self.backend.context.dev_path()
|
||||||
buffers: GbmBuffers {
|
))
|
||||||
front_buffer: Cell::new(front_bo),
|
|
||||||
next_buffer: Cell::new(None),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
info!(self.logger, "Setting new mode: {:?}", mode.name());
|
front_bo
|
||||||
|
});
|
||||||
|
if let Ok(Some(fb)) = old_front_bo.take_userdata() {
|
||||||
|
if let Err(err) = framebuffer::destroy(&*self.backend.context, fb.handle()) {
|
||||||
|
warn!(
|
||||||
|
self.backend.logger,
|
||||||
|
"Error releasing old front_buffer framebuffer: {:?}", err
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop the old surface after cleanup
|
||||||
|
self.surface = surface;
|
||||||
self.mode = mode;
|
self.mode = mode;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the crtc id used by this backend
|
/// Returns the crtc id used by this backend
|
||||||
pub fn crtc(&self) -> crtc::Handle {
|
pub fn crtc(&self) -> crtc::Handle {
|
||||||
self.crtc
|
self.backend.crtc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for DrmBackend {
|
impl<A: Device + 'static> DrmBackendInternal<A> {
|
||||||
|
pub(crate) fn unlock_buffer(&self) {
|
||||||
|
// after the page swap is finished we need to release the rendered buffer.
|
||||||
|
// this is called from the PageFlipHandler
|
||||||
|
if let Some(next_buffer) = self.next_buffer.replace(None) {
|
||||||
|
trace!(self.logger, "Releasing old front buffer");
|
||||||
|
self.front_buffer.set(next_buffer);
|
||||||
|
// drop and release the old buffer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn page_flip(
|
||||||
|
&self, fb: Option<&framebuffer::Info>
|
||||||
|
) -> ::std::result::Result<(), SwapBuffersError> {
|
||||||
|
trace!(self.logger, "Queueing Page flip");
|
||||||
|
|
||||||
|
let fb = *fb.unwrap_or(&self.current_frame_buffer.get());
|
||||||
|
|
||||||
|
// and flip
|
||||||
|
crtc::page_flip(
|
||||||
|
&*self.context,
|
||||||
|
self.crtc,
|
||||||
|
fb.handle(),
|
||||||
|
&[crtc::PageFlipFlags::PageFlipEvent],
|
||||||
|
).map_err(|_| SwapBuffersError::ContextLost)?;
|
||||||
|
|
||||||
|
self.current_frame_buffer.set(fb);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A: Device + 'static> Drop for DrmBackend<A> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// Drop framebuffers attached to the userdata of the gbm surface buffers.
|
// Drop framebuffers attached to the userdata of the gbm surface buffers.
|
||||||
// (They don't implement drop, as they need the device)
|
// (They don't implement drop, as they need the device)
|
||||||
let crtc = self.crtc;
|
if let Ok(Some(fb)) = {
|
||||||
self.graphics.rent_all_mut(|graphics| {
|
if let Some(mut next) = self.backend.next_buffer.take() {
|
||||||
if let Some(fb) = graphics.gbm.surface.rent(|egl| {
|
|
||||||
if let Some(mut next) = egl.buffers.next_buffer.take() {
|
|
||||||
next.take_userdata()
|
next.take_userdata()
|
||||||
} else if let Ok(mut next) = graphics.gbm.surface.head().lock_front_buffer() {
|
} else if let Ok(mut next) = self.surface.lock_front_buffer() {
|
||||||
next.take_userdata()
|
next.take_userdata()
|
||||||
} else {
|
} else {
|
||||||
None
|
Ok(None)
|
||||||
}
|
}
|
||||||
}) {
|
} {
|
||||||
// ignore failure at this point
|
// ignore failure at this point
|
||||||
let _ = framebuffer::destroy(&*graphics.context.devices.drm, fb.handle());
|
let _ = framebuffer::destroy(&*self.backend.context, 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());
|
|
||||||
}
|
|
||||||
|
|
||||||
// ignore failure at this point
|
|
||||||
let _ = crtc::clear_cursor(&*graphics.context.devices.drm, crtc);
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GraphicsBackend for DrmBackend {
|
impl<A: Device + 'static> Drop for DrmBackendInternal<A> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Ok(Some(fb)) = self.front_buffer.get_mut().take_userdata() {
|
||||||
|
// ignore failure at this point
|
||||||
|
let _ = framebuffer::destroy(&*self.context, fb.handle());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore failure at this point
|
||||||
|
let _ = crtc::clear_cursor(&*self.context, self.crtc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A: Device + 'static> GraphicsBackend for DrmBackend<A> {
|
||||||
type CursorFormat = ImageBuffer<Rgba<u8>, Vec<u8>>;
|
type CursorFormat = ImageBuffer<Rgba<u8>, Vec<u8>>;
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn set_cursor_position(&self, x: u32, y: u32) -> Result<()> {
|
fn set_cursor_position(&self, x: u32, y: u32) -> Result<()> {
|
||||||
trace!(self.logger, "Move the cursor to {},{}", x, y);
|
trace!(self.backend.logger, "Move the cursor to {},{}", x, y);
|
||||||
crtc::move_cursor(
|
crtc::move_cursor(
|
||||||
self.graphics.head().head().head(),
|
&*self.backend.context,
|
||||||
self.crtc,
|
self.backend.crtc,
|
||||||
(x as i32, y as i32),
|
(x as i32, y as i32),
|
||||||
).chain_err(|| ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head())))
|
).chain_err(|| {
|
||||||
|
ErrorKind::DrmDev(format!(
|
||||||
|
"Error moving cursor on {:?}",
|
||||||
|
self.backend.context.dev_path()
|
||||||
|
))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_cursor_representation(
|
fn set_cursor_representation(
|
||||||
&self, buffer: &ImageBuffer<Rgba<u8>, Vec<u8>>, hotspot: (u32, u32)
|
&self, buffer: &ImageBuffer<Rgba<u8>, Vec<u8>>, hotspot: (u32, u32)
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let (w, h) = buffer.dimensions();
|
let (w, h) = buffer.dimensions();
|
||||||
|
debug!(self.backend.logger, "Importing cursor");
|
||||||
|
|
||||||
debug!(self.logger, "Importing cursor");
|
|
||||||
|
|
||||||
self.graphics.rent_all(|graphics| -> Result<()> {
|
|
||||||
graphics.gbm.cursor.set({
|
|
||||||
// import the cursor into a buffer we can render
|
// import the cursor into a buffer we can render
|
||||||
let mut cursor = graphics
|
let mut cursor = self.backend
|
||||||
.context
|
.context
|
||||||
.devices
|
|
||||||
.gbm
|
|
||||||
.create_buffer_object(
|
.create_buffer_object(
|
||||||
w,
|
w,
|
||||||
h,
|
h,
|
||||||
|
@ -434,88 +428,81 @@ impl GraphicsBackend for DrmBackend {
|
||||||
.chain_err(|| ErrorKind::GbmInitFailed)?;
|
.chain_err(|| ErrorKind::GbmInitFailed)?;
|
||||||
cursor
|
cursor
|
||||||
.write(&**buffer)
|
.write(&**buffer)
|
||||||
|
.chain_err(|| ErrorKind::GbmInitFailed)?
|
||||||
.chain_err(|| ErrorKind::GbmInitFailed)?;
|
.chain_err(|| ErrorKind::GbmInitFailed)?;
|
||||||
|
|
||||||
trace!(self.logger, "Set the new imported cursor");
|
trace!(self.backend.logger, "Setting the new imported cursor");
|
||||||
|
|
||||||
// and set it
|
// and set it
|
||||||
if crtc::set_cursor2(
|
if crtc::set_cursor2(
|
||||||
self.graphics.head().head().head(),
|
&*self.backend.context,
|
||||||
self.crtc,
|
self.backend.crtc,
|
||||||
&cursor,
|
&cursor,
|
||||||
(hotspot.0 as i32, hotspot.1 as i32),
|
(hotspot.0 as i32, hotspot.1 as i32),
|
||||||
).is_err()
|
).is_err()
|
||||||
{
|
{
|
||||||
crtc::set_cursor(self.graphics.head().head().head(), self.crtc, &cursor).chain_err(
|
crtc::set_cursor(&*self.backend.context, self.backend.crtc, &cursor).chain_err(|| {
|
||||||
|| ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head())),
|
ErrorKind::DrmDev(format!(
|
||||||
)?;
|
"Failed to set cursor on {:?}",
|
||||||
|
self.backend.context.dev_path()
|
||||||
|
))
|
||||||
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// and store it
|
// and store it
|
||||||
cursor
|
self.backend.cursor.set(cursor);
|
||||||
});
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EGLGraphicsBackend for DrmBackend {
|
impl<A: Device + 'static> EGLGraphicsBackend for DrmBackend<A> {
|
||||||
fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
|
fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
|
||||||
self.graphics.rent_all(|graphics| {
|
if {
|
||||||
// We cannot call lock_front_buffer anymore without releasing the previous buffer, which will happen when the page flip is done
|
let nb = self.backend.next_buffer.take();
|
||||||
if graphics.gbm.surface.rent(|egl| {
|
let res = nb.is_some();
|
||||||
let next = egl.buffers.next_buffer.take();
|
self.backend.next_buffer.set(nb);
|
||||||
let res = next.is_some();
|
|
||||||
egl.buffers.next_buffer.set(next);
|
|
||||||
res
|
res
|
||||||
}) {
|
} {
|
||||||
warn!(self.logger, "Tried to swap a DrmBackend with a queued flip");
|
// We cannot call lock_front_buffer anymore without releasing the previous buffer, which will happen when the page flip is done
|
||||||
|
warn!(
|
||||||
|
self.backend.logger,
|
||||||
|
"Tried to swap a DrmBackend with a queued flip"
|
||||||
|
);
|
||||||
return Err(SwapBuffersError::AlreadySwapped);
|
return Err(SwapBuffersError::AlreadySwapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
// flip normally
|
// flip normally
|
||||||
graphics.gbm.surface.rent(|egl| egl.surface.swap_buffers())?;
|
self.surface.swap_buffers()?;
|
||||||
|
|
||||||
graphics.gbm.surface.rent_all(|surface| {
|
// supporting only one buffer would cause a lot of inconvinience and
|
||||||
// supporting this error would cause a lot of inconvinience and
|
|
||||||
// would most likely result in a lot of flickering.
|
// would most likely result in a lot of flickering.
|
||||||
// neither weston, wlc or wlroots bother with that as well.
|
// neither weston, wlc or wlroots bother with that as well.
|
||||||
// so we just assume we got at least two buffers to do flipping
|
// so we just assume we got at least two buffers to do flipping.
|
||||||
let mut next_bo = surface
|
let mut next_bo = self.surface
|
||||||
.gbm
|
|
||||||
.lock_front_buffer()
|
.lock_front_buffer()
|
||||||
.expect("Surface only has one front buffer. Not supported by smithay");
|
.expect("Surface only has one front buffer. Not supported by smithay");
|
||||||
|
|
||||||
// create a framebuffer if the front buffer does not have one already
|
// create a framebuffer if the front buffer does not have one already
|
||||||
// (they are reused by gbm)
|
// (they are reused by gbm)
|
||||||
let maybe_fb = next_bo.userdata().cloned();
|
let maybe_fb = next_bo
|
||||||
|
.userdata()
|
||||||
|
.map_err(|_| SwapBuffersError::ContextLost)?
|
||||||
|
.cloned();
|
||||||
let fb = if let Some(info) = maybe_fb {
|
let fb = if let Some(info) = maybe_fb {
|
||||||
info
|
info
|
||||||
} else {
|
} else {
|
||||||
let fb = framebuffer::create(graphics.context.devices.drm, &*next_bo)
|
let fb = framebuffer::create(&*self.backend.context, &*next_bo)
|
||||||
.map_err(|_| SwapBuffersError::ContextLost)?;
|
.map_err(|_| SwapBuffersError::ContextLost)?;
|
||||||
next_bo.set_userdata(fb);
|
next_bo.set_userdata(fb).unwrap();
|
||||||
fb
|
fb
|
||||||
};
|
};
|
||||||
surface.egl.buffers.next_buffer.set(Some(next_bo));
|
self.backend.next_buffer.set(Some(next_bo));
|
||||||
|
|
||||||
trace!(self.logger, "Queueing Page flip");
|
self.backend.page_flip(Some(&fb))
|
||||||
|
|
||||||
// and flip
|
|
||||||
crtc::page_flip(
|
|
||||||
graphics.context.devices.drm,
|
|
||||||
self.crtc,
|
|
||||||
fb.handle(),
|
|
||||||
&[crtc::PageFlipFlags::PageFlipEvent],
|
|
||||||
).map_err(|_| SwapBuffersError::ContextLost)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void {
|
unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void {
|
||||||
self.graphics
|
self.backend.context.get_proc_address(symbol)
|
||||||
.head()
|
|
||||||
.rent(|context| context.get_proc_address(symbol))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_framebuffer_dimensions(&self) -> (u32, u32) {
|
fn get_framebuffer_dimensions(&self) -> (u32, u32) {
|
||||||
|
@ -524,19 +511,20 @@ impl EGLGraphicsBackend for DrmBackend {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_current(&self) -> bool {
|
fn is_current(&self) -> bool {
|
||||||
self.graphics.rent_all(|graphics| {
|
self.backend.context.is_current() && self.surface.is_current()
|
||||||
graphics.context.egl.is_current() && graphics.gbm.surface.rent(|egl| egl.surface.is_current())
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
|
unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
|
||||||
self.graphics
|
self.surface.make_current()
|
||||||
.rent(|gbm| gbm.surface.rent(|egl| egl.surface.make_current()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_pixel_format(&self) -> PixelFormat {
|
fn get_pixel_format(&self) -> PixelFormat {
|
||||||
self.graphics
|
self.backend.context.get_pixel_format()
|
||||||
.head()
|
}
|
||||||
.rent(|context| context.get_pixel_format())
|
}
|
||||||
|
|
||||||
|
impl<A: Device + 'static> EGLWaylandExtensions for DrmBackend<A> {
|
||||||
|
fn bind_wl_display(&self, display: &Display) -> EGLResult<EGLDisplay> {
|
||||||
|
self.backend.context.bind_wl_display(display)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,8 @@
|
||||||
//! Errors thrown by the `DrmDevice` and `DrmBackend`
|
//! Errors thrown by the `DrmDevice` and `DrmBackend`
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use backend::graphics::egl;
|
use backend::graphics::egl::error as egl;
|
||||||
use drm::control::{connector, crtc, Mode};
|
use drm::control::{connector, crtc, Mode};
|
||||||
use rental::TryNewError;
|
|
||||||
|
|
||||||
error_chain! {
|
error_chain! {
|
||||||
errors {
|
errors {
|
||||||
|
@ -19,6 +18,11 @@ error_chain! {
|
||||||
display("The drm device ({:?}) encountered an access error", dev),
|
display("The drm device ({:?}) encountered an access error", dev),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc = "Unable to determine device id of drm device"]
|
||||||
|
UnableToGetDeviceId {
|
||||||
|
description("Unable to determine device id of drm device"),
|
||||||
|
}
|
||||||
|
|
||||||
#[doc = "Creation of gbm resource failed"]
|
#[doc = "Creation of gbm resource failed"]
|
||||||
GbmInitFailed {
|
GbmInitFailed {
|
||||||
description("Creation of gbm resource failed"),
|
description("Creation of gbm resource failed"),
|
||||||
|
@ -60,9 +64,3 @@ error_chain! {
|
||||||
EGL(egl::Error, egl::ErrorKind) #[doc = "EGL error"];
|
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -44,23 +44,37 @@
|
||||||
//! extern crate smithay;
|
//! extern crate smithay;
|
||||||
//! extern crate wayland_server;
|
//! extern crate wayland_server;
|
||||||
//!
|
//!
|
||||||
|
//! use drm::Device as BasicDevice;
|
||||||
//! use drm::control::{Device as ControlDevice, ResourceInfo};
|
//! use drm::control::{Device as ControlDevice, ResourceInfo};
|
||||||
//! use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState};
|
//! use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState};
|
||||||
//! use drm::control::encoder::{Info as EncoderInfo};
|
//! use drm::control::encoder::{Info as EncoderInfo};
|
||||||
//! use std::fs::OpenOptions;
|
//! use std::fs::{File, OpenOptions};
|
||||||
|
//! use std::os::unix::io::RawFd;
|
||||||
|
//! use std::os::unix::io::AsRawFd;
|
||||||
//! use smithay::backend::drm::{DrmDevice, DrmBackend};
|
//! use smithay::backend::drm::{DrmDevice, DrmBackend};
|
||||||
//! use wayland_server::StateToken;
|
//! use wayland_server::StateToken;
|
||||||
//!
|
//!
|
||||||
//! # fn main() {
|
//! #[derive(Debug)]
|
||||||
|
//! pub struct Card(File);
|
||||||
//!
|
//!
|
||||||
|
//! impl AsRawFd for Card {
|
||||||
|
//! fn as_raw_fd(&self) -> RawFd {
|
||||||
|
//! self.0.as_raw_fd()
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! impl BasicDevice for Card {}
|
||||||
|
//! impl ControlDevice for Card {}
|
||||||
|
//!
|
||||||
|
//! # fn main() {
|
||||||
//! let (_display, mut event_loop) = wayland_server::create_display();
|
//! let (_display, mut event_loop) = wayland_server::create_display();
|
||||||
//!
|
//!
|
||||||
//! // Open the drm device
|
//! // Open the drm device
|
||||||
//! let mut options = OpenOptions::new();
|
//! let mut options = OpenOptions::new();
|
||||||
//! options.read(true);
|
//! options.read(true);
|
||||||
//! options.write(true);
|
//! options.write(true);
|
||||||
//! let mut device = DrmDevice::new_from_file(
|
//! let mut device = DrmDevice::new(
|
||||||
//! options.open("/dev/dri/card0").unwrap(), // try to detect it properly
|
//! Card(options.open("/dev/dri/card0").unwrap()), // try to detect it properly
|
||||||
//! None /*put a logger here*/
|
//! None /*put a logger here*/
|
||||||
//! ).unwrap();
|
//! ).unwrap();
|
||||||
//!
|
//!
|
||||||
|
@ -89,12 +103,11 @@
|
||||||
//! let mode = connector_info.modes()[0];
|
//! let mode = connector_info.modes()[0];
|
||||||
//!
|
//!
|
||||||
//! // Create the backend
|
//! // Create the backend
|
||||||
//! let backend: StateToken<DrmBackend> = device.create_backend(
|
//! let backend = device.create_backend(
|
||||||
//! event_loop.state(),
|
|
||||||
//! crtc,
|
//! crtc,
|
||||||
//! mode,
|
//! mode,
|
||||||
//! vec![connector_info.handle()]
|
//! vec![connector_info.handle()]
|
||||||
//! ).unwrap().clone();
|
//! ).unwrap();
|
||||||
//! # }
|
//! # }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
@ -116,15 +129,27 @@
|
||||||
//! # extern crate smithay;
|
//! # extern crate smithay;
|
||||||
//! # extern crate wayland_server;
|
//! # extern crate wayland_server;
|
||||||
//! #
|
//! #
|
||||||
|
//! # use drm::Device as BasicDevice;
|
||||||
//! # use drm::control::{Device as ControlDevice, ResourceInfo};
|
//! # use drm::control::{Device as ControlDevice, ResourceInfo};
|
||||||
//! # use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState};
|
//! # use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState};
|
||||||
//! use drm::control::crtc::{Handle as CrtcHandle};
|
//! use drm::control::crtc::{Handle as CrtcHandle};
|
||||||
//! use drm::result::Error as DrmError;
|
//! use drm::result::Error as DrmError;
|
||||||
//! # use std::fs::OpenOptions;
|
//! # use std::fs::{File, OpenOptions};
|
||||||
|
//! # use std::os::unix::io::RawFd;
|
||||||
|
//! # use std::os::unix::io::AsRawFd;
|
||||||
//! # use std::time::Duration;
|
//! # use std::time::Duration;
|
||||||
//! use smithay::backend::drm::{DrmDevice, DrmBackend, DrmHandler, drm_device_bind};
|
//! use smithay::backend::drm::{DrmDevice, DrmBackend, DrmHandler, drm_device_bind};
|
||||||
//! use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
//! use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
||||||
//! use wayland_server::{StateToken, StateProxy};
|
//! #
|
||||||
|
//! # #[derive(Debug)]
|
||||||
|
//! # pub struct Card(File);
|
||||||
|
//! # impl AsRawFd for Card {
|
||||||
|
//! # fn as_raw_fd(&self) -> RawFd {
|
||||||
|
//! # self.0.as_raw_fd()
|
||||||
|
//! # }
|
||||||
|
//! # }
|
||||||
|
//! # impl BasicDevice for Card {}
|
||||||
|
//! # impl ControlDevice for Card {}
|
||||||
//! #
|
//! #
|
||||||
//! # fn main() {
|
//! # fn main() {
|
||||||
//! #
|
//! #
|
||||||
|
@ -133,8 +158,8 @@
|
||||||
//! # let mut options = OpenOptions::new();
|
//! # let mut options = OpenOptions::new();
|
||||||
//! # options.read(true);
|
//! # options.read(true);
|
||||||
//! # options.write(true);
|
//! # options.write(true);
|
||||||
//! # let mut device = DrmDevice::new_from_file(
|
//! # let mut device = DrmDevice::new(
|
||||||
//! # options.open("/dev/dri/card0").unwrap(), // try to detect it properly
|
//! # Card(options.open("/dev/dri/card0").unwrap()), // try to detect it properly
|
||||||
//! # None /*put a logger here*/
|
//! # None /*put a logger here*/
|
||||||
//! # ).unwrap();
|
//! # ).unwrap();
|
||||||
//! #
|
//! #
|
||||||
|
@ -145,32 +170,28 @@
|
||||||
//! # .unwrap();
|
//! # .unwrap();
|
||||||
//! # let crtc = res_handles.crtcs()[0];
|
//! # let crtc = res_handles.crtcs()[0];
|
||||||
//! # let mode = connector_info.modes()[0];
|
//! # let mode = connector_info.modes()[0];
|
||||||
//! # let backend: StateToken<DrmBackend> = device.create_backend(
|
//! # let backend = device.create_backend(
|
||||||
//! # event_loop.state(),
|
|
||||||
//! # crtc,
|
//! # crtc,
|
||||||
//! # mode,
|
//! # mode,
|
||||||
//! # vec![connector_info.handle()]
|
//! # vec![connector_info.handle()]
|
||||||
//! # ).unwrap().clone();
|
//! # ).unwrap();
|
||||||
//!
|
//!
|
||||||
//! struct MyDrmHandler;
|
//! struct MyDrmHandler(DrmBackend<Card>);
|
||||||
//!
|
//!
|
||||||
//! impl DrmHandler<DrmBackend> for MyDrmHandler {
|
//! impl DrmHandler<Card> for MyDrmHandler {
|
||||||
//! fn ready<'a, S: Into<StateProxy<'a>>>(
|
//! fn ready(
|
||||||
//! &mut self,
|
//! &mut self,
|
||||||
//! state: S,
|
//! _device: &mut DrmDevice<Card>,
|
||||||
//! _device: &mut DrmDevice<DrmBackend>,
|
|
||||||
//! backend: &StateToken<DrmBackend>,
|
|
||||||
//! _crtc: CrtcHandle,
|
//! _crtc: CrtcHandle,
|
||||||
//! _frame: u32,
|
//! _frame: u32,
|
||||||
//! _duration: Duration)
|
//! _duration: Duration)
|
||||||
//! {
|
//! {
|
||||||
//! // render surfaces and swap again
|
//! // render surfaces and swap again
|
||||||
//! state.into().get(backend).swap_buffers().unwrap();
|
//! self.0.swap_buffers().unwrap();
|
||||||
//! }
|
//! }
|
||||||
//! fn error<'a, S: Into<StateProxy<'a>>>(
|
//! fn error(
|
||||||
//! &mut self,
|
//! &mut self,
|
||||||
//! _state: S,
|
//! device: &mut DrmDevice<Card>,
|
||||||
//! device: &mut DrmDevice<DrmBackend>,
|
|
||||||
//! error: DrmError)
|
//! error: DrmError)
|
||||||
//! {
|
//! {
|
||||||
//! panic!("DrmDevice errored: {}", error);
|
//! panic!("DrmDevice errored: {}", error);
|
||||||
|
@ -178,115 +199,72 @@
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! // render something (like clear_color)
|
//! // render something (like clear_color)
|
||||||
//! event_loop.state().get(&backend).swap_buffers().unwrap();
|
//! backend.swap_buffers().unwrap();
|
||||||
//!
|
//!
|
||||||
//! let device_token = event_loop.state().insert(device);
|
//! let device_token = event_loop.state().insert(device);
|
||||||
//! let _source = drm_device_bind(&mut event_loop, device_token, MyDrmHandler).unwrap();
|
//! let _source = drm_device_bind(&mut event_loop, device_token, MyDrmHandler(backend)).unwrap();
|
||||||
//!
|
//!
|
||||||
//! event_loop.run().unwrap();
|
//! event_loop.run().unwrap();
|
||||||
//! # }
|
//! # }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use backend::graphics::egl::{EGLContext, GlAttributes, PixelFormatRequirements};
|
use backend::graphics::egl::context::{EGLContext, GlAttributes};
|
||||||
use backend::graphics::egl::EGLGraphicsBackend;
|
use backend::graphics::egl::error::Result as EGLResult;
|
||||||
|
use backend::graphics::egl::native::Gbm;
|
||||||
|
use backend::graphics::egl::wayland::{EGLDisplay, EGLWaylandExtensions};
|
||||||
#[cfg(feature = "backend_session")]
|
#[cfg(feature = "backend_session")]
|
||||||
use backend::session::SessionObserver;
|
use backend::session::SessionObserver;
|
||||||
use drm::Device as BasicDevice;
|
use drm::Device as BasicDevice;
|
||||||
use drm::control::{connector, crtc, encoder, Mode, ResourceInfo};
|
use drm::control::{connector, crtc, encoder, Mode, ResourceInfo};
|
||||||
use drm::control::Device as ControlDevice;
|
use drm::control::Device as ControlDevice;
|
||||||
|
use drm::control::framebuffer;
|
||||||
use drm::result::Error as DrmError;
|
use drm::result::Error as DrmError;
|
||||||
use gbm::Device as GbmDevice;
|
use gbm::Device as GbmDevice;
|
||||||
use nix;
|
use nix;
|
||||||
use nix::Result as NixResult;
|
use nix::sys::stat::{dev_t, fstat};
|
||||||
use nix::unistd::close;
|
|
||||||
use std::borrow::Borrow;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs::File;
|
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::io::Result as IoResult;
|
use std::io::Result as IoResult;
|
||||||
use std::mem;
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
|
use std::path::PathBuf;
|
||||||
use std::rc::Rc;
|
use std::rc::{Rc, Weak};
|
||||||
|
use std::sync::{Once, ONCE_INIT};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use wayland_server::{EventLoopHandle, StateProxy, StateToken};
|
use wayland_server::{Display, EventLoopHandle, StateToken};
|
||||||
|
#[cfg(feature = "backend_session")]
|
||||||
|
use wayland_server::StateProxy;
|
||||||
use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest};
|
use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest};
|
||||||
|
|
||||||
mod backend;
|
mod backend;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
|
||||||
pub use self::backend::DrmBackend;
|
pub use self::backend::DrmBackend;
|
||||||
|
use self::backend::DrmBackendInternal;
|
||||||
use self::error::*;
|
use self::error::*;
|
||||||
|
|
||||||
/// Internal struct as required by the drm crate
|
static LOAD: Once = ONCE_INIT;
|
||||||
#[derive(Debug)]
|
|
||||||
pub(crate) struct DrmDev(RawFd);
|
|
||||||
|
|
||||||
impl AsRawFd for DrmDev {
|
|
||||||
fn as_raw_fd(&self) -> RawFd {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl BasicDevice for DrmDev {}
|
|
||||||
impl ControlDevice for DrmDev {}
|
|
||||||
|
|
||||||
impl DrmDev {
|
|
||||||
unsafe fn new_from_fd(fd: RawFd) -> Self {
|
|
||||||
DrmDev(fd)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_from_file(file: File) -> Self {
|
|
||||||
DrmDev(file.into_raw_fd())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
/// Representation of an open drm device node to create rendering backends
|
||||||
pub struct DrmDevice<B: Borrow<DrmBackend> + 'static> {
|
pub struct DrmDevice<A: ControlDevice + 'static> {
|
||||||
context: Rc<Context>,
|
context: Rc<EGLContext<Gbm<framebuffer::Info>, GbmDevice<A>>>,
|
||||||
backends: HashMap<crtc::Handle, StateToken<B>>,
|
|
||||||
old_state: HashMap<crtc::Handle, (crtc::Info, Vec<connector::Handle>)>,
|
old_state: HashMap<crtc::Handle, (crtc::Info, Vec<connector::Handle>)>,
|
||||||
|
device_id: dev_t,
|
||||||
|
backends: HashMap<crtc::Handle, Weak<DrmBackendInternal<A>>>,
|
||||||
active: bool,
|
active: bool,
|
||||||
logger: ::slog::Logger,
|
logger: ::slog::Logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static> DrmDevice<B> {
|
impl<A: ControlDevice + 'static> DrmDevice<A> {
|
||||||
/// Create a new `DrmDevice` from a raw file descriptor
|
/// Create a new `DrmDevice` from an open drm node
|
||||||
///
|
///
|
||||||
/// Returns an error of opening the device failed or context creation was not
|
/// Returns an error if the file is no valid drm node or context creation was not
|
||||||
/// successful.
|
/// successful.
|
||||||
///
|
pub fn new<L>(dev: A, logger: L) -> Result<Self>
|
||||||
/// # 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
|
where
|
||||||
L: Into<Option<::slog::Logger>>,
|
L: Into<Option<::slog::Logger>>,
|
||||||
{
|
{
|
||||||
DrmDevice::new(
|
DrmDevice::new_with_gl_attr(
|
||||||
DrmDev::new_from_fd(fd),
|
dev,
|
||||||
GlAttributes {
|
GlAttributes {
|
||||||
version: None,
|
version: None,
|
||||||
profile: None,
|
profile: None,
|
||||||
|
@ -297,53 +275,11 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static> DrmDevice<B> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new `DrmDevice` from a raw file descriptor and given `GlAttributes`
|
/// Create a new `DrmDevice` from an open drm node 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
|
/// Returns an error if the file is no valid drm node or context creation was not
|
||||||
/// successful.
|
/// successful.
|
||||||
pub fn new_from_file<L>(file: File, logger: L) -> Result<Self>
|
pub fn new_with_gl_attr<L>(dev: A, attributes: GlAttributes, 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
|
where
|
||||||
L: Into<Option<::slog::Logger>>,
|
L: Into<Option<::slog::Logger>>,
|
||||||
{
|
{
|
||||||
|
@ -355,31 +291,67 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static> DrmDevice<B> {
|
||||||
* and loading the DRI driver fails.
|
* and loading the DRI driver fails.
|
||||||
* Workaround this by dlopen()'ing libglapi with RTLD_GLOBAL.
|
* Workaround this by dlopen()'ing libglapi with RTLD_GLOBAL.
|
||||||
*/
|
*/
|
||||||
unsafe {
|
LOAD.call_once(|| unsafe {
|
||||||
nix::libc::dlopen(
|
nix::libc::dlopen(
|
||||||
"libglapi.so.0".as_ptr() as *const _,
|
"libglapi.so.0".as_ptr() as *const _,
|
||||||
nix::libc::RTLD_LAZY | nix::libc::RTLD_GLOBAL,
|
nix::libc::RTLD_LAZY | nix::libc::RTLD_GLOBAL,
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
|
|
||||||
|
let device_id = fstat(dev.as_raw_fd())
|
||||||
|
.chain_err(|| ErrorKind::UnableToGetDeviceId)?
|
||||||
|
.st_rdev;
|
||||||
|
|
||||||
|
let mut drm = DrmDevice {
|
||||||
|
// Open the gbm device from the drm device and create a context based on that
|
||||||
|
context: Rc::new(EGLContext::new(
|
||||||
|
{
|
||||||
|
debug!(log, "Creating gbm device");
|
||||||
|
let gbm = GbmDevice::new(dev).chain_err(|| ErrorKind::GbmInitFailed)?;
|
||||||
|
debug!(log, "Creating egl context from gbm device");
|
||||||
|
gbm
|
||||||
|
},
|
||||||
|
attributes,
|
||||||
|
Default::default(),
|
||||||
|
log.clone(),
|
||||||
|
).map_err(Error::from)?),
|
||||||
|
backends: HashMap::new(),
|
||||||
|
device_id,
|
||||||
|
old_state: HashMap::new(),
|
||||||
|
active: true,
|
||||||
|
logger: log.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
info!(log, "DrmDevice initializing");
|
info!(log, "DrmDevice initializing");
|
||||||
|
|
||||||
// we want to mode-set, so we better be the master
|
// we want to mode-set, so we better be the master
|
||||||
drm.set_master().chain_err(|| ErrorKind::DrmMasterFailed)?;
|
drm.set_master().chain_err(|| ErrorKind::DrmMasterFailed)?;
|
||||||
|
|
||||||
let mut old_state = HashMap::new();
|
let res_handles = drm.resource_handles().chain_err(|| {
|
||||||
let res_handles = drm.resource_handles()
|
ErrorKind::DrmDev(format!(
|
||||||
.chain_err(|| ErrorKind::DrmDev(format!("Loading drm resources on {:?}", drm)))?;
|
"Error loading drm resources on {:?}",
|
||||||
|
drm.dev_path()
|
||||||
|
))
|
||||||
|
})?;
|
||||||
for &con in res_handles.connectors() {
|
for &con in res_handles.connectors() {
|
||||||
let con_info = connector::Info::load_from_device(&drm, con)
|
let con_info = connector::Info::load_from_device(&drm, con).chain_err(|| {
|
||||||
.chain_err(|| ErrorKind::DrmDev(format!("Loading connector info on {:?}", drm)))?;
|
ErrorKind::DrmDev(format!(
|
||||||
|
"Error loading connector info on {:?}",
|
||||||
|
drm.dev_path()
|
||||||
|
))
|
||||||
|
})?;
|
||||||
if let Some(enc) = con_info.current_encoder() {
|
if let Some(enc) = con_info.current_encoder() {
|
||||||
let enc_info = encoder::Info::load_from_device(&drm, enc)
|
let enc_info = encoder::Info::load_from_device(&drm, enc).chain_err(|| {
|
||||||
.chain_err(|| ErrorKind::DrmDev(format!("Loading encoder info on {:?}", drm)))?;
|
ErrorKind::DrmDev(format!(
|
||||||
|
"Error loading encoder info on {:?}",
|
||||||
|
drm.dev_path()
|
||||||
|
))
|
||||||
|
})?;
|
||||||
if let Some(crtc) = enc_info.current_crtc() {
|
if let Some(crtc) = enc_info.current_crtc() {
|
||||||
let info = crtc::Info::load_from_device(&drm, crtc)
|
let info = crtc::Info::load_from_device(&drm, crtc).chain_err(|| {
|
||||||
.chain_err(|| ErrorKind::DrmDev(format!("Loading crtc info on {:?}", drm)))?;
|
ErrorKind::DrmDev(format!("Error loading crtc info on {:?}", drm.dev_path()))
|
||||||
old_state
|
})?;
|
||||||
|
drm.old_state
|
||||||
.entry(crtc)
|
.entry(crtc)
|
||||||
.or_insert((info, Vec::new()))
|
.or_insert((info, Vec::new()))
|
||||||
.1
|
.1
|
||||||
|
@ -388,33 +360,7 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static> DrmDevice<B> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open the gbm device from the drm device and create a context based on that
|
Ok(drm)
|
||||||
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(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(),
|
|
||||||
old_state,
|
|
||||||
active: true,
|
|
||||||
logger: log,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new backend on a given crtc with a given `Mode` for a given amount
|
/// Create a new backend on a given crtc with a given `Mode` for a given amount
|
||||||
|
@ -422,12 +368,11 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static> DrmDevice<B> {
|
||||||
///
|
///
|
||||||
/// Errors if initialization fails or the mode is not available on all given
|
/// Errors if initialization fails or the mode is not available on all given
|
||||||
/// connectors.
|
/// connectors.
|
||||||
pub fn create_backend<'a, I, S>(
|
pub fn create_backend<I>(
|
||||||
&mut self, state: S, crtc: crtc::Handle, mode: Mode, connectors: I
|
&mut self, crtc: crtc::Handle, mode: Mode, connectors: I
|
||||||
) -> Result<&StateToken<B>>
|
) -> Result<DrmBackend<A>>
|
||||||
where
|
where
|
||||||
I: Into<Vec<connector::Handle>>,
|
I: Into<Vec<connector::Handle>>,
|
||||||
S: Into<StateProxy<'a>>,
|
|
||||||
{
|
{
|
||||||
if self.backends.contains_key(&crtc) {
|
if self.backends.contains_key(&crtc) {
|
||||||
bail!(ErrorKind::CrtcAlreadyInUse(crtc));
|
bail!(ErrorKind::CrtcAlreadyInUse(crtc));
|
||||||
|
@ -442,11 +387,10 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static> DrmDevice<B> {
|
||||||
|
|
||||||
// check if we have an encoder for every connector and the mode mode
|
// check if we have an encoder for every connector and the mode mode
|
||||||
for connector in &connectors {
|
for connector in &connectors {
|
||||||
let con_info = connector::Info::load_from_device(self.context.head().head(), *connector)
|
let con_info = connector::Info::load_from_device(self, *connector).chain_err(|| {
|
||||||
.chain_err(|| {
|
|
||||||
ErrorKind::DrmDev(format!(
|
ErrorKind::DrmDev(format!(
|
||||||
"Loading connector info on {:?}",
|
"Error loading connector info on {:?}",
|
||||||
self.context.head().head()
|
self.dev_path()
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
@ -460,10 +404,10 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static> DrmDevice<B> {
|
||||||
.encoders()
|
.encoders()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|encoder| {
|
.map(|encoder| {
|
||||||
encoder::Info::load_from_device(self.context.head().head(), *encoder).chain_err(|| {
|
encoder::Info::load_from_device(self, *encoder).chain_err(|| {
|
||||||
ErrorKind::DrmDev(format!(
|
ErrorKind::DrmDev(format!(
|
||||||
"Loading encoder info on {:?}",
|
"Error loading encoder info on {:?}",
|
||||||
self.context.head().head()
|
self.dev_path()
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -472,8 +416,8 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static> DrmDevice<B> {
|
||||||
// and if any encoder supports the selected crtc
|
// and if any encoder supports the selected crtc
|
||||||
let resource_handles = self.resource_handles().chain_err(|| {
|
let resource_handles = self.resource_handles().chain_err(|| {
|
||||||
ErrorKind::DrmDev(format!(
|
ErrorKind::DrmDev(format!(
|
||||||
"Loading drm resources on {:?}",
|
"Error loading drm resources on {:?}",
|
||||||
self.context.head().head()
|
self.dev_path()
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
if !encoders
|
if !encoders
|
||||||
|
@ -489,65 +433,67 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static> DrmDevice<B> {
|
||||||
|
|
||||||
let logger = self.logger.new(o!("crtc" => format!("{:?}", crtc)));
|
let logger = self.logger.new(o!("crtc" => format!("{:?}", crtc)));
|
||||||
let backend = DrmBackend::new(self.context.clone(), crtc, mode, connectors, logger)?;
|
let backend = DrmBackend::new(self.context.clone(), crtc, mode, connectors, logger)?;
|
||||||
self.backends
|
self.backends.insert(crtc, backend.weak());
|
||||||
.insert(crtc, state.into().insert(backend.into()));
|
Ok(backend)
|
||||||
|
|
||||||
Ok(self.backends.get(&crtc).unwrap())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the current backend for a given crtc if any
|
/// Returns an internal device id, that is unique per boot per system
|
||||||
pub fn backend_for_crtc(&self, crtc: &crtc::Handle) -> Option<&StateToken<B>> {
|
pub fn device_id(&self) -> u64 {
|
||||||
self.backends.get(crtc)
|
self.device_id
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get all belonging backends
|
/// Trait for types representing open devices
|
||||||
pub fn current_backends(&self) -> Vec<&StateToken<B>> {
|
pub trait DevPath {
|
||||||
self.backends.values().collect()
|
/// Returns the path of the open device if possible
|
||||||
}
|
fn dev_path(&self) -> Option<PathBuf>;
|
||||||
|
}
|
||||||
|
|
||||||
/// Destroy the backend using a given crtc if any
|
impl<A: AsRawFd> DevPath for A {
|
||||||
///
|
fn dev_path(&self) -> Option<PathBuf> {
|
||||||
/// ## Panics
|
use std::fs;
|
||||||
/// Panics if the backend is already borrowed from the state
|
|
||||||
pub fn destroy_backend<'a, S>(&mut self, state: S, crtc: &crtc::Handle)
|
|
||||||
where
|
|
||||||
S: Into<StateProxy<'a>>,
|
|
||||||
{
|
|
||||||
if let Some(token) = self.backends.remove(crtc) {
|
|
||||||
state.into().remove(token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Close the device
|
fs::read_link(format!("/proc/self/fd/{:?}", self.as_raw_fd())).ok()
|
||||||
///
|
}
|
||||||
/// ## Warning
|
}
|
||||||
/// Never call this function if the device is managed by another backend e.g. the `UdevBackend`.
|
|
||||||
/// Only use this function for manually initialized devices.
|
impl<A: ControlDevice + 'static> PartialEq for DrmDevice<A> {
|
||||||
pub fn close(self) -> NixResult<()> {
|
fn eq(&self, other: &DrmDevice<A>) -> bool {
|
||||||
let fd = self.as_raw_fd();
|
self.device_id == other.device_id
|
||||||
mem::drop(self);
|
}
|
||||||
close(fd)
|
}
|
||||||
|
impl<A: ControlDevice + 'static> Eq for DrmDevice<A> {}
|
||||||
|
|
||||||
|
impl<A: ControlDevice + 'static> Hash for DrmDevice<A> {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.device_id.hash(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// for users convinience and FdEventSource registering
|
// for users convinience and FdEventSource registering
|
||||||
impl<B: Borrow<DrmBackend> + 'static> AsRawFd for DrmDevice<B> {
|
impl<A: ControlDevice + 'static> AsRawFd for DrmDevice<A> {
|
||||||
fn as_raw_fd(&self) -> RawFd {
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
self.context.head().head().as_raw_fd()
|
self.context.as_raw_fd()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B: Borrow<DrmBackend> + 'static> BasicDevice for DrmDevice<B> {}
|
impl<A: ControlDevice + 'static> BasicDevice for DrmDevice<A> {}
|
||||||
impl<B: Borrow<DrmBackend> + 'static> ControlDevice for DrmDevice<B> {}
|
impl<A: ControlDevice + 'static> ControlDevice for DrmDevice<A> {}
|
||||||
|
|
||||||
impl<B: Borrow<DrmBackend> + 'static> Drop for DrmDevice<B> {
|
impl<A: ControlDevice + 'static> EGLWaylandExtensions for DrmDevice<A> {
|
||||||
|
fn bind_wl_display(&self, display: &Display) -> EGLResult<EGLDisplay> {
|
||||||
|
self.context.bind_wl_display(display)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A: ControlDevice + 'static> Drop for DrmDevice<A> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if Rc::strong_count(&self.context) > 1 {
|
if Rc::strong_count(&self.context) > 1 {
|
||||||
panic!("Pending DrmBackends. Please free all backends before the DrmDevice gets destroyed");
|
panic!("Pending DrmBackends. You need to free all backends before the DrmDevice gets destroyed");
|
||||||
}
|
}
|
||||||
for (handle, (info, connectors)) in self.old_state.drain() {
|
for (handle, (info, connectors)) in self.old_state.drain() {
|
||||||
if let Err(err) = crtc::set(
|
if let Err(err) = crtc::set(
|
||||||
self.context.head().head(),
|
&*self.context,
|
||||||
handle,
|
handle,
|
||||||
info.fb(),
|
info.fb(),
|
||||||
&connectors,
|
&connectors,
|
||||||
|
@ -569,49 +515,29 @@ impl<B: Borrow<DrmBackend> + 'static> Drop for DrmDevice<B> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B: Borrow<DrmBackend> + 'static> Hash for DrmDevice<B> {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
||||||
self.as_raw_fd().hash(state)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Handler for drm node events
|
/// Handler for drm node events
|
||||||
///
|
///
|
||||||
/// See module-level documentation for its use
|
/// See module-level documentation for its use
|
||||||
pub trait DrmHandler<B: Borrow<DrmBackend> + 'static> {
|
pub trait DrmHandler<A: ControlDevice + 'static> {
|
||||||
/// A `DrmBackend` has finished swapping buffers and new frame can now
|
/// The `DrmBackend` of crtc has finished swapping buffers and new frame can now
|
||||||
/// (and should be immediately) be rendered.
|
/// (and should be immediately) be rendered.
|
||||||
///
|
fn ready(&mut self, device: &mut DrmDevice<A>, crtc: crtc::Handle, frame: u32, duration: Duration);
|
||||||
/// The `id` argument is the `Id` of the `DrmBackend` that finished rendering,
|
|
||||||
/// check using `DrmBackend::is`.
|
|
||||||
///
|
|
||||||
/// ## Panics
|
|
||||||
/// The device is already borrowed from the given `state`. Borrowing it again will panic
|
|
||||||
/// and is not necessary as it is already provided via the `device` parameter.
|
|
||||||
fn ready<'a, S: Into<StateProxy<'a>>>(
|
|
||||||
&mut self, state: S, device: &mut DrmDevice<B>, backend: &StateToken<B>, crtc: crtc::Handle,
|
|
||||||
frame: u32, duration: Duration,
|
|
||||||
);
|
|
||||||
/// The `DrmDevice` has thrown an error.
|
/// The `DrmDevice` has thrown an error.
|
||||||
///
|
///
|
||||||
/// The related backends are most likely *not* usable anymore and
|
/// The related backends are most likely *not* usable anymore and
|
||||||
/// the whole stack has to be recreated..
|
/// the whole stack has to be recreated..
|
||||||
///
|
fn error(&mut self, device: &mut DrmDevice<A>, error: DrmError);
|
||||||
/// ## Panics
|
|
||||||
/// The device is already borrowed from the given `state`. Borrowing it again will panic
|
|
||||||
/// and is not necessary as it is already provided via the `device` parameter.
|
|
||||||
fn error<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &mut DrmDevice<B>, error: DrmError);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Bind a `DrmDevice` to an `EventLoop`,
|
/// Bind a `DrmDevice` to an `EventLoop`,
|
||||||
///
|
///
|
||||||
/// This will cause it to recieve events and feed them into an `DrmHandler`
|
/// This will cause it to recieve events and feed them into an `DrmHandler`
|
||||||
pub fn drm_device_bind<B, H>(
|
pub fn drm_device_bind<A, H>(
|
||||||
evlh: &mut EventLoopHandle, device: StateToken<DrmDevice<B>>, handler: H
|
evlh: &mut EventLoopHandle, device: StateToken<DrmDevice<A>>, handler: H
|
||||||
) -> IoResult<FdEventSource<(StateToken<DrmDevice<B>>, H)>>
|
) -> IoResult<FdEventSource<(StateToken<DrmDevice<A>>, H)>>
|
||||||
where
|
where
|
||||||
B: From<DrmBackend> + Borrow<DrmBackend> + 'static,
|
A: ControlDevice + 'static,
|
||||||
H: DrmHandler<B> + 'static,
|
H: DrmHandler<A> + 'static,
|
||||||
{
|
{
|
||||||
let fd = evlh.state().get(&device).as_raw_fd();
|
let fd = evlh.state().get(&device).as_raw_fd();
|
||||||
evlh.add_fd_event_source(
|
evlh.add_fd_event_source(
|
||||||
|
@ -622,10 +548,10 @@ where
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_event_source_implementation<B, H>() -> FdEventSourceImpl<(StateToken<DrmDevice<B>>, H)>
|
fn fd_event_source_implementation<A, H>() -> FdEventSourceImpl<(StateToken<DrmDevice<A>>, H)>
|
||||||
where
|
where
|
||||||
B: From<DrmBackend> + Borrow<DrmBackend> + 'static,
|
A: ControlDevice + 'static,
|
||||||
H: DrmHandler<B> + 'static,
|
H: DrmHandler<A> + 'static,
|
||||||
{
|
{
|
||||||
FdEventSourceImpl {
|
FdEventSourceImpl {
|
||||||
ready: |evlh, &mut (ref mut dev_token, ref mut handler), _, _| {
|
ready: |evlh, &mut (ref mut dev_token, ref mut handler), _, _| {
|
||||||
|
@ -639,44 +565,40 @@ where
|
||||||
match events {
|
match events {
|
||||||
Ok(events) => for event in events {
|
Ok(events) => for event in events {
|
||||||
if let crtc::Event::PageFlip(event) = event {
|
if let crtc::Event::PageFlip(event) = event {
|
||||||
evlh.state().with_value(dev_token, |state, mut dev| {
|
let dev = evlh.state().get_mut(dev_token);
|
||||||
if dev.active {
|
if dev.active {
|
||||||
if let Some(backend_token) = dev.backend_for_crtc(&event.crtc).cloned() {
|
if let Some(backend) = dev.backends
|
||||||
|
.get(&event.crtc)
|
||||||
|
.iter()
|
||||||
|
.flat_map(|x| x.upgrade())
|
||||||
|
.next()
|
||||||
|
{
|
||||||
// we can now unlock the buffer
|
// we can now unlock the buffer
|
||||||
state.get(&backend_token).borrow().unlock_buffer();
|
backend.unlock_buffer();
|
||||||
trace!(logger, "Handling event for backend {:?}", event.crtc);
|
trace!(logger, "Handling event for backend {:?}", event.crtc);
|
||||||
// and then call the user to render the next frame
|
// and then call the user to render the next frame
|
||||||
handler.ready(
|
handler.ready(dev, event.crtc, event.frame, event.duration);
|
||||||
state,
|
} else {
|
||||||
&mut dev,
|
dev.backends.remove(&event.crtc);
|
||||||
&backend_token,
|
|
||||||
event.crtc,
|
|
||||||
event.frame,
|
|
||||||
event.duration,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(err) => evlh.state().with_value(dev_token, |state, mut dev| {
|
Err(err) => handler.error(evlh.state().get_mut(dev_token), err),
|
||||||
handler.error(state, &mut dev, err)
|
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
error: |evlh, &mut (ref mut dev_token, ref mut handler), _, error| {
|
error: |evlh, &mut (ref mut dev_token, ref mut handler), _, error| {
|
||||||
evlh.state().with_value(dev_token, |state, mut dev| {
|
let mut dev = evlh.state().get_mut(dev_token);
|
||||||
warn!(dev.logger, "DrmDevice errored: {}", error);
|
warn!(dev.logger, "DrmDevice errored: {}", error);
|
||||||
handler.error(state, &mut dev, error.into());
|
handler.error(&mut dev, error.into());
|
||||||
})
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "backend_session")]
|
#[cfg(feature = "backend_session")]
|
||||||
impl<B: Borrow<DrmBackend> + 'static> SessionObserver for StateToken<DrmDevice<B>> {
|
impl<A: ControlDevice + 'static> SessionObserver for StateToken<DrmDevice<A>> {
|
||||||
fn pause<'a>(&mut self, state: &mut StateProxy<'a>) {
|
fn pause<'a>(&mut self, state: &mut StateProxy<'a>) {
|
||||||
let device: &mut DrmDevice<B> = state.get_mut(self);
|
let device = state.get_mut(self);
|
||||||
device.active = false;
|
device.active = false;
|
||||||
if let Err(err) = device.drop_master() {
|
if let Err(err) = device.drop_master() {
|
||||||
error!(
|
error!(
|
||||||
|
@ -687,7 +609,7 @@ impl<B: Borrow<DrmBackend> + 'static> SessionObserver for StateToken<DrmDevice<B
|
||||||
}
|
}
|
||||||
|
|
||||||
fn activate<'a>(&mut self, state: &mut StateProxy<'a>) {
|
fn activate<'a>(&mut self, state: &mut StateProxy<'a>) {
|
||||||
state.with_value(self, |state, device| {
|
let device = state.get_mut(self);
|
||||||
device.active = true;
|
device.active = true;
|
||||||
if let Err(err) = device.set_master() {
|
if let Err(err) = device.set_master() {
|
||||||
crit!(
|
crit!(
|
||||||
|
@ -696,18 +618,22 @@ impl<B: Borrow<DrmBackend> + 'static> SessionObserver for StateToken<DrmDevice<B
|
||||||
err
|
err
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
for token in device.backends.values() {
|
let mut crtcs = Vec::new();
|
||||||
let backend = state.get(token);
|
for (crtc, backend) in device.backends.iter() {
|
||||||
if let Err(err) = backend.borrow().swap_buffers() {
|
if let Some(backend) = backend.upgrade() {
|
||||||
// TODO handle this better?
|
backend.unlock_buffer();
|
||||||
|
if let Err(err) = backend.page_flip(None) {
|
||||||
error!(
|
error!(
|
||||||
device.logger,
|
device.logger,
|
||||||
"Failed to activate crtc ({:?}) again. Error: {}",
|
"Failed to activate crtc ({:?}) again. Error: {}", crtc, err
|
||||||
backend.borrow().crtc(),
|
|
||||||
err
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
crtcs.push(*crtc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for crtc in crtcs {
|
||||||
|
device.backends.remove(&crtc);
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,613 @@
|
||||||
|
//! EGL context related structs
|
||||||
|
|
||||||
|
use super::{ffi, EGLSurface, PixelFormat};
|
||||||
|
use super::error::*;
|
||||||
|
use super::native;
|
||||||
|
#[cfg(feature = "backend_drm")]
|
||||||
|
use drm::Device as BasicDevice;
|
||||||
|
#[cfg(feature = "backend_drm")]
|
||||||
|
use drm::control::Device as ControlDevice;
|
||||||
|
#[cfg(feature = "backend_drm")]
|
||||||
|
use gbm::Device as GbmDevice;
|
||||||
|
use nix::libc::{c_int, c_void};
|
||||||
|
use slog;
|
||||||
|
use std::ffi::{CStr, CString};
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::mem;
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
#[cfg(feature = "backend_drm")]
|
||||||
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
|
use std::ptr;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
/// EGL context for rendering
|
||||||
|
pub struct EGLContext<B: native::Backend, N: native::NativeDisplay<B>> {
|
||||||
|
native: N,
|
||||||
|
pub(crate) context: Rc<ffi::egl::types::EGLContext>,
|
||||||
|
pub(crate) display: Rc<ffi::egl::types::EGLDisplay>,
|
||||||
|
pub(crate) config_id: ffi::egl::types::EGLConfig,
|
||||||
|
pub(crate) surface_attributes: Vec<c_int>,
|
||||||
|
pixel_format: PixelFormat,
|
||||||
|
pub(crate) wl_drm_support: bool,
|
||||||
|
pub(crate) egl_to_texture_support: bool,
|
||||||
|
logger: slog::Logger,
|
||||||
|
_backend: PhantomData<B>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: native::Backend, N: native::NativeDisplay<B>> Deref for EGLContext<B, N> {
|
||||||
|
type Target = N;
|
||||||
|
fn deref(&self) -> &N {
|
||||||
|
&self.native
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: native::Backend, N: native::NativeDisplay<B>> DerefMut for EGLContext<B, N> {
|
||||||
|
fn deref_mut(&mut self) -> &mut N {
|
||||||
|
&mut self.native
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: native::Backend, N: native::NativeDisplay<B>> EGLContext<B, N> {
|
||||||
|
/// Create a new `EGLContext` from a given `NativeDisplay`
|
||||||
|
pub fn new<L>(
|
||||||
|
native: N, attributes: GlAttributes, reqs: PixelFormatRequirements, logger: L
|
||||||
|
) -> Result<EGLContext<B, N>>
|
||||||
|
where
|
||||||
|
L: Into<Option<::slog::Logger>>,
|
||||||
|
{
|
||||||
|
let log = ::slog_or_stdlog(logger.into()).new(o!("smithay_module" => "renderer_egl"));
|
||||||
|
let ptr = native.ptr()?;
|
||||||
|
let (
|
||||||
|
context,
|
||||||
|
display,
|
||||||
|
config_id,
|
||||||
|
surface_attributes,
|
||||||
|
pixel_format,
|
||||||
|
wl_drm_support,
|
||||||
|
egl_to_texture_support,
|
||||||
|
) = unsafe { EGLContext::<B, N>::new_internal(ptr, attributes, reqs, log.clone()) }?;
|
||||||
|
|
||||||
|
Ok(EGLContext {
|
||||||
|
native,
|
||||||
|
context,
|
||||||
|
display,
|
||||||
|
config_id,
|
||||||
|
surface_attributes,
|
||||||
|
pixel_format,
|
||||||
|
wl_drm_support,
|
||||||
|
egl_to_texture_support,
|
||||||
|
logger: log,
|
||||||
|
_backend: PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn new_internal(
|
||||||
|
ptr: ffi::NativeDisplayType, mut attributes: GlAttributes, reqs: PixelFormatRequirements,
|
||||||
|
log: ::slog::Logger,
|
||||||
|
) -> Result<
|
||||||
|
(
|
||||||
|
Rc<ffi::egl::types::EGLContext>,
|
||||||
|
Rc<ffi::egl::types::EGLDisplay>,
|
||||||
|
ffi::egl::types::EGLConfig,
|
||||||
|
Vec<c_int>,
|
||||||
|
PixelFormat,
|
||||||
|
bool,
|
||||||
|
bool,
|
||||||
|
),
|
||||||
|
> {
|
||||||
|
// If no version is given, try OpenGLES 3.0, if available,
|
||||||
|
// fallback to 2.0 otherwise
|
||||||
|
let version = match attributes.version {
|
||||||
|
Some((3, x)) => (3, x),
|
||||||
|
Some((2, x)) => (2, x),
|
||||||
|
None => {
|
||||||
|
debug!(log, "Trying to initialize EGL with OpenGLES 3.0");
|
||||||
|
attributes.version = Some((3, 0));
|
||||||
|
match EGLContext::<B, N>::new_internal(ptr, attributes, reqs, log.clone()) {
|
||||||
|
Ok(x) => return Ok(x),
|
||||||
|
Err(err) => {
|
||||||
|
warn!(log, "EGL OpenGLES 3.0 Initialization failed with {}", err);
|
||||||
|
debug!(log, "Trying to initialize EGL with OpenGLES 2.0");
|
||||||
|
attributes.version = Some((2, 0));
|
||||||
|
return EGLContext::<B, N>::new_internal(ptr, attributes, reqs, log);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some((1, x)) => {
|
||||||
|
error!(
|
||||||
|
log,
|
||||||
|
"OpenGLES 1.* is not supported by the EGL renderer backend"
|
||||||
|
);
|
||||||
|
bail!(ErrorKind::OpenGlVersionNotSupported((1, x)));
|
||||||
|
}
|
||||||
|
Some(version) => {
|
||||||
|
error!(
|
||||||
|
log,
|
||||||
|
"OpenGLES {:?} is unknown and not supported by the EGL renderer backend", version
|
||||||
|
);
|
||||||
|
bail!(ErrorKind::OpenGlVersionNotSupported(version));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ffi::egl::LOAD.call_once(|| {
|
||||||
|
fn constrain<F>(f: F) -> F
|
||||||
|
where
|
||||||
|
F: for<'a> Fn(&'a str) -> *const ::std::os::raw::c_void,
|
||||||
|
{
|
||||||
|
f
|
||||||
|
};
|
||||||
|
ffi::egl::load_with(|sym| {
|
||||||
|
let name = CString::new(sym).unwrap();
|
||||||
|
let symbol = ffi::egl::LIB.get::<*mut c_void>(name.as_bytes());
|
||||||
|
match symbol {
|
||||||
|
Ok(x) => *x as *const _,
|
||||||
|
Err(_) => ptr::null(),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let proc_address = constrain(|sym| {
|
||||||
|
let addr = CString::new(sym).unwrap();
|
||||||
|
let addr = addr.as_ptr();
|
||||||
|
ffi::egl::GetProcAddress(addr) as *const _
|
||||||
|
});
|
||||||
|
ffi::egl::load_with(&proc_address);
|
||||||
|
ffi::egl::BindWaylandDisplayWL::load_with(&proc_address);
|
||||||
|
ffi::egl::UnbindWaylandDisplayWL::load_with(&proc_address);
|
||||||
|
ffi::egl::QueryWaylandBufferWL::load_with(&proc_address);
|
||||||
|
ffi::gl::load_with(&proc_address);
|
||||||
|
});
|
||||||
|
|
||||||
|
// the first step is to query the list of extensions without any display, if supported
|
||||||
|
let dp_extensions = {
|
||||||
|
let p = ffi::egl::QueryString(ffi::egl::NO_DISPLAY, ffi::egl::EXTENSIONS as i32);
|
||||||
|
|
||||||
|
// this possibility is available only with EGL 1.5 or EGL_EXT_platform_base, otherwise
|
||||||
|
// `eglQueryString` returns an error
|
||||||
|
if p.is_null() {
|
||||||
|
vec![]
|
||||||
|
} else {
|
||||||
|
let p = CStr::from_ptr(p);
|
||||||
|
let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| String::new());
|
||||||
|
list.split(' ').map(|e| e.to_string()).collect::<Vec<_>>()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
debug!(log, "EGL No-Display Extensions: {:?}", dp_extensions);
|
||||||
|
|
||||||
|
let display = B::get_display(
|
||||||
|
ptr,
|
||||||
|
|e: &str| dp_extensions.iter().any(|s| s == e),
|
||||||
|
log.clone(),
|
||||||
|
);
|
||||||
|
if display == ffi::egl::NO_DISPLAY {
|
||||||
|
error!(log, "EGL Display is not valid");
|
||||||
|
bail!(ErrorKind::DisplayNotSupported);
|
||||||
|
}
|
||||||
|
|
||||||
|
let egl_version = {
|
||||||
|
let mut major: ffi::egl::types::EGLint = mem::uninitialized();
|
||||||
|
let mut minor: ffi::egl::types::EGLint = mem::uninitialized();
|
||||||
|
|
||||||
|
if ffi::egl::Initialize(display, &mut major, &mut minor) == 0 {
|
||||||
|
bail!(ErrorKind::InitFailed);
|
||||||
|
}
|
||||||
|
|
||||||
|
info!(log, "EGL Initialized");
|
||||||
|
info!(log, "EGL Version: {:?}", (major, minor));
|
||||||
|
|
||||||
|
(major, minor)
|
||||||
|
};
|
||||||
|
|
||||||
|
// the list of extensions supported by the client once initialized is different from the
|
||||||
|
// list of extensions obtained earlier
|
||||||
|
let extensions = if egl_version >= (1, 2) {
|
||||||
|
let p = CStr::from_ptr(ffi::egl::QueryString(display, ffi::egl::EXTENSIONS as i32));
|
||||||
|
let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| String::new());
|
||||||
|
list.split(' ').map(|e| e.to_string()).collect::<Vec<_>>()
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
};
|
||||||
|
|
||||||
|
info!(log, "EGL Extensions: {:?}", extensions);
|
||||||
|
|
||||||
|
if egl_version >= (1, 2) && ffi::egl::BindAPI(ffi::egl::OPENGL_ES_API) == 0 {
|
||||||
|
error!(
|
||||||
|
log,
|
||||||
|
"OpenGLES not supported by the underlying EGL implementation"
|
||||||
|
);
|
||||||
|
bail!(ErrorKind::OpenGlesNotSupported);
|
||||||
|
}
|
||||||
|
|
||||||
|
let descriptor = {
|
||||||
|
let mut out: Vec<c_int> = Vec::with_capacity(37);
|
||||||
|
|
||||||
|
if egl_version >= (1, 2) {
|
||||||
|
trace!(log, "Setting COLOR_BUFFER_TYPE to RGB_BUFFER");
|
||||||
|
out.push(ffi::egl::COLOR_BUFFER_TYPE as c_int);
|
||||||
|
out.push(ffi::egl::RGB_BUFFER as c_int);
|
||||||
|
}
|
||||||
|
|
||||||
|
trace!(log, "Setting SURFACE_TYPE to WINDOW");
|
||||||
|
|
||||||
|
out.push(ffi::egl::SURFACE_TYPE as c_int);
|
||||||
|
// TODO: Some versions of Mesa report a BAD_ATTRIBUTE error
|
||||||
|
// if we ask for PBUFFER_BIT as well as WINDOW_BIT
|
||||||
|
out.push((ffi::egl::WINDOW_BIT) as c_int);
|
||||||
|
|
||||||
|
match version {
|
||||||
|
(3, _) => {
|
||||||
|
if egl_version < (1, 3) {
|
||||||
|
error!(
|
||||||
|
log,
|
||||||
|
"OpenglES 3.* is not supported on EGL Versions lower then 1.3"
|
||||||
|
);
|
||||||
|
bail!(ErrorKind::NoAvailablePixelFormat);
|
||||||
|
}
|
||||||
|
trace!(log, "Setting RENDERABLE_TYPE to OPENGL_ES3");
|
||||||
|
out.push(ffi::egl::RENDERABLE_TYPE as c_int);
|
||||||
|
out.push(ffi::egl::OPENGL_ES3_BIT as c_int);
|
||||||
|
trace!(log, "Setting CONFORMANT to OPENGL_ES3");
|
||||||
|
out.push(ffi::egl::CONFORMANT as c_int);
|
||||||
|
out.push(ffi::egl::OPENGL_ES3_BIT as c_int);
|
||||||
|
}
|
||||||
|
(2, _) => {
|
||||||
|
if egl_version < (1, 3) {
|
||||||
|
error!(
|
||||||
|
log,
|
||||||
|
"OpenglES 2.* is not supported on EGL Versions lower then 1.3"
|
||||||
|
);
|
||||||
|
bail!(ErrorKind::NoAvailablePixelFormat);
|
||||||
|
}
|
||||||
|
trace!(log, "Setting RENDERABLE_TYPE to OPENGL_ES2");
|
||||||
|
out.push(ffi::egl::RENDERABLE_TYPE as c_int);
|
||||||
|
out.push(ffi::egl::OPENGL_ES2_BIT as c_int);
|
||||||
|
trace!(log, "Setting CONFORMANT to OPENGL_ES2");
|
||||||
|
out.push(ffi::egl::CONFORMANT as c_int);
|
||||||
|
out.push(ffi::egl::OPENGL_ES2_BIT as c_int);
|
||||||
|
}
|
||||||
|
(_, _) => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(hardware_accelerated) = reqs.hardware_accelerated {
|
||||||
|
out.push(ffi::egl::CONFIG_CAVEAT as c_int);
|
||||||
|
out.push(if hardware_accelerated {
|
||||||
|
trace!(log, "Setting CONFIG_CAVEAT to NONE");
|
||||||
|
ffi::egl::NONE as c_int
|
||||||
|
} else {
|
||||||
|
trace!(log, "Setting CONFIG_CAVEAT to SLOW_CONFIG");
|
||||||
|
ffi::egl::SLOW_CONFIG as c_int
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(color) = reqs.color_bits {
|
||||||
|
trace!(log, "Setting RED_SIZE to {}", color / 3);
|
||||||
|
out.push(ffi::egl::RED_SIZE as c_int);
|
||||||
|
out.push((color / 3) as c_int);
|
||||||
|
trace!(
|
||||||
|
log,
|
||||||
|
"Setting GREEN_SIZE to {}",
|
||||||
|
color / 3 + if color % 3 != 0 { 1 } else { 0 }
|
||||||
|
);
|
||||||
|
out.push(ffi::egl::GREEN_SIZE as c_int);
|
||||||
|
out.push((color / 3 + if color % 3 != 0 { 1 } else { 0 }) as c_int);
|
||||||
|
trace!(
|
||||||
|
log,
|
||||||
|
"Setting BLUE_SIZE to {}",
|
||||||
|
color / 3 + if color % 3 == 2 { 1 } else { 0 }
|
||||||
|
);
|
||||||
|
out.push(ffi::egl::BLUE_SIZE as c_int);
|
||||||
|
out.push((color / 3 + if color % 3 == 2 { 1 } else { 0 }) as c_int);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(alpha) = reqs.alpha_bits {
|
||||||
|
trace!(log, "Setting ALPHA_SIZE to {}", alpha);
|
||||||
|
out.push(ffi::egl::ALPHA_SIZE as c_int);
|
||||||
|
out.push(alpha as c_int);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(depth) = reqs.depth_bits {
|
||||||
|
trace!(log, "Setting DEPTH_SIZE to {}", depth);
|
||||||
|
out.push(ffi::egl::DEPTH_SIZE as c_int);
|
||||||
|
out.push(depth as c_int);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(stencil) = reqs.stencil_bits {
|
||||||
|
trace!(log, "Setting STENCIL_SIZE to {}", stencil);
|
||||||
|
out.push(ffi::egl::STENCIL_SIZE as c_int);
|
||||||
|
out.push(stencil as c_int);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(multisampling) = reqs.multisampling {
|
||||||
|
trace!(log, "Setting SAMPLES to {}", multisampling);
|
||||||
|
out.push(ffi::egl::SAMPLES as c_int);
|
||||||
|
out.push(multisampling as c_int);
|
||||||
|
}
|
||||||
|
|
||||||
|
if reqs.stereoscopy {
|
||||||
|
error!(log, "Stereoscopy is currently unsupported (sorry!)");
|
||||||
|
bail!(ErrorKind::NoAvailablePixelFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
out.push(ffi::egl::NONE as c_int);
|
||||||
|
out
|
||||||
|
};
|
||||||
|
|
||||||
|
// calling `eglChooseConfig`
|
||||||
|
let mut config_id = mem::uninitialized();
|
||||||
|
let mut num_configs = mem::uninitialized();
|
||||||
|
if ffi::egl::ChooseConfig(
|
||||||
|
display,
|
||||||
|
descriptor.as_ptr(),
|
||||||
|
&mut config_id,
|
||||||
|
1,
|
||||||
|
&mut num_configs,
|
||||||
|
) == 0
|
||||||
|
{
|
||||||
|
bail!(ErrorKind::ConfigFailed);
|
||||||
|
}
|
||||||
|
if num_configs == 0 {
|
||||||
|
error!(log, "No matching color format found");
|
||||||
|
bail!(ErrorKind::NoAvailablePixelFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
// analyzing each config
|
||||||
|
macro_rules! attrib {
|
||||||
|
($display:expr, $config:expr, $attr:expr) => (
|
||||||
|
{
|
||||||
|
let mut value = mem::uninitialized();
|
||||||
|
let res = ffi::egl::GetConfigAttrib($display, $config,
|
||||||
|
$attr as ffi::egl::types::EGLint, &mut value);
|
||||||
|
if res == 0 {
|
||||||
|
bail!(ErrorKind::ConfigFailed);
|
||||||
|
}
|
||||||
|
value
|
||||||
|
}
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let desc = PixelFormat {
|
||||||
|
hardware_accelerated: attrib!(display, config_id, ffi::egl::CONFIG_CAVEAT)
|
||||||
|
!= ffi::egl::SLOW_CONFIG as i32,
|
||||||
|
color_bits: attrib!(display, config_id, ffi::egl::RED_SIZE) as u8
|
||||||
|
+ attrib!(display, config_id, ffi::egl::BLUE_SIZE) as u8
|
||||||
|
+ attrib!(display, config_id, ffi::egl::GREEN_SIZE) as u8,
|
||||||
|
alpha_bits: attrib!(display, config_id, ffi::egl::ALPHA_SIZE) as u8,
|
||||||
|
depth_bits: attrib!(display, config_id, ffi::egl::DEPTH_SIZE) as u8,
|
||||||
|
stencil_bits: attrib!(display, config_id, ffi::egl::STENCIL_SIZE) as u8,
|
||||||
|
stereoscopy: false,
|
||||||
|
double_buffer: true,
|
||||||
|
multisampling: match attrib!(display, config_id, ffi::egl::SAMPLES) {
|
||||||
|
0 | 1 => None,
|
||||||
|
a => Some(a as u16),
|
||||||
|
},
|
||||||
|
srgb: false, // TODO: use EGL_KHR_gl_colorspace to know that
|
||||||
|
};
|
||||||
|
|
||||||
|
info!(log, "Selected color format: {:?}", desc);
|
||||||
|
|
||||||
|
let mut context_attributes = Vec::with_capacity(10);
|
||||||
|
|
||||||
|
if egl_version >= (1, 5) || extensions.iter().any(|s| *s == "EGL_KHR_create_context") {
|
||||||
|
trace!(log, "Setting CONTEXT_MAJOR_VERSION to {}", version.0);
|
||||||
|
context_attributes.push(ffi::egl::CONTEXT_MAJOR_VERSION as i32);
|
||||||
|
context_attributes.push(version.0 as i32);
|
||||||
|
trace!(log, "Setting CONTEXT_MINOR_VERSION to {}", version.1);
|
||||||
|
context_attributes.push(ffi::egl::CONTEXT_MINOR_VERSION as i32);
|
||||||
|
context_attributes.push(version.1 as i32);
|
||||||
|
|
||||||
|
if attributes.debug && egl_version >= (1, 5) {
|
||||||
|
trace!(log, "Setting CONTEXT_OPENGL_DEBUG to TRUE");
|
||||||
|
context_attributes.push(ffi::egl::CONTEXT_OPENGL_DEBUG as i32);
|
||||||
|
context_attributes.push(ffi::egl::TRUE as i32);
|
||||||
|
}
|
||||||
|
|
||||||
|
context_attributes.push(ffi::egl::CONTEXT_FLAGS_KHR as i32);
|
||||||
|
context_attributes.push(0);
|
||||||
|
} else if egl_version >= (1, 3) {
|
||||||
|
trace!(log, "Setting CONTEXT_CLIENT_VERSION to {}", version.0);
|
||||||
|
context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32);
|
||||||
|
context_attributes.push(version.0 as i32);
|
||||||
|
}
|
||||||
|
|
||||||
|
context_attributes.push(ffi::egl::NONE as i32);
|
||||||
|
|
||||||
|
trace!(log, "Creating EGL context...");
|
||||||
|
let context = ffi::egl::CreateContext(display, config_id, ptr::null(), context_attributes.as_ptr());
|
||||||
|
|
||||||
|
if context.is_null() {
|
||||||
|
match ffi::egl::GetError() as u32 {
|
||||||
|
ffi::egl::BAD_ATTRIBUTE => bail!(ErrorKind::CreationFailed),
|
||||||
|
err_no => bail!(ErrorKind::Unknown(err_no)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debug!(log, "EGL context successfully created");
|
||||||
|
|
||||||
|
let surface_attributes = {
|
||||||
|
let mut out: Vec<c_int> = Vec::with_capacity(3);
|
||||||
|
|
||||||
|
match reqs.double_buffer {
|
||||||
|
Some(true) => {
|
||||||
|
trace!(log, "Setting RENDER_BUFFER to BACK_BUFFER");
|
||||||
|
out.push(ffi::egl::RENDER_BUFFER as c_int);
|
||||||
|
out.push(ffi::egl::BACK_BUFFER as c_int);
|
||||||
|
}
|
||||||
|
Some(false) => {
|
||||||
|
trace!(log, "Setting RENDER_BUFFER to SINGLE_BUFFER");
|
||||||
|
out.push(ffi::egl::RENDER_BUFFER as c_int);
|
||||||
|
out.push(ffi::egl::SINGLE_BUFFER as c_int);
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
out.push(ffi::egl::NONE as i32);
|
||||||
|
out
|
||||||
|
};
|
||||||
|
|
||||||
|
info!(log, "EGL context created");
|
||||||
|
|
||||||
|
// make current and get list of gl extensions
|
||||||
|
ffi::egl::MakeCurrent(
|
||||||
|
display as *const _,
|
||||||
|
ptr::null(),
|
||||||
|
ptr::null(),
|
||||||
|
context as *const _,
|
||||||
|
);
|
||||||
|
|
||||||
|
// the list of gl extensions supported by the context
|
||||||
|
let gl_extensions = {
|
||||||
|
let data = CStr::from_ptr(ffi::gl::GetString(ffi::gl::EXTENSIONS) as *const _)
|
||||||
|
.to_bytes()
|
||||||
|
.to_vec();
|
||||||
|
let list = String::from_utf8(data).unwrap();
|
||||||
|
list.split(' ').map(|e| e.to_string()).collect::<Vec<_>>()
|
||||||
|
};
|
||||||
|
|
||||||
|
info!(log, "GL Extensions: {:?}", gl_extensions);
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
Rc::new(context as *const _),
|
||||||
|
Rc::new(display as *const _),
|
||||||
|
config_id,
|
||||||
|
surface_attributes,
|
||||||
|
desc,
|
||||||
|
extensions
|
||||||
|
.iter()
|
||||||
|
.any(|s| *s == "EGL_WL_bind_wayland_display"),
|
||||||
|
gl_extensions
|
||||||
|
.iter()
|
||||||
|
.any(|s| *s == "GL_OES_EGL_image" || *s == "GL_OES_EGL_image_base"),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a surface for rendering
|
||||||
|
pub fn create_surface(&self, args: N::Arguments) -> Result<EGLSurface<B::Surface>> {
|
||||||
|
trace!(self.logger, "Creating EGL window surface.");
|
||||||
|
let res = EGLSurface::new(
|
||||||
|
self,
|
||||||
|
self.native
|
||||||
|
.create_surface(args)
|
||||||
|
.chain_err(|| ErrorKind::SurfaceCreationFailed)?,
|
||||||
|
);
|
||||||
|
if res.is_ok() {
|
||||||
|
debug!(self.logger, "EGL surface successfully created");
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the address of an OpenGL function.
|
||||||
|
///
|
||||||
|
/// Supposes that the context has been made current before this function is called.
|
||||||
|
pub unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void {
|
||||||
|
let addr = CString::new(symbol.as_bytes()).unwrap();
|
||||||
|
let addr = addr.as_ptr();
|
||||||
|
ffi::egl::GetProcAddress(addr) as *const _
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the OpenGL context is the current one in the thread.
|
||||||
|
pub fn is_current(&self) -> bool {
|
||||||
|
unsafe { ffi::egl::GetCurrentContext() == (*self.context) as *const _ }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the pixel format of the main framebuffer of the context.
|
||||||
|
pub fn get_pixel_format(&self) -> PixelFormat {
|
||||||
|
self.pixel_format
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<B: native::Backend, N: native::NativeDisplay<B> + Send> Send for EGLContext<B, N> {}
|
||||||
|
unsafe impl<B: native::Backend, N: native::NativeDisplay<B> + Sync> Sync for EGLContext<B, N> {}
|
||||||
|
|
||||||
|
impl<B: native::Backend, N: native::NativeDisplay<B>> Drop for EGLContext<B, N> {
|
||||||
|
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
|
||||||
|
ffi::egl::DestroyContext((*self.display) as *const _, (*self.context) as *const _);
|
||||||
|
ffi::egl::Terminate((*self.display) as *const _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "backend_drm")]
|
||||||
|
impl<T: 'static, A: AsRawFd + 'static> AsRawFd for EGLContext<native::Gbm<T>, GbmDevice<A>> {
|
||||||
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
|
self.native.as_raw_fd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "backend_drm")]
|
||||||
|
impl<T: 'static, A: BasicDevice + 'static> BasicDevice for EGLContext<native::Gbm<T>, GbmDevice<A>> {}
|
||||||
|
#[cfg(feature = "backend_drm")]
|
||||||
|
impl<T: 'static, A: ControlDevice + 'static> ControlDevice for EGLContext<native::Gbm<T>, GbmDevice<A>> {}
|
||||||
|
|
||||||
|
/// Attributes to use when creating an OpenGL context.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct GlAttributes {
|
||||||
|
/// Describes the OpenGL API and version that are being requested when a context is created.
|
||||||
|
///
|
||||||
|
/// `Some(3, 0)` will request a OpenGL ES 3.0 context for example.
|
||||||
|
/// `None` means "don't care" (minimum will be 2.0).
|
||||||
|
pub version: Option<(u8, u8)>,
|
||||||
|
/// OpenGL profile to use
|
||||||
|
pub profile: Option<GlProfile>,
|
||||||
|
/// Whether to enable the debug flag of the context.
|
||||||
|
///
|
||||||
|
/// Debug contexts are usually slower but give better error reporting.
|
||||||
|
pub debug: bool,
|
||||||
|
/// Whether to use vsync. If vsync is enabled, calling swap_buffers will block until the screen refreshes.
|
||||||
|
/// This is typically used to prevent screen tearing.
|
||||||
|
pub vsync: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Describes the requested OpenGL context profiles.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum GlProfile {
|
||||||
|
/// Include all the immediate more functions and definitions.
|
||||||
|
Compatibility,
|
||||||
|
/// Include all the future-compatible functions and definitions.
|
||||||
|
Core,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Describes how the backend should choose a pixel format.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct PixelFormatRequirements {
|
||||||
|
/// If `true`, only hardware-accelerated formats will be conisdered. If `false`, only software renderers.
|
||||||
|
/// `None` means "don't care". Default is `None`.
|
||||||
|
pub hardware_accelerated: Option<bool>,
|
||||||
|
/// Minimum number of bits for the color buffer, excluding alpha. None means "don't care". The default is `None``.
|
||||||
|
pub color_bits: Option<u8>,
|
||||||
|
/// If `true`, the color buffer must be in a floating point format. Default is `false`.
|
||||||
|
///
|
||||||
|
/// Using floating points allows you to write values outside of the `[0.0, 1.0]` range.
|
||||||
|
pub float_color_buffer: bool,
|
||||||
|
/// Minimum number of bits for the alpha in the color buffer. `None` means "don't care". The default is `None`.
|
||||||
|
pub alpha_bits: Option<u8>,
|
||||||
|
/// Minimum number of bits for the depth buffer. `None` means "don't care". The default value is `None`.
|
||||||
|
pub depth_bits: Option<u8>,
|
||||||
|
/// Minimum number of bits for the depth buffer. `None` means "don't care". The default value is `None`.
|
||||||
|
pub stencil_bits: Option<u8>,
|
||||||
|
/// If `true`, only double-buffered formats will be considered. If `false`, only single-buffer formats.
|
||||||
|
/// `None` means "don't care". The default is `None`.
|
||||||
|
pub double_buffer: Option<bool>,
|
||||||
|
/// Contains the minimum number of samples per pixel in the color, depth and stencil buffers.
|
||||||
|
/// `None` means "don't care". Default is `None`. A value of `Some(0)` indicates that multisampling must not be enabled.
|
||||||
|
pub multisampling: Option<u16>,
|
||||||
|
/// If `true`, only stereoscopic formats will be considered. If `false`, only non-stereoscopic formats.
|
||||||
|
/// The default is `false`.
|
||||||
|
pub stereoscopy: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PixelFormatRequirements {
|
||||||
|
fn default() -> Self {
|
||||||
|
PixelFormatRequirements {
|
||||||
|
hardware_accelerated: Some(true),
|
||||||
|
color_bits: Some(24),
|
||||||
|
float_color_buffer: false,
|
||||||
|
alpha_bits: Some(8),
|
||||||
|
depth_bits: Some(24),
|
||||||
|
stencil_bits: Some(8),
|
||||||
|
double_buffer: Some(true),
|
||||||
|
multisampling: None,
|
||||||
|
stereoscopy: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
//! EGL error types
|
||||||
|
|
||||||
|
error_chain! {
|
||||||
|
errors {
|
||||||
|
#[doc = "The requested OpenGL version is not supported"]
|
||||||
|
OpenGlVersionNotSupported(version: (u8, u8)) {
|
||||||
|
description("The requested OpenGL version is not supported."),
|
||||||
|
display("The requested OpenGL version {:?} is not supported.", version),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "The EGL implementation does not support creating OpenGL ES contexts"]
|
||||||
|
OpenGlesNotSupported {
|
||||||
|
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 = "Backend does not match the context type"]
|
||||||
|
NonMatchingBackend(expected: &'static str) {
|
||||||
|
description("The expected backend did not match the runtime."),
|
||||||
|
display("The expected backend '{:?}' does not match the runtime.", expected),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 required EGL extension is not supported by the underlying EGL implementation"]
|
||||||
|
EglExtensionNotSupported(extensions: &'static [&'static str]) {
|
||||||
|
description("The required EGL extension is not supported by the underlying EGL implementation"),
|
||||||
|
display("None of the following EGL extensions is supported by the underlying EGL implementation,
|
||||||
|
at least one is required: {:?}", extensions)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "Only one EGLDisplay may be bound to a given WlDisplay at any time"]
|
||||||
|
OtherEGLDisplayAlreadyBound {
|
||||||
|
description("Only one EGLDisplay may be bound to a given WlDisplay at any time")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "No EGLDisplay is currently bound to this WlDisplay"]
|
||||||
|
NoEGLDisplayBound {
|
||||||
|
description("No EGLDisplay is currently bound to this WlDisplay")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "Index of plane is out of bounds for EGLImages"]
|
||||||
|
PlaneIndexOutOfBounds {
|
||||||
|
description("Index of plane is out of bounds for EGLImages")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "Failed to create EGLImages from the buffer"]
|
||||||
|
EGLImageCreationFailed {
|
||||||
|
description("Failed to create EGLImages from the buffer")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "The reason of failure could not be determined"]
|
||||||
|
Unknown(err_no: u32)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,182 @@
|
||||||
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
|
pub use nix::libc::{c_long, c_uint, c_void, int32_t, uint64_t};
|
||||||
|
|
||||||
|
pub type khronos_utime_nanoseconds_t = khronos_uint64_t;
|
||||||
|
pub type khronos_uint64_t = uint64_t;
|
||||||
|
pub type khronos_ssize_t = c_long;
|
||||||
|
pub type EGLint = int32_t;
|
||||||
|
pub type EGLNativeDisplayType = NativeDisplayType;
|
||||||
|
pub type EGLNativePixmapType = NativePixmapType;
|
||||||
|
pub type EGLNativeWindowType = NativeWindowType;
|
||||||
|
pub type NativeDisplayType = *const c_void;
|
||||||
|
pub type NativePixmapType = *const c_void;
|
||||||
|
pub type NativeWindowType = *const c_void;
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "cargo-clippy", allow(clippy))]
|
||||||
|
pub mod gl {
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "cargo-clippy", allow(clippy))]
|
||||||
|
pub mod egl {
|
||||||
|
use super::*;
|
||||||
|
use libloading::Library;
|
||||||
|
use std::sync::{Once, ONCE_INIT};
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref LIB: Library = {
|
||||||
|
Library::new("libEGL.so.1").expect("Failed to load LibEGL")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub static LOAD: Once = ONCE_INIT;
|
||||||
|
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/egl_bindings.rs"));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* `gl_generator` cannot generate bindings for the `EGL_WL_bind_wayland_display` extension.
|
||||||
|
* Lets do it ourselves...
|
||||||
|
*/
|
||||||
|
|
||||||
|
#[allow(non_snake_case, unused_variables, dead_code)]
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn BindWaylandDisplayWL(
|
||||||
|
dpy: types::EGLDisplay, display: *mut __gl_imports::raw::c_void
|
||||||
|
) -> types::EGLBoolean {
|
||||||
|
__gl_imports::mem::transmute::<
|
||||||
|
_,
|
||||||
|
extern "system" fn(types::EGLDisplay, *mut __gl_imports::raw::c_void) -> types::EGLBoolean,
|
||||||
|
>(wayland_storage::BindWaylandDisplayWL.f)(dpy, display)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case, unused_variables, dead_code)]
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn UnbindWaylandDisplayWL(
|
||||||
|
dpy: types::EGLDisplay, display: *mut __gl_imports::raw::c_void
|
||||||
|
) -> types::EGLBoolean {
|
||||||
|
__gl_imports::mem::transmute::<
|
||||||
|
_,
|
||||||
|
extern "system" fn(types::EGLDisplay, *mut __gl_imports::raw::c_void) -> types::EGLBoolean,
|
||||||
|
>(wayland_storage::UnbindWaylandDisplayWL.f)(dpy, display)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case, unused_variables, dead_code)]
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn QueryWaylandBufferWL(
|
||||||
|
dpy: types::EGLDisplay, buffer: *mut __gl_imports::raw::c_void, attribute: types::EGLint,
|
||||||
|
value: *mut types::EGLint,
|
||||||
|
) -> types::EGLBoolean {
|
||||||
|
__gl_imports::mem::transmute::<
|
||||||
|
_,
|
||||||
|
extern "system" fn(
|
||||||
|
types::EGLDisplay,
|
||||||
|
*mut __gl_imports::raw::c_void,
|
||||||
|
types::EGLint,
|
||||||
|
*mut types::EGLint,
|
||||||
|
) -> types::EGLBoolean,
|
||||||
|
>(wayland_storage::QueryWaylandBufferWL.f)(dpy, buffer, attribute, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
mod wayland_storage {
|
||||||
|
use super::FnPtr;
|
||||||
|
use super::__gl_imports::raw;
|
||||||
|
pub static mut BindWaylandDisplayWL: FnPtr = FnPtr {
|
||||||
|
f: super::missing_fn_panic as *const raw::c_void,
|
||||||
|
is_loaded: false,
|
||||||
|
};
|
||||||
|
pub static mut UnbindWaylandDisplayWL: FnPtr = FnPtr {
|
||||||
|
f: super::missing_fn_panic as *const raw::c_void,
|
||||||
|
is_loaded: false,
|
||||||
|
};
|
||||||
|
pub static mut QueryWaylandBufferWL: FnPtr = FnPtr {
|
||||||
|
f: super::missing_fn_panic as *const raw::c_void,
|
||||||
|
is_loaded: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub mod BindWaylandDisplayWL {
|
||||||
|
use super::{metaloadfn, wayland_storage};
|
||||||
|
use super::FnPtr;
|
||||||
|
use super::__gl_imports::raw;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn is_loaded() -> bool {
|
||||||
|
unsafe { wayland_storage::BindWaylandDisplayWL.is_loaded }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn load_with<F>(mut loadfn: F)
|
||||||
|
where
|
||||||
|
F: FnMut(&str) -> *const raw::c_void,
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
wayland_storage::BindWaylandDisplayWL =
|
||||||
|
FnPtr::new(metaloadfn(&mut loadfn, "eglBindWaylandDisplayWL", &[]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub mod UnbindWaylandDisplayWL {
|
||||||
|
use super::{metaloadfn, wayland_storage};
|
||||||
|
use super::FnPtr;
|
||||||
|
use super::__gl_imports::raw;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn is_loaded() -> bool {
|
||||||
|
unsafe { wayland_storage::UnbindWaylandDisplayWL.is_loaded }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn load_with<F>(mut loadfn: F)
|
||||||
|
where
|
||||||
|
F: FnMut(&str) -> *const raw::c_void,
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
wayland_storage::UnbindWaylandDisplayWL =
|
||||||
|
FnPtr::new(metaloadfn(&mut loadfn, "eglUnbindWaylandDisplayWL", &[]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub mod QueryWaylandBufferWL {
|
||||||
|
use super::{metaloadfn, wayland_storage};
|
||||||
|
use super::FnPtr;
|
||||||
|
use super::__gl_imports::raw;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn is_loaded() -> bool {
|
||||||
|
unsafe { wayland_storage::QueryWaylandBufferWL.is_loaded }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn load_with<F>(mut loadfn: F)
|
||||||
|
where
|
||||||
|
F: FnMut(&str) -> *const raw::c_void,
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
wayland_storage::QueryWaylandBufferWL =
|
||||||
|
FnPtr::new(metaloadfn(&mut loadfn, "eglQueryWaylandBufferWL", &[]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accepted as <target> in eglCreateImageKHR
|
||||||
|
pub const WAYLAND_BUFFER_WL: c_uint = 0x31D5;
|
||||||
|
// Accepted in the <attrib_list> parameter of eglCreateImageKHR:
|
||||||
|
pub const WAYLAND_PLANE_WL: c_uint = 0x31D6;
|
||||||
|
// Possible values for EGL_TEXTURE_FORMAT:
|
||||||
|
pub const TEXTURE_Y_U_V_WL: int32_t = 0x31D7;
|
||||||
|
pub const TEXTURE_Y_UV_WL: int32_t = 0x31D8;
|
||||||
|
pub const TEXTURE_Y_XUXV_WL: int32_t = 0x31D9;
|
||||||
|
pub const TEXTURE_EXTERNAL_WL: int32_t = 0x31DA;
|
||||||
|
// Accepted in the <attribute> parameter of eglQueryWaylandBufferWL:
|
||||||
|
pub const EGL_TEXTURE_FORMAT: int32_t = 0x3080;
|
||||||
|
pub const WAYLAND_Y_INVERTED_WL: int32_t = 0x31DB;
|
||||||
|
}
|
|
@ -0,0 +1,150 @@
|
||||||
|
//! Common traits and types for egl rendering
|
||||||
|
|
||||||
|
// Large parts of this module are taken from
|
||||||
|
// https://github.com/tomaka/glutin/tree/044e651edf67a2029eecc650dd42546af1501414/src/api/egl/
|
||||||
|
//
|
||||||
|
// It therefore falls under glutin's Apache 2.0 license
|
||||||
|
// (see https://github.com/tomaka/glutin/tree/044e651edf67a2029eecc650dd42546af1501414/LICENSE)
|
||||||
|
|
||||||
|
use super::GraphicsBackend;
|
||||||
|
use nix::libc::c_void;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
pub mod context;
|
||||||
|
pub use self::context::EGLContext;
|
||||||
|
pub mod error;
|
||||||
|
#[allow(non_camel_case_types, dead_code, unused_mut, non_upper_case_globals)]
|
||||||
|
pub mod ffi;
|
||||||
|
pub mod native;
|
||||||
|
pub mod surface;
|
||||||
|
pub use self::surface::EGLSurface;
|
||||||
|
pub mod wayland;
|
||||||
|
pub use self::wayland::{BufferAccessError, EGLImages, EGLWaylandExtensions};
|
||||||
|
|
||||||
|
/// Error that can happen when swapping buffers.
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum SwapBuffersError {
|
||||||
|
/// The OpenGL context has been lost and needs to be recreated.
|
||||||
|
///
|
||||||
|
/// All the objects associated to it (textures, buffers, programs, etc.)
|
||||||
|
/// need to be recreated from scratch.
|
||||||
|
///
|
||||||
|
/// Operations will have no effect. Functions that read textures, buffers, etc.
|
||||||
|
/// from OpenGL will return uninitialized data instead.
|
||||||
|
///
|
||||||
|
/// A context loss usually happens on mobile devices when the user puts the
|
||||||
|
/// application on sleep and wakes it up later. However any OpenGL implementation
|
||||||
|
/// can theoretically lose the context at any time.
|
||||||
|
ContextLost,
|
||||||
|
/// The buffers have already been swapped.
|
||||||
|
///
|
||||||
|
/// This error can be returned when `swap_buffers` has been called multiple times
|
||||||
|
/// without any modification in between.
|
||||||
|
AlreadySwapped,
|
||||||
|
/// Unknown GL error
|
||||||
|
Unknown(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ::std::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"
|
||||||
|
}
|
||||||
|
SwapBuffersError::Unknown(_) => "Unknown Open GL error occurred",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cause(&self) -> Option<&::std::error::Error> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Error that can happen on optional EGL features
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct EglExtensionNotSupportedError(&'static [&'static str]);
|
||||||
|
|
||||||
|
impl fmt::Display for EglExtensionNotSupportedError {
|
||||||
|
fn fmt(&self, formatter: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
|
||||||
|
write!(
|
||||||
|
formatter,
|
||||||
|
"None of the following EGL extensions is supported by the underlying EGL implementation,
|
||||||
|
at least one is required: {:?}",
|
||||||
|
self.0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::error::Error for EglExtensionNotSupportedError {
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
"The required EGL extension is not supported by the underlying EGL implementation"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cause(&self) -> Option<&::std::error::Error> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Describes the pixel format of the main framebuffer
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct PixelFormat {
|
||||||
|
/// is the format hardware accelerated
|
||||||
|
pub hardware_accelerated: bool,
|
||||||
|
/// number of bits used for colors
|
||||||
|
pub color_bits: u8,
|
||||||
|
/// number of bits used for alpha channel
|
||||||
|
pub alpha_bits: u8,
|
||||||
|
/// number of bits used for depth channel
|
||||||
|
pub depth_bits: u8,
|
||||||
|
/// number of bits used for stencil buffer
|
||||||
|
pub stencil_bits: u8,
|
||||||
|
/// is stereoscopy enabled
|
||||||
|
pub stereoscopy: bool,
|
||||||
|
/// is double buffering enabled
|
||||||
|
pub double_buffer: bool,
|
||||||
|
/// number of samples used for multisampling if enabled
|
||||||
|
pub multisampling: Option<u16>,
|
||||||
|
/// is srgb enabled
|
||||||
|
pub srgb: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trait that describes objects that have an OpenGL context
|
||||||
|
/// and can be used to render upon
|
||||||
|
pub trait EGLGraphicsBackend: GraphicsBackend {
|
||||||
|
/// Swaps buffers at the end of a frame.
|
||||||
|
fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError>;
|
||||||
|
|
||||||
|
/// Returns the address of an OpenGL function.
|
||||||
|
///
|
||||||
|
/// Supposes that the context has been made current before this function is called.
|
||||||
|
unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void;
|
||||||
|
|
||||||
|
/// Returns the dimensions of the window, or screen, etc in points.
|
||||||
|
///
|
||||||
|
/// That are the scaled pixels of the underlying graphics backend.
|
||||||
|
/// For nested compositors this will respect the scaling of the root compositor.
|
||||||
|
/// For drawing directly onto hardware this unit will be equal to actual pixels.
|
||||||
|
fn get_framebuffer_dimensions(&self) -> (u32, u32);
|
||||||
|
|
||||||
|
/// Returns true if the OpenGL context is the current one in the thread.
|
||||||
|
fn is_current(&self) -> bool;
|
||||||
|
|
||||||
|
/// Makes the OpenGL context the current context in the current thread.
|
||||||
|
///
|
||||||
|
/// # Unsafety
|
||||||
|
///
|
||||||
|
/// This function is marked unsafe, because the context cannot be made current
|
||||||
|
/// on multiple threads.
|
||||||
|
unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError>;
|
||||||
|
|
||||||
|
/// Returns the pixel format of the main framebuffer of the context.
|
||||||
|
fn get_pixel_format(&self) -> PixelFormat;
|
||||||
|
}
|
|
@ -0,0 +1,267 @@
|
||||||
|
//! Type safe native types for safe context/surface creation
|
||||||
|
|
||||||
|
use super::error::*;
|
||||||
|
use super::ffi;
|
||||||
|
#[cfg(feature = "backend_drm")]
|
||||||
|
use backend::drm::error::{Error as DrmError, ErrorKind as DrmErrorKind, Result as DrmResult};
|
||||||
|
#[cfg(feature = "backend_drm")]
|
||||||
|
use gbm::{AsRaw, BufferObjectFlags, Device as GbmDevice, Format as GbmFormat, Surface as GbmSurface};
|
||||||
|
#[cfg(feature = "backend_drm")]
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
#[cfg(feature = "backend_drm")]
|
||||||
|
use std::os::unix::io::AsRawFd;
|
||||||
|
#[cfg(any(feature = "backend_drm", feature = "backend_winit"))]
|
||||||
|
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;
|
||||||
|
|
||||||
|
/// Trait for typed backend variants (X11/Wayland/GBM)
|
||||||
|
pub trait Backend {
|
||||||
|
/// Surface type created by this backend
|
||||||
|
type Surface: NativeSurface;
|
||||||
|
|
||||||
|
/// Return an `EGLDisplay` based on this backend
|
||||||
|
///
|
||||||
|
/// # Unsafety
|
||||||
|
///
|
||||||
|
/// The returned `EGLDisplay` needs to be a valid ptr for egl,
|
||||||
|
/// but there is no way to test that.
|
||||||
|
unsafe fn get_display<F: Fn(&str) -> bool>(
|
||||||
|
display: ffi::NativeDisplayType, has_dp_extension: F, log: ::slog::Logger
|
||||||
|
) -> ffi::egl::types::EGLDisplay;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "backend_winit")]
|
||||||
|
/// Wayland backend type
|
||||||
|
pub enum Wayland {}
|
||||||
|
#[cfg(feature = "backend_winit")]
|
||||||
|
impl Backend for Wayland {
|
||||||
|
type Surface = wegl::WlEglSurface;
|
||||||
|
|
||||||
|
unsafe fn get_display<F>(
|
||||||
|
display: ffi::NativeDisplayType, has_dp_extension: F, log: ::slog::Logger
|
||||||
|
) -> ffi::egl::types::EGLDisplay
|
||||||
|
where
|
||||||
|
F: Fn(&str) -> bool,
|
||||||
|
{
|
||||||
|
if has_dp_extension("EGL_KHR_platform_wayland") && ffi::egl::GetPlatformDisplay::is_loaded() {
|
||||||
|
trace!(
|
||||||
|
log,
|
||||||
|
"EGL Display Initialization via EGL_KHR_platform_wayland"
|
||||||
|
);
|
||||||
|
ffi::egl::GetPlatformDisplay(
|
||||||
|
ffi::egl::PLATFORM_WAYLAND_KHR,
|
||||||
|
display as *mut _,
|
||||||
|
ptr::null(),
|
||||||
|
)
|
||||||
|
} else if has_dp_extension("EGL_EXT_platform_wayland") && ffi::egl::GetPlatformDisplayEXT::is_loaded()
|
||||||
|
{
|
||||||
|
trace!(
|
||||||
|
log,
|
||||||
|
"EGL Display Initialization via EGL_EXT_platform_wayland"
|
||||||
|
);
|
||||||
|
ffi::egl::GetPlatformDisplayEXT(
|
||||||
|
ffi::egl::PLATFORM_WAYLAND_EXT,
|
||||||
|
display as *mut _,
|
||||||
|
ptr::null(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
trace!(log, "Default EGL Display Initialization via GetDisplay");
|
||||||
|
ffi::egl::GetDisplay(display as *mut _)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "backend_winit")]
|
||||||
|
/// Typed Xlib window for the `X11` backend
|
||||||
|
pub struct XlibWindow(u64);
|
||||||
|
#[cfg(feature = "backend_winit")]
|
||||||
|
/// X11 backend type
|
||||||
|
pub enum X11 {}
|
||||||
|
#[cfg(feature = "backend_winit")]
|
||||||
|
impl Backend for X11 {
|
||||||
|
type Surface = XlibWindow;
|
||||||
|
|
||||||
|
unsafe fn get_display<F>(
|
||||||
|
display: ffi::NativeDisplayType, has_dp_extension: F, log: ::slog::Logger
|
||||||
|
) -> ffi::egl::types::EGLDisplay
|
||||||
|
where
|
||||||
|
F: Fn(&str) -> bool,
|
||||||
|
{
|
||||||
|
if has_dp_extension("EGL_KHR_platform_x11") && ffi::egl::GetPlatformDisplay::is_loaded() {
|
||||||
|
trace!(log, "EGL Display Initialization via EGL_KHR_platform_x11");
|
||||||
|
ffi::egl::GetPlatformDisplay(ffi::egl::PLATFORM_X11_KHR, display as *mut _, ptr::null())
|
||||||
|
} else if has_dp_extension("EGL_EXT_platform_x11") && ffi::egl::GetPlatformDisplayEXT::is_loaded() {
|
||||||
|
trace!(log, "EGL Display Initialization via EGL_EXT_platform_x11");
|
||||||
|
ffi::egl::GetPlatformDisplayEXT(ffi::egl::PLATFORM_X11_EXT, display as *mut _, ptr::null())
|
||||||
|
} else {
|
||||||
|
trace!(log, "Default EGL Display Initialization via GetDisplay");
|
||||||
|
ffi::egl::GetDisplay(display as *mut _)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(feature = "backend_drm")]
|
||||||
|
/// Gbm backend type
|
||||||
|
pub struct Gbm<T: 'static> {
|
||||||
|
_userdata: PhantomData<T>,
|
||||||
|
}
|
||||||
|
#[cfg(feature = "backend_drm")]
|
||||||
|
impl<T: 'static> Backend for Gbm<T> {
|
||||||
|
type Surface = GbmSurface<T>;
|
||||||
|
|
||||||
|
unsafe fn get_display<F>(
|
||||||
|
display: ffi::NativeDisplayType, has_dp_extension: F, log: ::slog::Logger
|
||||||
|
) -> ffi::egl::types::EGLDisplay
|
||||||
|
where
|
||||||
|
F: Fn(&str) -> bool,
|
||||||
|
{
|
||||||
|
if has_dp_extension("EGL_KHR_platform_gbm") && ffi::egl::GetPlatformDisplay::is_loaded() {
|
||||||
|
trace!(log, "EGL Display Initialization via EGL_KHR_platform_gbm");
|
||||||
|
ffi::egl::GetPlatformDisplay(ffi::egl::PLATFORM_GBM_KHR, display as *mut _, ptr::null())
|
||||||
|
} else if has_dp_extension("EGL_MESA_platform_gbm") && ffi::egl::GetPlatformDisplayEXT::is_loaded() {
|
||||||
|
trace!(log, "EGL Display Initialization via EGL_MESA_platform_gbm");
|
||||||
|
ffi::egl::GetPlatformDisplayEXT(ffi::egl::PLATFORM_GBM_MESA, display as *mut _, ptr::null())
|
||||||
|
} else if has_dp_extension("EGL_MESA_platform_gbm") && ffi::egl::GetPlatformDisplay::is_loaded() {
|
||||||
|
trace!(log, "EGL Display Initialization via EGL_MESA_platform_gbm");
|
||||||
|
ffi::egl::GetPlatformDisplay(ffi::egl::PLATFORM_GBM_MESA, display as *mut _, ptr::null())
|
||||||
|
} else {
|
||||||
|
trace!(log, "Default EGL Display Initialization via GetDisplay");
|
||||||
|
ffi::egl::GetDisplay(display as *mut _)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trait for types returning Surfaces which can be used to initialize `EGLSurface`s
|
||||||
|
///
|
||||||
|
/// ## Unsafety
|
||||||
|
///
|
||||||
|
/// The returned `NativeDisplayType` must be valid for egl and there is no way to test that.
|
||||||
|
pub unsafe trait NativeDisplay<B: Backend> {
|
||||||
|
/// Arguments used to surface creation.
|
||||||
|
type Arguments;
|
||||||
|
/// Error type thrown by the surface creation in case of failure.
|
||||||
|
type Error: ::std::error::Error + Send + 'static;
|
||||||
|
/// Because one typ might implement multiple `Backend` this function must be called to check
|
||||||
|
/// if the expected `Backend` is used at runtime.
|
||||||
|
fn is_backend(&self) -> bool;
|
||||||
|
/// Return a raw pointer egl will accept for context creation.
|
||||||
|
fn ptr(&self) -> Result<ffi::NativeDisplayType>;
|
||||||
|
/// Create a surface
|
||||||
|
fn create_surface(&self, args: Self::Arguments) -> ::std::result::Result<B::Surface, Self::Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "backend_winit")]
|
||||||
|
unsafe impl NativeDisplay<X11> for WinitWindow {
|
||||||
|
type Arguments = ();
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn is_backend(&self) -> bool {
|
||||||
|
self.get_xlib_display().is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ptr(&self) -> Result<ffi::NativeDisplayType> {
|
||||||
|
self.get_xlib_display()
|
||||||
|
.map(|ptr| ptr as *const _)
|
||||||
|
.ok_or(ErrorKind::NonMatchingBackend("X11").into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_surface(&self, _args: ()) -> Result<XlibWindow> {
|
||||||
|
self.get_xlib_window()
|
||||||
|
.map(|ptr| XlibWindow(ptr))
|
||||||
|
.ok_or(ErrorKind::NonMatchingBackend("X11").into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "backend_winit")]
|
||||||
|
unsafe impl NativeDisplay<Wayland> for WinitWindow {
|
||||||
|
type Arguments = ();
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn is_backend(&self) -> bool {
|
||||||
|
self.get_wayland_display().is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ptr(&self) -> Result<ffi::NativeDisplayType> {
|
||||||
|
self.get_wayland_display()
|
||||||
|
.map(|ptr| ptr as *const _)
|
||||||
|
.ok_or(ErrorKind::NonMatchingBackend("Wayland").into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_surface(&self, _args: ()) -> Result<wegl::WlEglSurface> {
|
||||||
|
if let Some(surface) = self.get_wayland_surface() {
|
||||||
|
let (w, h) = self.get_inner_size().unwrap();
|
||||||
|
Ok(unsafe { wegl::WlEglSurface::new_from_raw(surface as *mut _, w as i32, h as i32) })
|
||||||
|
} else {
|
||||||
|
bail!(ErrorKind::NonMatchingBackend("Wayland"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "backend_drm")]
|
||||||
|
/// Arguments necessary to construct a `GbmSurface`
|
||||||
|
pub struct GbmSurfaceArguments {
|
||||||
|
/// Size of the surface
|
||||||
|
pub size: (u32, u32),
|
||||||
|
/// Pixel format of the surface
|
||||||
|
pub format: GbmFormat,
|
||||||
|
/// Flags for surface creation
|
||||||
|
pub flags: BufferObjectFlags,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "backend_drm")]
|
||||||
|
unsafe impl<A: AsRawFd + 'static, T: 'static> NativeDisplay<Gbm<T>> for GbmDevice<A> {
|
||||||
|
type Arguments = GbmSurfaceArguments;
|
||||||
|
type Error = DrmError;
|
||||||
|
|
||||||
|
fn is_backend(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ptr(&self) -> Result<ffi::NativeDisplayType> {
|
||||||
|
Ok(self.as_raw() as *const _)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_surface(&self, args: GbmSurfaceArguments) -> DrmResult<GbmSurface<T>> {
|
||||||
|
use backend::drm::error::ResultExt as DrmResultExt;
|
||||||
|
|
||||||
|
DrmResultExt::chain_err(
|
||||||
|
GbmDevice::create_surface(self, args.size.0, args.size.1, args.format, args.flags),
|
||||||
|
|| DrmErrorKind::GbmInitFailed,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trait for types returning valid surface pointers for initializing egl
|
||||||
|
///
|
||||||
|
/// ## Unsafety
|
||||||
|
///
|
||||||
|
/// The returned `NativeWindowType` must be valid for egl and there is no way to test that.
|
||||||
|
pub unsafe trait NativeSurface {
|
||||||
|
/// Return a raw pointer egl will accept for surface creation.
|
||||||
|
fn ptr(&self) -> ffi::NativeWindowType;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "backend_winit")]
|
||||||
|
unsafe impl NativeSurface for XlibWindow {
|
||||||
|
fn ptr(&self) -> ffi::NativeWindowType {
|
||||||
|
self.0 as *const _
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "backend_winit")]
|
||||||
|
unsafe impl NativeSurface for wegl::WlEglSurface {
|
||||||
|
fn ptr(&self) -> ffi::NativeWindowType {
|
||||||
|
self.ptr() as *const _
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "backend_drm")]
|
||||||
|
unsafe impl<T: 'static> NativeSurface for GbmSurface<T> {
|
||||||
|
fn ptr(&self) -> ffi::NativeWindowType {
|
||||||
|
self.as_raw() as *const _
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,123 @@
|
||||||
|
//! EGL surface related structs
|
||||||
|
|
||||||
|
use super::{EGLContext, SwapBuffersError};
|
||||||
|
use super::error::*;
|
||||||
|
use super::ffi;
|
||||||
|
use super::native;
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
use std::rc::{Rc, Weak};
|
||||||
|
|
||||||
|
/// EGL surface of a given egl context for rendering
|
||||||
|
pub struct EGLSurface<N: native::NativeSurface> {
|
||||||
|
context: Weak<ffi::egl::types::EGLContext>,
|
||||||
|
display: Weak<ffi::egl::types::EGLDisplay>,
|
||||||
|
native: N,
|
||||||
|
surface: ffi::egl::types::EGLSurface,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: native::NativeSurface> Deref for EGLSurface<N> {
|
||||||
|
type Target = N;
|
||||||
|
fn deref(&self) -> &N {
|
||||||
|
&self.native
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: native::NativeSurface> DerefMut for EGLSurface<N> {
|
||||||
|
fn deref_mut(&mut self) -> &mut N {
|
||||||
|
&mut self.native
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: native::NativeSurface> EGLSurface<N> {
|
||||||
|
pub(crate) fn new<B: native::Backend<Surface = N>, D: native::NativeDisplay<B>>(
|
||||||
|
context: &EGLContext<B, D>, native: N
|
||||||
|
) -> Result<EGLSurface<N>> {
|
||||||
|
let surface = unsafe {
|
||||||
|
ffi::egl::CreateWindowSurface(
|
||||||
|
*context.display,
|
||||||
|
context.config_id,
|
||||||
|
native.ptr(),
|
||||||
|
context.surface_attributes.as_ptr(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if surface.is_null() {
|
||||||
|
bail!(ErrorKind::SurfaceCreationFailed);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(EGLSurface {
|
||||||
|
context: Rc::downgrade(&context.context),
|
||||||
|
display: Rc::downgrade(&context.display),
|
||||||
|
native,
|
||||||
|
surface,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Swaps buffers at the end of a frame.
|
||||||
|
pub fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
|
||||||
|
if let Some(display) = self.display.upgrade() {
|
||||||
|
let ret = unsafe { ffi::egl::SwapBuffers((*display) as *const _, self.surface as *const _) };
|
||||||
|
|
||||||
|
if ret == 0 {
|
||||||
|
match unsafe { ffi::egl::GetError() } as u32 {
|
||||||
|
ffi::egl::CONTEXT_LOST => Err(SwapBuffersError::ContextLost),
|
||||||
|
err => Err(SwapBuffersError::Unknown(err)),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(SwapBuffersError::ContextLost)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Makes the OpenGL context the current context in the current thread.
|
||||||
|
///
|
||||||
|
/// # Unsafety
|
||||||
|
///
|
||||||
|
/// This function is marked unsafe, because the context cannot be made current
|
||||||
|
/// on multiple threads.
|
||||||
|
pub unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
|
||||||
|
if let (Some(display), Some(context)) = (self.display.upgrade(), self.context.upgrade()) {
|
||||||
|
let ret = ffi::egl::MakeCurrent(
|
||||||
|
(*display) as *const _,
|
||||||
|
self.surface as *const _,
|
||||||
|
self.surface as *const _,
|
||||||
|
(*context) as *const _,
|
||||||
|
);
|
||||||
|
|
||||||
|
if ret == 0 {
|
||||||
|
match ffi::egl::GetError() as u32 {
|
||||||
|
ffi::egl::CONTEXT_LOST => Err(SwapBuffersError::ContextLost),
|
||||||
|
err => panic!("eglMakeCurrent failed (eglGetError returned 0x{:x})", err),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(SwapBuffersError::ContextLost)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the OpenGL surface is the current one in the thread.
|
||||||
|
pub fn is_current(&self) -> bool {
|
||||||
|
if self.context.upgrade().is_some() {
|
||||||
|
unsafe {
|
||||||
|
ffi::egl::GetCurrentSurface(ffi::egl::DRAW as _) == self.surface as *const _
|
||||||
|
&& ffi::egl::GetCurrentSurface(ffi::egl::READ as _) == self.surface as *const _
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: native::NativeSurface> Drop for EGLSurface<N> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Some(display) = self.display.upgrade() {
|
||||||
|
unsafe {
|
||||||
|
ffi::egl::DestroySurface((*display) as *const _, self.surface as *const _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,403 @@
|
||||||
|
//! Wayland specific EGL functionality - EGL based `WlBuffer`s.
|
||||||
|
//!
|
||||||
|
//! The types of this module can be used to initialize hardware acceleration rendering
|
||||||
|
//! based on EGL for clients as it may enabled usage of `EGLImage` based `WlBuffer`s.
|
||||||
|
//!
|
||||||
|
//! To use it bind any backend implementing the `EGLWaylandExtensions` trait, that shall do the
|
||||||
|
//! rendering (so pick a fast one), to the `wayland_server::Display` of your compositor.
|
||||||
|
//! Note only one backend may be bould to any `Display` at any time.
|
||||||
|
//!
|
||||||
|
//! You may then use the resulting `EGLDisplay` to recieve `EGLImages` of an egl-based `WlBuffer`
|
||||||
|
//! for rendering.
|
||||||
|
|
||||||
|
use backend::graphics::egl::{ffi, native, EGLContext, EglExtensionNotSupportedError};
|
||||||
|
use backend::graphics::egl::error::*;
|
||||||
|
use backend::graphics::egl::ffi::egl::types::EGLImage;
|
||||||
|
use nix::libc::c_uint;
|
||||||
|
use std::fmt;
|
||||||
|
use std::rc::{Rc, Weak};
|
||||||
|
use wayland_server::{Display, Resource};
|
||||||
|
use wayland_server::protocol::wl_buffer::WlBuffer;
|
||||||
|
use wayland_sys::server::wl_display;
|
||||||
|
|
||||||
|
/// Error that can occur when accessing an EGL buffer
|
||||||
|
pub enum BufferAccessError {
|
||||||
|
/// The corresponding Context is not alive anymore
|
||||||
|
ContextLost,
|
||||||
|
/// This buffer is not managed by the EGL buffer
|
||||||
|
NotManaged(WlBuffer),
|
||||||
|
/// Failed to create EGLImages from the buffer
|
||||||
|
EGLImageCreationFailed,
|
||||||
|
/// The required EGL extension is not supported by the underlying EGL implementation
|
||||||
|
EglExtensionNotSupported(EglExtensionNotSupportedError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for BufferAccessError {
|
||||||
|
fn fmt(&self, formatter: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
|
||||||
|
match *self {
|
||||||
|
BufferAccessError::ContextLost => write!(formatter, "BufferAccessError::ContextLost"),
|
||||||
|
BufferAccessError::NotManaged(_) => write!(formatter, "BufferAccessError::NotManaged"),
|
||||||
|
BufferAccessError::EGLImageCreationFailed => {
|
||||||
|
write!(formatter, "BufferAccessError::EGLImageCreationFailed")
|
||||||
|
}
|
||||||
|
BufferAccessError::EglExtensionNotSupported(ref err) => write!(formatter, "{:?}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for BufferAccessError {
|
||||||
|
fn fmt(&self, formatter: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
|
||||||
|
use std::error::Error;
|
||||||
|
match *self {
|
||||||
|
BufferAccessError::ContextLost
|
||||||
|
| BufferAccessError::NotManaged(_)
|
||||||
|
| BufferAccessError::EGLImageCreationFailed => write!(formatter, "{}", self.description()),
|
||||||
|
BufferAccessError::EglExtensionNotSupported(ref err) => err.fmt(formatter),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::error::Error for BufferAccessError {
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
match *self {
|
||||||
|
BufferAccessError::ContextLost => "The corresponding context was lost",
|
||||||
|
BufferAccessError::NotManaged(_) => "This buffer is not mananged by EGL",
|
||||||
|
BufferAccessError::EGLImageCreationFailed => "Failed to create EGLImages from the buffer",
|
||||||
|
BufferAccessError::EglExtensionNotSupported(ref err) => err.description(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cause(&self) -> Option<&::std::error::Error> {
|
||||||
|
match *self {
|
||||||
|
BufferAccessError::EglExtensionNotSupported(ref err) => Some(err),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<EglExtensionNotSupportedError> for BufferAccessError {
|
||||||
|
fn from(error: EglExtensionNotSupportedError) -> Self {
|
||||||
|
BufferAccessError::EglExtensionNotSupported(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Error that might happen when binding an `EGLImage` to a gl texture
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum TextureCreationError {
|
||||||
|
/// The given plane index is out of bounds
|
||||||
|
PlaneIndexOutOfBounds,
|
||||||
|
/// The OpenGL context has been lost and needs to be recreated.
|
||||||
|
///
|
||||||
|
/// All the objects associated to it (textures, buffers, programs, etc.)
|
||||||
|
/// need to be recreated from scratch.
|
||||||
|
///
|
||||||
|
/// Operations will have no effect. Functions that read textures, buffers, etc.
|
||||||
|
/// from OpenGL will return uninitialized data instead.
|
||||||
|
///
|
||||||
|
/// A context loss usually happens on mobile devices when the user puts the
|
||||||
|
/// application on sleep and wakes it up later. However any OpenGL implementation
|
||||||
|
/// can theoretically lose the context at any time.
|
||||||
|
ContextLost,
|
||||||
|
/// Failed to bind the EGLImage to the given texture
|
||||||
|
///
|
||||||
|
/// The given argument is the gl error code
|
||||||
|
TextureBindingFailed(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for TextureCreationError {
|
||||||
|
fn fmt(&self, formatter: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
|
||||||
|
use std::error::Error;
|
||||||
|
match *self {
|
||||||
|
TextureCreationError::ContextLost => write!(formatter, "{}", self.description()),
|
||||||
|
TextureCreationError::PlaneIndexOutOfBounds => write!(formatter, "{}", self.description()),
|
||||||
|
TextureCreationError::TextureBindingFailed(code) => write!(
|
||||||
|
formatter,
|
||||||
|
"{}. Gl error code: {:?}",
|
||||||
|
self.description(),
|
||||||
|
code
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::error::Error for TextureCreationError {
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
match *self {
|
||||||
|
TextureCreationError::ContextLost => "The context has been lost, it needs to be recreated",
|
||||||
|
TextureCreationError::PlaneIndexOutOfBounds => "This buffer is not mananged by EGL",
|
||||||
|
TextureCreationError::TextureBindingFailed(_) => "Failed to create EGLImages from the buffer",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cause(&self) -> Option<&::std::error::Error> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Texture format types
|
||||||
|
#[repr(i32)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub enum Format {
|
||||||
|
/// RGB format
|
||||||
|
RGB = ffi::egl::TEXTURE_RGB as i32,
|
||||||
|
/// RGB + alpha channel format
|
||||||
|
RGBA = ffi::egl::TEXTURE_RGBA as i32,
|
||||||
|
/// External format
|
||||||
|
External = ffi::egl::TEXTURE_EXTERNAL_WL,
|
||||||
|
/// 2-plane Y and UV format
|
||||||
|
Y_UV = ffi::egl::TEXTURE_Y_UV_WL,
|
||||||
|
/// 3-plane Y, U and V format
|
||||||
|
Y_U_V = ffi::egl::TEXTURE_Y_U_V_WL,
|
||||||
|
/// 2-plane Y and XUXV format
|
||||||
|
Y_XUXV = ffi::egl::TEXTURE_Y_XUXV_WL,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Format {
|
||||||
|
/// Amount of planes this format uses
|
||||||
|
pub fn num_planes(&self) -> usize {
|
||||||
|
match *self {
|
||||||
|
Format::RGB | Format::RGBA | Format::External => 1,
|
||||||
|
Format::Y_UV | Format::Y_XUXV => 2,
|
||||||
|
Format::Y_U_V => 3,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Images of the egl-based `WlBuffer`.
|
||||||
|
pub struct EGLImages {
|
||||||
|
display: Weak<ffi::egl::types::EGLDisplay>,
|
||||||
|
/// Width in pixels
|
||||||
|
pub width: u32,
|
||||||
|
/// Height in pixels
|
||||||
|
pub height: u32,
|
||||||
|
/// If the y-axis is inverted or not
|
||||||
|
pub y_inverted: bool,
|
||||||
|
/// Format of these images
|
||||||
|
pub format: Format,
|
||||||
|
images: Vec<EGLImage>,
|
||||||
|
buffer: WlBuffer,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EGLImages {
|
||||||
|
/// Amount of planes of these `EGLImages`
|
||||||
|
pub fn num_planes(&self) -> usize {
|
||||||
|
self.format.num_planes()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bind plane to an OpenGL texture id
|
||||||
|
///
|
||||||
|
/// This does only temporarily modify the OpenGL state any changes are reverted before returning.
|
||||||
|
///
|
||||||
|
/// # Unsafety
|
||||||
|
///
|
||||||
|
/// The given `tex_id` needs to be a valid GL texture otherwise undefined behavior might occur.
|
||||||
|
pub unsafe fn bind_to_texture(
|
||||||
|
&self, plane: usize, tex_id: c_uint
|
||||||
|
) -> ::std::result::Result<(), TextureCreationError> {
|
||||||
|
if self.display.upgrade().is_some() {
|
||||||
|
let mut old_tex_id: i32 = 0;
|
||||||
|
ffi::gl::GetIntegerv(ffi::gl::TEXTURE_BINDING_2D, &mut old_tex_id);
|
||||||
|
ffi::gl::BindTexture(ffi::gl::TEXTURE_2D, tex_id);
|
||||||
|
ffi::gl::EGLImageTargetTexture2DOES(
|
||||||
|
ffi::gl::TEXTURE_2D,
|
||||||
|
*self.images
|
||||||
|
.get(plane)
|
||||||
|
.ok_or(TextureCreationError::PlaneIndexOutOfBounds)?,
|
||||||
|
);
|
||||||
|
let res = match ffi::egl::GetError() as u32 {
|
||||||
|
ffi::egl::SUCCESS => Ok(()),
|
||||||
|
err => Err(TextureCreationError::TextureBindingFailed(err)),
|
||||||
|
};
|
||||||
|
ffi::gl::BindTexture(ffi::gl::TEXTURE_2D, old_tex_id as u32);
|
||||||
|
res
|
||||||
|
} else {
|
||||||
|
Err(TextureCreationError::ContextLost)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for EGLImages {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Some(display) = self.display.upgrade() {
|
||||||
|
for image in self.images.drain(..) {
|
||||||
|
unsafe {
|
||||||
|
ffi::egl::DestroyImageKHR(*display, image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.buffer.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trait any backend type may implement that allows binding a `wayland_server::Display`
|
||||||
|
/// to create an `EGLDisplay` for egl-based `WlBuffer`s.
|
||||||
|
pub trait EGLWaylandExtensions {
|
||||||
|
/// Binds this EGL context to the given Wayland display.
|
||||||
|
///
|
||||||
|
/// This will allow clients to utilize EGL to create hardware-accelerated
|
||||||
|
/// surfaces. The server will need to be able to handle egl-wl_buffers.
|
||||||
|
/// See the `wayland::drm` module.
|
||||||
|
///
|
||||||
|
/// ## Errors
|
||||||
|
///
|
||||||
|
/// This might return `WlExtensionNotSupported` if binding is not supported
|
||||||
|
/// by the EGL implementation.
|
||||||
|
///
|
||||||
|
/// This might return `OtherEGLDisplayAlreadyBound` if called for the same
|
||||||
|
/// `Display` multiple times, as only one context may be bound at any given time.
|
||||||
|
fn bind_wl_display(&self, display: &Display) -> Result<EGLDisplay>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type to recieve `EGLImages` for egl-based `WlBuffer`s.
|
||||||
|
///
|
||||||
|
/// Can be created by using `EGLWaylandExtensions::bind_wl_display`.
|
||||||
|
pub struct EGLDisplay(Weak<ffi::egl::types::EGLDisplay>, *mut wl_display);
|
||||||
|
|
||||||
|
impl EGLDisplay {
|
||||||
|
fn new<B: native::Backend, N: native::NativeDisplay<B>>(
|
||||||
|
context: &EGLContext<B, N>, display: *mut wl_display
|
||||||
|
) -> EGLDisplay {
|
||||||
|
EGLDisplay(Rc::downgrade(&context.display), display)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Try to recieve `EGLImages` from a given `WlBuffer`.
|
||||||
|
///
|
||||||
|
/// In case the buffer is not managed by egl (but e.g. the wayland::shm module)
|
||||||
|
/// a `BufferAccessError::NotManaged(WlBuffer)` is returned with the original buffer
|
||||||
|
/// to render it another way.
|
||||||
|
pub fn egl_buffer_contents(
|
||||||
|
&self, buffer: WlBuffer
|
||||||
|
) -> ::std::result::Result<EGLImages, BufferAccessError> {
|
||||||
|
if let Some(display) = self.0.upgrade() {
|
||||||
|
let mut format: i32 = 0;
|
||||||
|
if unsafe {
|
||||||
|
ffi::egl::QueryWaylandBufferWL(
|
||||||
|
*display,
|
||||||
|
buffer.ptr() as *mut _,
|
||||||
|
ffi::egl::EGL_TEXTURE_FORMAT,
|
||||||
|
&mut format as *mut _,
|
||||||
|
) == 0
|
||||||
|
} {
|
||||||
|
return Err(BufferAccessError::NotManaged(buffer));
|
||||||
|
}
|
||||||
|
let format = match format {
|
||||||
|
x if x == ffi::egl::TEXTURE_RGB as i32 => Format::RGB,
|
||||||
|
x if x == ffi::egl::TEXTURE_RGBA as i32 => Format::RGBA,
|
||||||
|
ffi::egl::TEXTURE_EXTERNAL_WL => Format::External,
|
||||||
|
ffi::egl::TEXTURE_Y_UV_WL => Format::Y_UV,
|
||||||
|
ffi::egl::TEXTURE_Y_U_V_WL => Format::Y_U_V,
|
||||||
|
ffi::egl::TEXTURE_Y_XUXV_WL => Format::Y_XUXV,
|
||||||
|
_ => panic!("EGL returned invalid texture type"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut width: i32 = 0;
|
||||||
|
if unsafe {
|
||||||
|
ffi::egl::QueryWaylandBufferWL(
|
||||||
|
*display,
|
||||||
|
buffer.ptr() as *mut _,
|
||||||
|
ffi::egl::WIDTH as i32,
|
||||||
|
&mut width as *mut _,
|
||||||
|
) == 0
|
||||||
|
} {
|
||||||
|
return Err(BufferAccessError::NotManaged(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut height: i32 = 0;
|
||||||
|
if unsafe {
|
||||||
|
ffi::egl::QueryWaylandBufferWL(
|
||||||
|
*display,
|
||||||
|
buffer.ptr() as *mut _,
|
||||||
|
ffi::egl::HEIGHT as i32,
|
||||||
|
&mut height as *mut _,
|
||||||
|
) == 0
|
||||||
|
} {
|
||||||
|
return Err(BufferAccessError::NotManaged(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut inverted: i32 = 0;
|
||||||
|
if unsafe {
|
||||||
|
ffi::egl::QueryWaylandBufferWL(
|
||||||
|
*display,
|
||||||
|
buffer.ptr() as *mut _,
|
||||||
|
ffi::egl::WAYLAND_Y_INVERTED_WL,
|
||||||
|
&mut inverted as *mut _,
|
||||||
|
) != 0
|
||||||
|
} {
|
||||||
|
inverted = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut images = Vec::with_capacity(format.num_planes());
|
||||||
|
for i in 0..format.num_planes() {
|
||||||
|
let mut out = Vec::with_capacity(3);
|
||||||
|
out.push(ffi::egl::WAYLAND_PLANE_WL as i32);
|
||||||
|
out.push(i as i32);
|
||||||
|
out.push(ffi::egl::NONE as i32);
|
||||||
|
|
||||||
|
images.push({
|
||||||
|
let image = unsafe {
|
||||||
|
ffi::egl::CreateImageKHR(
|
||||||
|
*display,
|
||||||
|
ffi::egl::NO_CONTEXT,
|
||||||
|
ffi::egl::WAYLAND_BUFFER_WL,
|
||||||
|
buffer.ptr() as *mut _,
|
||||||
|
out.as_ptr(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if image == ffi::egl::NO_IMAGE_KHR {
|
||||||
|
return Err(BufferAccessError::EGLImageCreationFailed);
|
||||||
|
} else {
|
||||||
|
image
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(EGLImages {
|
||||||
|
display: Rc::downgrade(&display),
|
||||||
|
width: width as u32,
|
||||||
|
height: height as u32,
|
||||||
|
y_inverted: inverted != 0,
|
||||||
|
format,
|
||||||
|
images,
|
||||||
|
buffer,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(BufferAccessError::ContextLost)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for EGLDisplay {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Some(display) = self.0.upgrade() {
|
||||||
|
if !self.1.is_null() {
|
||||||
|
unsafe {
|
||||||
|
ffi::egl::UnbindWaylandDisplayWL(*display, self.1 as *mut _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: EGLWaylandExtensions> EGLWaylandExtensions for Rc<E> {
|
||||||
|
fn bind_wl_display(&self, display: &Display) -> Result<EGLDisplay> {
|
||||||
|
(**self).bind_wl_display(display)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: native::Backend, N: native::NativeDisplay<B>> EGLWaylandExtensions for EGLContext<B, N> {
|
||||||
|
fn bind_wl_display(&self, display: &Display) -> Result<EGLDisplay> {
|
||||||
|
if !self.wl_drm_support {
|
||||||
|
bail!(ErrorKind::EglExtensionNotSupported(&[
|
||||||
|
"EGL_WL_bind_wayland_display"
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
if !self.egl_to_texture_support {
|
||||||
|
bail!(ErrorKind::EglExtensionNotSupported(&["GL_OES_EGL_image"]));
|
||||||
|
}
|
||||||
|
let res = unsafe { ffi::egl::BindWaylandDisplayWL(*self.display, display.ptr() as *mut _) };
|
||||||
|
if res == 0 {
|
||||||
|
bail!(ErrorKind::OtherEGLDisplayAlreadyBound);
|
||||||
|
}
|
||||||
|
Ok(EGLDisplay::new(self, unsafe { display.ptr() }))
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
//! Glium compatibility module
|
//! Glium compatibility module
|
||||||
|
|
||||||
use backend::graphics::egl::{EGLGraphicsBackend, SwapBuffersError};
|
use backend::graphics::egl::{EGLGraphicsBackend, SwapBuffersError};
|
||||||
|
use backend::graphics::egl::error::Result as EGLResult;
|
||||||
|
use backend::graphics::egl::wayland::{EGLDisplay, EGLWaylandExtensions};
|
||||||
use glium::Frame;
|
use glium::Frame;
|
||||||
use glium::SwapBuffersError as GliumSwapBuffersError;
|
use glium::SwapBuffersError as GliumSwapBuffersError;
|
||||||
use glium::backend::{Backend, Context, Facade};
|
use glium::backend::{Backend, Context, Facade};
|
||||||
|
@ -8,6 +10,7 @@ use glium::debug::DebugCallbackBehavior;
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use wayland_server::Display;
|
||||||
|
|
||||||
impl From<SwapBuffersError> for GliumSwapBuffersError {
|
impl From<SwapBuffersError> for GliumSwapBuffersError {
|
||||||
fn from(error: SwapBuffersError) -> Self {
|
fn from(error: SwapBuffersError) -> Self {
|
||||||
|
@ -73,6 +76,13 @@ impl<T: EGLGraphicsBackend + 'static> From<T> for GliumGraphicsBackend<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: EGLGraphicsBackend + EGLWaylandExtensions + 'static> EGLWaylandExtensions
|
||||||
|
for GliumGraphicsBackend<T> {
|
||||||
|
fn bind_wl_display(&self, display: &Display) -> EGLResult<EGLDisplay> {
|
||||||
|
(*self.backend).0.bind_wl_display(display)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unsafe impl<T: EGLGraphicsBackend> Backend for InternalBackend<T> {
|
unsafe impl<T: EGLGraphicsBackend> Backend for InternalBackend<T> {
|
||||||
fn swap_buffers(&self) -> Result<(), GliumSwapBuffersError> {
|
fn swap_buffers(&self) -> Result<(), GliumSwapBuffersError> {
|
||||||
self.0.swap_buffers().map_err(Into::into)
|
self.0.swap_buffers().map_err(Into::into)
|
||||||
|
|
|
@ -9,37 +9,48 @@
|
||||||
//! See also `examples/udev.rs` for pure hardware backed example of a compositor utilizing this
|
//! See also `examples/udev.rs` for pure hardware backed example of a compositor utilizing this
|
||||||
//! backend.
|
//! backend.
|
||||||
|
|
||||||
use backend::drm::{drm_device_bind, DrmBackend, DrmDevice, DrmHandler};
|
use backend::drm::{drm_device_bind, DrmDevice, DrmHandler};
|
||||||
use backend::session::{Session, SessionObserver};
|
use backend::session::{Session, SessionObserver};
|
||||||
|
use drm::Device as BasicDevice;
|
||||||
|
use drm::control::Device as ControlDevice;
|
||||||
use nix::fcntl;
|
use nix::fcntl;
|
||||||
use nix::sys::stat::{dev_t, fstat};
|
use nix::sys::stat::dev_t;
|
||||||
use std::borrow::Borrow;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::io::{Error as IoError, Result as IoResult};
|
use std::io::{Error as IoError, Result as IoResult};
|
||||||
use std::mem::drop;
|
use std::mem::drop;
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use udev::{Context, Enumerator, Event, EventType, MonitorBuilder, MonitorSocket, Result as UdevResult};
|
use udev::{Context, Enumerator, Event, EventType, MonitorBuilder, MonitorSocket, Result as UdevResult};
|
||||||
use wayland_server::{EventLoopHandle, StateProxy, StateToken};
|
use wayland_server::{EventLoopHandle, StateProxy, StateToken};
|
||||||
use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest};
|
use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest};
|
||||||
|
|
||||||
|
/// Udev's `DrmDevice` type based on the underlying session
|
||||||
|
pub struct SessionFdDrmDevice(RawFd);
|
||||||
|
|
||||||
|
impl AsRawFd for SessionFdDrmDevice {
|
||||||
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl BasicDevice for SessionFdDrmDevice {}
|
||||||
|
impl ControlDevice for SessionFdDrmDevice {}
|
||||||
|
|
||||||
/// Graphical backend that monitors available drm devices.
|
/// Graphical backend that monitors available drm devices.
|
||||||
///
|
///
|
||||||
/// Provides a way to automatically initialize a `DrmDevice` for available gpus and notifies the
|
/// Provides a way to automatically initialize a `DrmDevice` for available gpus and notifies the
|
||||||
/// given handler of any changes. Can be used to provide hot-plug functionality for gpus and
|
/// given handler of any changes. Can be used to provide hot-plug functionality for gpus and
|
||||||
/// attached monitors.
|
/// attached monitors.
|
||||||
pub struct UdevBackend<
|
pub struct UdevBackend<
|
||||||
B: Borrow<DrmBackend> + 'static,
|
H: DrmHandler<SessionFdDrmDevice> + 'static,
|
||||||
H: DrmHandler<B> + 'static,
|
|
||||||
S: Session + 'static,
|
S: Session + 'static,
|
||||||
T: UdevHandler<B, H> + 'static,
|
T: UdevHandler<H> + 'static,
|
||||||
> {
|
> {
|
||||||
devices: HashMap<
|
devices: HashMap<
|
||||||
dev_t,
|
dev_t,
|
||||||
(
|
(
|
||||||
StateToken<DrmDevice<B>>,
|
StateToken<DrmDevice<SessionFdDrmDevice>>,
|
||||||
FdEventSource<(StateToken<DrmDevice<B>>, H)>,
|
FdEventSource<(StateToken<DrmDevice<SessionFdDrmDevice>>, H)>,
|
||||||
),
|
),
|
||||||
>,
|
>,
|
||||||
monitor: MonitorSocket,
|
monitor: MonitorSocket,
|
||||||
|
@ -48,12 +59,8 @@ pub struct UdevBackend<
|
||||||
logger: ::slog::Logger,
|
logger: ::slog::Logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<H: DrmHandler<SessionFdDrmDevice> + 'static, S: Session + 'static, T: UdevHandler<H> + 'static>
|
||||||
B: From<DrmBackend> + Borrow<DrmBackend> + 'static,
|
UdevBackend<H, S, T> {
|
||||||
H: DrmHandler<B> + 'static,
|
|
||||||
S: Session + 'static,
|
|
||||||
T: UdevHandler<B, H> + 'static,
|
|
||||||
> UdevBackend<B, H, S, T> {
|
|
||||||
/// Creates a new `UdevBackend` and adds it to the given `EventLoop`'s state.
|
/// Creates a new `UdevBackend` and adds it to the given `EventLoop`'s state.
|
||||||
///
|
///
|
||||||
/// ## Arguments
|
/// ## Arguments
|
||||||
|
@ -64,7 +71,7 @@ impl<
|
||||||
/// `logger` - slog Logger to be used by the backend and its `DrmDevices`.
|
/// `logger` - slog Logger to be used by the backend and its `DrmDevices`.
|
||||||
pub fn new<'a, L>(
|
pub fn new<'a, L>(
|
||||||
mut evlh: &mut EventLoopHandle, context: &Context, mut session: S, mut handler: T, logger: L
|
mut evlh: &mut EventLoopHandle, context: &Context, mut session: S, mut handler: T, logger: L
|
||||||
) -> Result<StateToken<UdevBackend<B, H, S, T>>>
|
) -> Result<StateToken<UdevBackend<H, S, T>>>
|
||||||
where
|
where
|
||||||
L: Into<Option<::slog::Logger>>,
|
L: Into<Option<::slog::Logger>>,
|
||||||
{
|
{
|
||||||
|
@ -75,43 +82,29 @@ impl<
|
||||||
.into_iter()
|
.into_iter()
|
||||||
// Create devices
|
// Create devices
|
||||||
.flat_map(|path| {
|
.flat_map(|path| {
|
||||||
match unsafe { DrmDevice::new_from_fd(
|
match DrmDevice::new(
|
||||||
{
|
{
|
||||||
match session.open(&path, fcntl::O_RDWR | fcntl::O_CLOEXEC | fcntl::O_NOCTTY | fcntl::O_NONBLOCK) {
|
match session.open(&path, fcntl::O_RDWR | fcntl::O_CLOEXEC | fcntl::O_NOCTTY | fcntl::O_NONBLOCK) {
|
||||||
Ok(fd) => fd,
|
Ok(fd) => SessionFdDrmDevice(fd),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
warn!(logger, "Unable to open drm device {:?}, Error: {:?}. Skipping", path, err);
|
warn!(logger, "Unable to open drm device {:?}, Error: {:?}. Skipping", path, err);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, logger.clone()
|
}, logger.clone()
|
||||||
) } {
|
) {
|
||||||
// Call the handler, which might add it to the runloop
|
// Call the handler, which might add it to the runloop
|
||||||
Ok(mut device) => match handler.device_added(&mut evlh.state().as_proxy(), &mut device) {
|
Ok(mut device) => {
|
||||||
// fstat them
|
let fd = device.as_raw_fd();
|
||||||
Some(drm_handler) => match fstat(device.as_raw_fd()) {
|
let devnum = device.device_id();
|
||||||
Ok(stat) => {
|
match handler.device_added(&mut evlh.state().as_proxy(), &mut device) {
|
||||||
|
Some(drm_handler) => {
|
||||||
let token = evlh.state().insert(device);
|
let token = evlh.state().insert(device);
|
||||||
if let Ok(event_source) = drm_device_bind(&mut evlh, token.clone(), drm_handler) {
|
if let Ok(event_source) = drm_device_bind(&mut evlh, token.clone(), drm_handler) {
|
||||||
Some((stat.st_rdev, (token, event_source)))
|
Some((devnum, (token, event_source)))
|
||||||
} else {
|
} else {
|
||||||
handler.device_removed(evlh.state(), &token);
|
handler.device_removed(evlh.state(), &token);
|
||||||
let device = evlh.state().remove(token);
|
let device = evlh.state().remove(token);
|
||||||
let fd = device.as_raw_fd();
|
|
||||||
drop(device);
|
|
||||||
if let Err(err) = session.close(fd) {
|
|
||||||
warn!(logger, "Failed to close dropped device. Error: {:?}. Ignoring", err);
|
|
||||||
};
|
|
||||||
None
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(err) => {
|
|
||||||
// almost impossible to hit, but lets do it as good as possible
|
|
||||||
error!(logger, "Failed to get devnum of newly initialized device, dropping. Error: {:?}", err);
|
|
||||||
let token = evlh.state().insert(device);
|
|
||||||
handler.device_removed(evlh.state(), &token);
|
|
||||||
let device = evlh.state().remove(token);
|
|
||||||
let fd = device.as_raw_fd();
|
|
||||||
drop(device);
|
drop(device);
|
||||||
if let Err(err) = session.close(fd) {
|
if let Err(err) = session.close(fd) {
|
||||||
warn!(logger, "Failed to close dropped device. Error: {:?}. Ignoring", err);
|
warn!(logger, "Failed to close dropped device. Error: {:?}. Ignoring", err);
|
||||||
|
@ -120,21 +113,21 @@ impl<
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
let fd = device.as_raw_fd();
|
|
||||||
drop(device); //drops master
|
drop(device); //drops master
|
||||||
if let Err(err) = session.close(fd) {
|
if let Err(err) = session.close(fd) {
|
||||||
warn!(logger, "Failed to close device. Error: {:?}. Ignoring", err);
|
warn!(logger, "Failed to close device. Error: {:?}. Ignoring", err);
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
warn!(logger, "Failed to initialize device {:?}. Error: {:?}. Skipping", path, err);
|
warn!(logger, "Failed to initialize device {:?}. Error: {:?}. Skipping", path, err);
|
||||||
return None;
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<HashMap<dev_t, (StateToken<DrmDevice<B>>, FdEventSource<(StateToken<DrmDevice<B>>, H)>)>>();
|
.collect::<HashMap<dev_t, (StateToken<DrmDevice<SessionFdDrmDevice>>, FdEventSource<(StateToken<DrmDevice<SessionFdDrmDevice>>, H)>)>>();
|
||||||
|
|
||||||
let mut builder = MonitorBuilder::new(context).chain_err(|| ErrorKind::FailedToInitMonitor)?;
|
let mut builder = MonitorBuilder::new(context).chain_err(|| ErrorKind::FailedToInitMonitor)?;
|
||||||
builder
|
builder
|
||||||
|
@ -181,11 +174,10 @@ impl<
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
B: Borrow<DrmBackend> + 'static,
|
H: DrmHandler<SessionFdDrmDevice> + 'static,
|
||||||
H: DrmHandler<B> + 'static,
|
|
||||||
S: Session + 'static,
|
S: Session + 'static,
|
||||||
T: UdevHandler<B, H> + 'static,
|
T: UdevHandler<H> + 'static,
|
||||||
> SessionObserver for StateToken<UdevBackend<B, H, S, T>> {
|
> SessionObserver for StateToken<UdevBackend<H, S, T>> {
|
||||||
fn pause<'a>(&mut self, state: &mut StateProxy<'a>) {
|
fn pause<'a>(&mut self, state: &mut StateProxy<'a>) {
|
||||||
state.with_value(self, |state, udev| {
|
state.with_value(self, |state, udev| {
|
||||||
for &mut (ref mut device, _) in udev.devices.values_mut() {
|
for &mut (ref mut device, _) in udev.devices.values_mut() {
|
||||||
|
@ -207,24 +199,22 @@ impl<
|
||||||
///
|
///
|
||||||
/// Allows the backend to recieve kernel events and thus to drive the `UdevHandler`.
|
/// Allows the backend to recieve kernel events and thus to drive the `UdevHandler`.
|
||||||
/// No runtime functionality can be provided without using this function.
|
/// No runtime functionality can be provided without using this function.
|
||||||
pub fn udev_backend_bind<B, S, H, T>(
|
pub fn udev_backend_bind<S, H, T>(
|
||||||
evlh: &mut EventLoopHandle, udev: StateToken<UdevBackend<B, H, S, T>>
|
evlh: &mut EventLoopHandle, udev: StateToken<UdevBackend<H, S, T>>
|
||||||
) -> IoResult<FdEventSource<StateToken<UdevBackend<B, H, S, T>>>>
|
) -> IoResult<FdEventSource<StateToken<UdevBackend<H, S, T>>>>
|
||||||
where
|
where
|
||||||
B: From<DrmBackend> + Borrow<DrmBackend> + 'static,
|
H: DrmHandler<SessionFdDrmDevice> + 'static,
|
||||||
H: DrmHandler<B> + 'static,
|
T: UdevHandler<H> + 'static,
|
||||||
T: UdevHandler<B, H> + 'static,
|
|
||||||
S: Session + 'static,
|
S: Session + 'static,
|
||||||
{
|
{
|
||||||
let fd = evlh.state().get(&udev).monitor.as_raw_fd();
|
let fd = evlh.state().get(&udev).monitor.as_raw_fd();
|
||||||
evlh.add_fd_event_source(fd, fd_event_source_implementation(), udev, FdInterest::READ)
|
evlh.add_fd_event_source(fd, fd_event_source_implementation(), udev, FdInterest::READ)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_event_source_implementation<B, S, H, T>() -> FdEventSourceImpl<StateToken<UdevBackend<B, H, S, T>>>
|
fn fd_event_source_implementation<S, H, T>() -> FdEventSourceImpl<StateToken<UdevBackend<H, S, T>>>
|
||||||
where
|
where
|
||||||
B: From<DrmBackend> + Borrow<DrmBackend> + 'static,
|
H: DrmHandler<SessionFdDrmDevice> + 'static,
|
||||||
H: DrmHandler<B> + 'static,
|
T: UdevHandler<H> + 'static,
|
||||||
T: UdevHandler<B, H> + 'static,
|
|
||||||
S: Session + 'static,
|
S: Session + 'static,
|
||||||
{
|
{
|
||||||
FdEventSourceImpl {
|
FdEventSourceImpl {
|
||||||
|
@ -241,8 +231,7 @@ where
|
||||||
info!(evlh.state().get(token).logger, "Device Added");
|
info!(evlh.state().get(token).logger, "Device Added");
|
||||||
if let (Some(path), Some(devnum)) = (event.devnode(), event.devnum()) {
|
if let (Some(path), Some(devnum)) = (event.devnode(), event.devnum()) {
|
||||||
let mut device = {
|
let mut device = {
|
||||||
match unsafe {
|
match DrmDevice::new(
|
||||||
DrmDevice::new_from_fd(
|
|
||||||
{
|
{
|
||||||
let logger = evlh.state().get(token).logger.clone();
|
let logger = evlh.state().get(token).logger.clone();
|
||||||
match evlh.state().get_mut(token).session.open(
|
match evlh.state().get_mut(token).session.open(
|
||||||
|
@ -250,16 +239,20 @@ where
|
||||||
fcntl::O_RDWR | fcntl::O_CLOEXEC | fcntl::O_NOCTTY
|
fcntl::O_RDWR | fcntl::O_CLOEXEC | fcntl::O_NOCTTY
|
||||||
| fcntl::O_NONBLOCK,
|
| fcntl::O_NONBLOCK,
|
||||||
) {
|
) {
|
||||||
Ok(fd) => fd,
|
Ok(fd) => SessionFdDrmDevice(fd),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
warn!(logger, "Unable to open drm device {:?}, Error: {:?}. Skipping", path, err);
|
warn!(
|
||||||
|
logger,
|
||||||
|
"Unable to open drm device {:?}, Error: {:?}. Skipping",
|
||||||
|
path,
|
||||||
|
err
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
evlh.state().get(token).logger.clone(),
|
evlh.state().get(token).logger.clone(),
|
||||||
)
|
) {
|
||||||
} {
|
|
||||||
Ok(dev) => dev,
|
Ok(dev) => dev,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
warn!(
|
warn!(
|
||||||
|
@ -272,6 +265,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let fd = device.as_raw_fd();
|
||||||
match evlh.state().with_value(token, |state, udev| {
|
match evlh.state().with_value(token, |state, udev| {
|
||||||
udev.handler.device_added(state, &mut device)
|
udev.handler.device_added(state, &mut device)
|
||||||
}) {
|
}) {
|
||||||
|
@ -289,7 +283,6 @@ where
|
||||||
let mut state: StateProxy = state.into();
|
let mut state: StateProxy = state.into();
|
||||||
udev.handler.device_removed(&mut state, &dev_token);
|
udev.handler.device_removed(&mut state, &dev_token);
|
||||||
let device = state.remove(dev_token);
|
let device = state.remove(dev_token);
|
||||||
let fd = device.as_raw_fd();
|
|
||||||
drop(device);
|
drop(device);
|
||||||
if let Err(err) = udev.session.close(fd) {
|
if let Err(err) = udev.session.close(fd) {
|
||||||
warn!(
|
warn!(
|
||||||
|
@ -302,7 +295,6 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let fd = device.as_raw_fd();
|
|
||||||
drop(device);
|
drop(device);
|
||||||
evlh.state().with_value(token, |_state, udev| {
|
evlh.state().with_value(token, |_state, udev| {
|
||||||
if let Err(err) = udev.session.close(fd) {
|
if let Err(err) = udev.session.close(fd) {
|
||||||
|
@ -365,16 +357,16 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handler for the `UdevBackend`, allows to open, close and update drm devices as they change during runtime.
|
/// Handler for the `UdevBackend`, allows to open, close and update drm devices as they change during runtime.
|
||||||
pub trait UdevHandler<B: Borrow<DrmBackend> + 'static, H: DrmHandler<B> + 'static>
|
pub trait UdevHandler<H: DrmHandler<SessionFdDrmDevice> + 'static> {
|
||||||
{
|
|
||||||
/// Called on initialization for every known device and when a new device is detected.
|
/// Called on initialization for every known device and when a new device is detected.
|
||||||
///
|
///
|
||||||
/// Returning a `DrmHandler` will initialize the device, returning `None` will ignore the device.
|
/// Returning a `DrmHandler` will initialize the device, returning `None` will ignore the device.
|
||||||
///
|
///
|
||||||
/// ## Panics
|
/// ## Panics
|
||||||
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
|
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
|
||||||
fn device_added<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &mut DrmDevice<B>)
|
fn device_added<'a, S: Into<StateProxy<'a>>>(
|
||||||
-> Option<H>;
|
&mut self, state: S, device: &mut DrmDevice<SessionFdDrmDevice>
|
||||||
|
) -> Option<H>;
|
||||||
/// Called when an open device is changed.
|
/// Called when an open device is changed.
|
||||||
///
|
///
|
||||||
/// This usually indicates that some connectors did become available or were unplugged. The handler
|
/// This usually indicates that some connectors did become available or were unplugged. The handler
|
||||||
|
@ -382,7 +374,9 @@ pub trait UdevHandler<B: Borrow<DrmBackend> + 'static, H: DrmHandler<B> + 'stati
|
||||||
///
|
///
|
||||||
/// ## Panics
|
/// ## Panics
|
||||||
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
|
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
|
||||||
fn device_changed<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &StateToken<DrmDevice<B>>);
|
fn device_changed<'a, S: Into<StateProxy<'a>>>(
|
||||||
|
&mut self, state: S, device: &StateToken<DrmDevice<SessionFdDrmDevice>>
|
||||||
|
);
|
||||||
/// Called when a device was removed.
|
/// Called when a device was removed.
|
||||||
///
|
///
|
||||||
/// The device will not accept any operations anymore and its file descriptor will be closed once
|
/// The device will not accept any operations anymore and its file descriptor will be closed once
|
||||||
|
@ -390,7 +384,9 @@ pub trait UdevHandler<B: Borrow<DrmBackend> + 'static, H: DrmHandler<B> + 'stati
|
||||||
///
|
///
|
||||||
/// ## Panics
|
/// ## Panics
|
||||||
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
|
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
|
||||||
fn device_removed<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &StateToken<DrmDevice<B>>);
|
fn device_removed<'a, S: Into<StateProxy<'a>>>(
|
||||||
|
&mut self, state: S, device: &StateToken<DrmDevice<SessionFdDrmDevice>>
|
||||||
|
);
|
||||||
/// Called when the udev context has encountered and error.
|
/// Called when the udev context has encountered and error.
|
||||||
///
|
///
|
||||||
/// ## Panics
|
/// ## Panics
|
||||||
|
|
|
@ -1,44 +1,25 @@
|
||||||
//! Implementation of backend traits for types provided by `winit`
|
//! Implementation of backend traits for types provided by `winit`
|
||||||
|
|
||||||
use backend::graphics::GraphicsBackend;
|
use backend::graphics::GraphicsBackend;
|
||||||
use backend::graphics::egl::{self, EGLContext, EGLGraphicsBackend, GlAttributes, PixelFormat,
|
use backend::graphics::egl::{EGLContext, EGLGraphicsBackend, EGLSurface, PixelFormat, SwapBuffersError};
|
||||||
PixelFormatRequirements, SwapBuffersError};
|
use backend::graphics::egl::context::GlAttributes;
|
||||||
|
use backend::graphics::egl::error as egl_error;
|
||||||
|
use backend::graphics::egl::error::Result as EGLResult;
|
||||||
|
use backend::graphics::egl::native;
|
||||||
|
use backend::graphics::egl::wayland::{EGLDisplay, EGLWaylandExtensions};
|
||||||
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::libc::c_void;
|
use nix::libc::c_void;
|
||||||
use rental::TryNewError;
|
|
||||||
use std::cell::Cell;
|
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::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 wayland_server::Display;
|
||||||
use winit::{ElementState, Event, EventsLoop, KeyboardInput, MouseButton as WinitMouseButton, MouseCursor,
|
use winit::{ElementState, Event, EventsLoop, KeyboardInput, MouseButton as WinitMouseButton, MouseCursor,
|
||||||
MouseScrollDelta, Touch, TouchPhase, WindowBuilder, WindowEvent};
|
MouseScrollDelta, Touch, TouchPhase, Window as WinitWindow, WindowBuilder, WindowEvent};
|
||||||
use winit::os::unix::WindowExt;
|
|
||||||
|
|
||||||
rental! {
|
|
||||||
mod rental {
|
|
||||||
use std::boxed::Box;
|
|
||||||
use ::winit::{Window as WinitWindow};
|
|
||||||
use ::backend::graphics::egl::{EGLContext, EGLSurface};
|
|
||||||
|
|
||||||
#[rental]
|
|
||||||
pub struct Window {
|
|
||||||
window: Box<WinitWindow>,
|
|
||||||
egl: EGL<'window>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rental]
|
|
||||||
pub struct EGL<'a> {
|
|
||||||
context: Box<EGLContext<'a, WinitWindow>>,
|
|
||||||
surface: EGLSurface<'context, 'a, WinitWindow>,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
use self::rental::{Window, EGL};
|
|
||||||
|
|
||||||
error_chain! {
|
error_chain! {
|
||||||
errors {
|
errors {
|
||||||
|
@ -46,23 +27,42 @@ error_chain! {
|
||||||
InitFailed {
|
InitFailed {
|
||||||
description("Failed to initialize a window")
|
description("Failed to initialize a window")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc = "Context creation is not supported on the current window system"]
|
||||||
|
NotSupported {
|
||||||
|
description("Context creation is not supported on the current window system.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
links {
|
links {
|
||||||
EGL(egl::Error, egl::ErrorKind) #[doc = "EGL error"];
|
EGL(egl_error::Error, egl_error::ErrorKind) #[doc = "EGL error"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<H> From<TryNewError<Error, H>> for Error {
|
enum Window {
|
||||||
fn from(err: TryNewError<Error, H>) -> Error {
|
Wayland {
|
||||||
err.0
|
context: EGLContext<native::Wayland, WinitWindow>,
|
||||||
|
surface: EGLSurface<wegl::WlEglSurface>,
|
||||||
|
},
|
||||||
|
X11 {
|
||||||
|
context: EGLContext<native::X11, WinitWindow>,
|
||||||
|
surface: EGLSurface<native::XlibWindow>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Window {
|
||||||
|
fn window(&self) -> &WinitWindow {
|
||||||
|
match self {
|
||||||
|
&Window::Wayland { ref context, .. } => &**context,
|
||||||
|
&Window::X11 { ref context, .. } => &**context,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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>,
|
||||||
ready: Cell<bool>,
|
|
||||||
logger: ::slog::Logger,
|
logger: ::slog::Logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,31 +136,26 @@ where
|
||||||
.chain_err(|| ErrorKind::InitFailed)?;
|
.chain_err(|| ErrorKind::InitFailed)?;
|
||||||
debug!(log, "Window created");
|
debug!(log, "Window created");
|
||||||
|
|
||||||
let window = Rc::new(Window::try_new(Box::new(winit_window), |window| {
|
let reqs = Default::default();
|
||||||
EGL::try_new(
|
let window = Rc::new(
|
||||||
Box::new(match EGLContext::new_from_winit(
|
if native::NativeDisplay::<native::Wayland>::is_backend(&winit_window) {
|
||||||
&*window,
|
let context =
|
||||||
attributes,
|
EGLContext::<native::Wayland, WinitWindow>::new(winit_window, attributes, reqs, log.clone())?;
|
||||||
PixelFormatRequirements {
|
let surface = context.create_surface(())?;
|
||||||
hardware_accelerated: Some(true),
|
Window::Wayland { context, surface }
|
||||||
color_bits: Some(24),
|
} else if native::NativeDisplay::<native::X11>::is_backend(&winit_window) {
|
||||||
alpha_bits: Some(8),
|
let context =
|
||||||
..Default::default()
|
EGLContext::<native::X11, WinitWindow>::new(winit_window, attributes, reqs, log.clone())?;
|
||||||
|
let surface = context.create_surface(())?;
|
||||||
|
Window::X11 { context, surface }
|
||||||
|
} else {
|
||||||
|
bail!(ErrorKind::NotSupported);
|
||||||
},
|
},
|
||||||
log.clone(),
|
);
|
||||||
) {
|
|
||||||
Ok(context) => context,
|
|
||||||
Err(err) => bail!(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(),
|
||||||
ready: Cell::new(false),
|
|
||||||
logger: log.new(o!("smithay_winit_component" => "graphics")),
|
logger: log.new(o!("smithay_winit_component" => "graphics")),
|
||||||
},
|
},
|
||||||
WinitInputBackend {
|
WinitInputBackend {
|
||||||
|
@ -189,7 +184,7 @@ impl GraphicsBackend for WinitGraphicsBackend {
|
||||||
|
|
||||||
fn set_cursor_position(&self, x: u32, y: u32) -> ::std::result::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.head().set_cursor_position(x as i32, y as i32)
|
self.window.window().set_cursor_position(x as i32, y as i32)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_cursor_representation(
|
fn set_cursor_representation(
|
||||||
|
@ -197,7 +192,7 @@ impl GraphicsBackend for WinitGraphicsBackend {
|
||||||
) -> ::std::result::Result<(), ()> {
|
) -> ::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.head().set_cursor(*cursor);
|
self.window.window().set_cursor(*cursor);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -205,44 +200,62 @@ impl GraphicsBackend for WinitGraphicsBackend {
|
||||||
impl EGLGraphicsBackend for WinitGraphicsBackend {
|
impl EGLGraphicsBackend for WinitGraphicsBackend {
|
||||||
fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
|
fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
|
||||||
trace!(self.logger, "Swapping buffers");
|
trace!(self.logger, "Swapping buffers");
|
||||||
if !self.ready.get() {
|
match *self.window {
|
||||||
if self.window.head().is_ready() {
|
Window::Wayland { ref surface, .. } => surface.swap_buffers(),
|
||||||
// avoid locking the mutex every time once the window is ready
|
Window::X11 { ref surface, .. } => surface.swap_buffers(),
|
||||||
self.ready.set(true);
|
|
||||||
} else {
|
|
||||||
// Not yet ready, just silently ignore the swap-buffers call
|
|
||||||
return Ok(());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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.window.rent(|egl| egl.head().get_proc_address(symbol))
|
match *self.window {
|
||||||
|
Window::Wayland { ref context, .. } => context.get_proc_address(symbol),
|
||||||
|
Window::X11 { ref context, .. } => context.get_proc_address(symbol),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_framebuffer_dimensions(&self) -> (u32, u32) {
|
fn get_framebuffer_dimensions(&self) -> (u32, u32) {
|
||||||
self.window
|
self.window
|
||||||
.head()
|
.window()
|
||||||
.get_inner_size_pixels()
|
.get_inner_size()
|
||||||
.expect("Window does not exist anymore")
|
.expect("Window does not exist anymore")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_current(&self) -> bool {
|
fn is_current(&self) -> bool {
|
||||||
self.window
|
match *self.window {
|
||||||
.rent(|egl| egl.rent_all(|egl| egl.context.is_current() && egl.surface.is_current()))
|
Window::Wayland {
|
||||||
|
ref context,
|
||||||
|
ref surface,
|
||||||
|
} => context.is_current() && surface.is_current(),
|
||||||
|
Window::X11 {
|
||||||
|
ref context,
|
||||||
|
ref surface,
|
||||||
|
} => context.is_current() && surface.is_current(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
|
unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
|
||||||
debug!(self.logger, "Setting EGL context to be the current context");
|
trace!(self.logger, "Setting EGL context to be the current context");
|
||||||
self.window
|
match *self.window {
|
||||||
.rent(|egl| egl.rent(|surface| surface.make_current()))
|
Window::Wayland { ref surface, .. } => surface.make_current(),
|
||||||
|
Window::X11 { ref surface, .. } => surface.make_current(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_pixel_format(&self) -> PixelFormat {
|
fn get_pixel_format(&self) -> PixelFormat {
|
||||||
self.window.rent(|egl| egl.head().get_pixel_format())
|
match *self.window {
|
||||||
|
Window::Wayland { ref context, .. } => context.get_pixel_format(),
|
||||||
|
Window::X11 { ref context, .. } => context.get_pixel_format(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EGLWaylandExtensions for WinitGraphicsBackend {
|
||||||
|
fn bind_wl_display(&self, display: &Display) -> EGLResult<EGLDisplay> {
|
||||||
|
match *self.window {
|
||||||
|
Window::Wayland { ref context, .. } => context.bind_wl_display(display),
|
||||||
|
Window::X11 { ref context, .. } => context.bind_wl_display(display),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,8 +339,8 @@ impl PointerMotionAbsoluteEvent for WinitMouseMovedEvent {
|
||||||
cmp::min(
|
cmp::min(
|
||||||
(self.x * width as f64
|
(self.x * width as f64
|
||||||
/ self.window
|
/ self.window
|
||||||
.head()
|
.window()
|
||||||
.get_inner_size_points()
|
.get_inner_size()
|
||||||
.unwrap_or((width, 0))
|
.unwrap_or((width, 0))
|
||||||
.0 as f64) as u32,
|
.0 as f64) as u32,
|
||||||
0,
|
0,
|
||||||
|
@ -338,8 +351,8 @@ impl PointerMotionAbsoluteEvent for WinitMouseMovedEvent {
|
||||||
cmp::min(
|
cmp::min(
|
||||||
(self.y * height as f64
|
(self.y * height as f64
|
||||||
/ self.window
|
/ self.window
|
||||||
.head()
|
.window()
|
||||||
.get_inner_size_points()
|
.get_inner_size()
|
||||||
.unwrap_or((0, height))
|
.unwrap_or((0, height))
|
||||||
.1 as f64) as u32,
|
.1 as f64) as u32,
|
||||||
0,
|
0,
|
||||||
|
@ -439,8 +452,8 @@ impl TouchDownEvent for WinitTouchStartedEvent {
|
||||||
cmp::min(
|
cmp::min(
|
||||||
self.location.0 as i32 * width as i32
|
self.location.0 as i32 * width as i32
|
||||||
/ self.window
|
/ self.window
|
||||||
.head()
|
.window()
|
||||||
.get_inner_size_points()
|
.get_inner_size()
|
||||||
.unwrap_or((width, 0))
|
.unwrap_or((width, 0))
|
||||||
.0 as i32,
|
.0 as i32,
|
||||||
0,
|
0,
|
||||||
|
@ -451,8 +464,8 @@ impl TouchDownEvent for WinitTouchStartedEvent {
|
||||||
cmp::min(
|
cmp::min(
|
||||||
self.location.1 as i32 * height as i32
|
self.location.1 as i32 * height as i32
|
||||||
/ self.window
|
/ self.window
|
||||||
.head()
|
.window()
|
||||||
.get_inner_size_points()
|
.get_inner_size()
|
||||||
.unwrap_or((0, height))
|
.unwrap_or((0, height))
|
||||||
.1 as i32,
|
.1 as i32,
|
||||||
0,
|
0,
|
||||||
|
@ -491,8 +504,8 @@ 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.location.0 as u32 * width
|
||||||
/ self.window
|
/ self.window
|
||||||
.head()
|
.window()
|
||||||
.get_inner_size_points()
|
.get_inner_size()
|
||||||
.unwrap_or((width, 0))
|
.unwrap_or((width, 0))
|
||||||
.0
|
.0
|
||||||
}
|
}
|
||||||
|
@ -500,8 +513,8 @@ impl TouchMotionEvent for WinitTouchMovedEvent {
|
||||||
fn y_transformed(&self, height: u32) -> u32 {
|
fn y_transformed(&self, height: u32) -> u32 {
|
||||||
self.location.1 as u32 * height
|
self.location.1 as u32 * height
|
||||||
/ self.window
|
/ self.window
|
||||||
.head()
|
.window()
|
||||||
.get_inner_size_points()
|
.get_inner_size()
|
||||||
.unwrap_or((0, height))
|
.unwrap_or((0, height))
|
||||||
.1
|
.1
|
||||||
}
|
}
|
||||||
|
@ -626,14 +639,13 @@ 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.head().set_inner_size(x, y);
|
window.window().set_inner_size(x, y);
|
||||||
window.rent(|egl| {
|
match **window {
|
||||||
egl.rent(|surface| {
|
Window::Wayland { ref surface, .. } => {
|
||||||
if let Some(wegl_surface) = (**surface).as_ref() {
|
surface.resize(x as i32, y as i32, 0, 0)
|
||||||
wegl_surface.resize(x as i32, y as i32, 0, 0)
|
|
||||||
}
|
}
|
||||||
})
|
_ => {}
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
WindowEvent::KeyboardInput {
|
WindowEvent::KeyboardInput {
|
||||||
|
@ -667,7 +679,7 @@ impl InputBackend for WinitInputBackend {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
WindowEvent::MouseMoved {
|
WindowEvent::CursorMoved {
|
||||||
position: (x, y), ..
|
position: (x, y), ..
|
||||||
},
|
},
|
||||||
Some(handler),
|
Some(handler),
|
||||||
|
|
|
@ -8,13 +8,12 @@
|
||||||
#![recursion_limit = "1024"]
|
#![recursion_limit = "1024"]
|
||||||
|
|
||||||
extern crate image;
|
extern crate image;
|
||||||
#[macro_use]
|
#[cfg_attr(feature = "backend_session", macro_use)]
|
||||||
extern crate nix;
|
extern crate nix;
|
||||||
#[macro_use]
|
|
||||||
extern crate rental;
|
|
||||||
extern crate tempfile;
|
extern crate tempfile;
|
||||||
extern crate wayland_protocols;
|
extern crate wayland_protocols;
|
||||||
extern crate wayland_server;
|
extern crate wayland_server;
|
||||||
|
extern crate wayland_sys;
|
||||||
extern crate xkbcommon;
|
extern crate xkbcommon;
|
||||||
|
|
||||||
#[cfg(feature = "backend_drm")]
|
#[cfg(feature = "backend_drm")]
|
||||||
|
@ -48,6 +47,9 @@ extern crate slog_stdlog;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate error_chain;
|
extern crate error_chain;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate lazy_static;
|
||||||
|
|
||||||
pub mod backend;
|
pub mod backend;
|
||||||
pub mod wayland;
|
pub mod wayland;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
|
@ -72,9 +72,7 @@
|
||||||
//!
|
//!
|
||||||
//! As you can see in the previous example, in the end we are retrieving a token from
|
//! As you can see in the previous example, in the end we are retrieving a token from
|
||||||
//! the init function. This token is necessary to retrieve the metadata associated with
|
//! the init function. This token is necessary to retrieve the metadata associated with
|
||||||
//! a surface. It can be cloned, and is sendable accross threads (as long as your
|
//! a surface. It can be cloned. See `CompositorToken` for the details of what it enables you.
|
||||||
//! `Mydata` and `MyRoles` are `Send` too). See `CompositorToken` for
|
|
||||||
//! the details of what it enables you.
|
|
||||||
//!
|
//!
|
||||||
//! The surface metadata is held in the `SurfaceAttributes` struct. In contains double-buffered
|
//! The surface metadata is held in the `SurfaceAttributes` struct. In contains double-buffered
|
||||||
//! state pending from the client as defined by the protocol for `wl_surface`, as well as your
|
//! state pending from the client as defined by the protocol for `wl_surface`, as well as your
|
||||||
|
@ -248,9 +246,6 @@ pub struct CompositorToken<U, R, ID> {
|
||||||
_idata: ::std::marker::PhantomData<*mut ID>,
|
_idata: ::std::marker::PhantomData<*mut ID>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<U: Send, R: Send, ID> Send for CompositorToken<U, R, ID> {}
|
|
||||||
unsafe impl<U: Send, R: Send, ID> Sync for CompositorToken<U, R, ID> {}
|
|
||||||
|
|
||||||
// we implement them manually because #[derive(..)] would require
|
// we implement them manually because #[derive(..)] would require
|
||||||
// U: Clone and R: Clone
|
// U: Clone and R: Clone
|
||||||
impl<U, R, ID> Copy for CompositorToken<U, R, ID> {}
|
impl<U, R, ID> Copy for CompositorToken<U, R, ID> {}
|
||||||
|
@ -270,7 +265,7 @@ impl<U, R, ID> CompositorToken<U, R, ID> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<U: Send + 'static, R: Send + 'static, ID: 'static> CompositorToken<U, R, ID> {
|
impl<U: 'static, R: 'static, ID: 'static> CompositorToken<U, R, ID> {
|
||||||
/// Access the data of a surface
|
/// Access the data of a surface
|
||||||
///
|
///
|
||||||
/// The closure will be called with the contents of the data associated with this surface.
|
/// The closure will be called with the contents of the data associated with this surface.
|
||||||
|
|
|
@ -236,7 +236,9 @@ fn seat_global_bind(
|
||||||
) {
|
) {
|
||||||
evlh.register(&seat, seat_implementation(), token.clone(), None);
|
evlh.register(&seat, seat_implementation(), token.clone(), None);
|
||||||
let seat_mgr = evlh.state().get_mut(token);
|
let seat_mgr = evlh.state().get_mut(token);
|
||||||
|
if seat.version() >= 2 {
|
||||||
seat.name(seat_mgr.name.clone());
|
seat.name(seat_mgr.name.clone());
|
||||||
|
}
|
||||||
seat.capabilities(seat_mgr.compute_caps());
|
seat.capabilities(seat_mgr.compute_caps());
|
||||||
seat_mgr.known_seats.push(seat);
|
seat_mgr.known_seats.push(seat);
|
||||||
}
|
}
|
||||||
|
|
|
@ -449,8 +449,7 @@ impl<SD> ShellClient<SD> {
|
||||||
}
|
}
|
||||||
match self.kind {
|
match self.kind {
|
||||||
ShellClientKind::Wl(ref shell) => {
|
ShellClientKind::Wl(ref shell) => {
|
||||||
let mutex =
|
let mutex = unsafe { &*(shell.get_user_data() as *mut self::wl_handlers::ShellUserData<SD>) };
|
||||||
unsafe { &*(shell.get_user_data() as *mut self::wl_handlers::ShellUserData<SD>) };
|
|
||||||
let mut guard = mutex.lock().unwrap();
|
let mut guard = mutex.lock().unwrap();
|
||||||
if guard.0.pending_ping == 0 {
|
if guard.0.pending_ping == 0 {
|
||||||
return Err(());
|
return Err(());
|
||||||
|
@ -487,8 +486,7 @@ impl<SD> ShellClient<SD> {
|
||||||
}
|
}
|
||||||
match self.kind {
|
match self.kind {
|
||||||
ShellClientKind::Wl(ref shell) => {
|
ShellClientKind::Wl(ref shell) => {
|
||||||
let mutex =
|
let mutex = unsafe { &*(shell.get_user_data() as *mut self::wl_handlers::ShellUserData<SD>) };
|
||||||
unsafe { &*(shell.get_user_data() as *mut self::wl_handlers::ShellUserData<SD>) };
|
|
||||||
let mut guard = mutex.lock().unwrap();
|
let mut guard = mutex.lock().unwrap();
|
||||||
Ok(f(&mut guard.0.data))
|
Ok(f(&mut guard.0.data))
|
||||||
}
|
}
|
||||||
|
|
|
@ -271,8 +271,7 @@ where
|
||||||
{
|
{
|
||||||
wl_shell_surface::Implementation {
|
wl_shell_surface::Implementation {
|
||||||
pong: |evlh, idata, _, shell_surface, serial| {
|
pong: |evlh, idata, _, shell_surface, serial| {
|
||||||
let &(_, ref shell) =
|
let &(_, ref shell) = unsafe { &*(shell_surface.get_user_data() as *mut ShellSurfaceUserData) };
|
||||||
unsafe { &*(shell_surface.get_user_data() as *mut ShellSurfaceUserData) };
|
|
||||||
let valid = {
|
let valid = {
|
||||||
let mutex = unsafe { &*(shell.get_user_data() as *mut ShellUserData<SD>) };
|
let mutex = unsafe { &*(shell.get_user_data() as *mut ShellUserData<SD>) };
|
||||||
let mut guard = mutex.lock().unwrap();
|
let mut guard = mutex.lock().unwrap();
|
||||||
|
|
|
@ -230,9 +230,8 @@ fn destroy_surface(surface: &zxdg_surface_v6::ZxdgSurfaceV6) {
|
||||||
let ptr = surface.get_user_data();
|
let ptr = surface.get_user_data();
|
||||||
surface.set_user_data(::std::ptr::null_mut());
|
surface.set_user_data(::std::ptr::null_mut());
|
||||||
// drop the state
|
// drop the state
|
||||||
let data = unsafe {
|
let data =
|
||||||
Box::from_raw(ptr as *mut (zxdg_surface_v6::ZxdgSurfaceV6, zxdg_shell_v6::ZxdgShellV6))
|
unsafe { Box::from_raw(ptr as *mut (zxdg_surface_v6::ZxdgSurfaceV6, zxdg_shell_v6::ZxdgShellV6)) };
|
||||||
};
|
|
||||||
// explicit call to drop to not forget what we're doing here
|
// explicit call to drop to not forget what we're doing here
|
||||||
::std::mem::drop(data);
|
::std::mem::drop(data);
|
||||||
}
|
}
|
||||||
|
@ -544,9 +543,8 @@ where
|
||||||
with_surface_toplevel_data(idata, toplevel, |toplevel_data| {
|
with_surface_toplevel_data(idata, toplevel, |toplevel_data| {
|
||||||
toplevel_data.parent = parent.map(|toplevel_surface_parent| {
|
toplevel_data.parent = parent.map(|toplevel_surface_parent| {
|
||||||
let parent_ptr = toplevel_surface_parent.get_user_data();
|
let parent_ptr = toplevel_surface_parent.get_user_data();
|
||||||
let &(ref parent_surface, _) = unsafe {
|
let &(ref parent_surface, _) =
|
||||||
&*(parent_ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6))
|
unsafe { &*(parent_ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) };
|
||||||
};
|
|
||||||
unsafe { parent_surface.clone_unchecked() }
|
unsafe { parent_surface.clone_unchecked() }
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
|
@ -43,6 +43,9 @@ containers:
|
||||||
- !Container base
|
- !Container base
|
||||||
- !Env HOME: /work/.vagga/nightly-home
|
- !Env HOME: /work/.vagga/nightly-home
|
||||||
- !Sh curl https://sh.rustup.rs -sSf | sh -s -- -y --default-host x86_64-unknown-linux-gnu --default-toolchain nightly --no-modify-path
|
- !Sh curl https://sh.rustup.rs -sSf | sh -s -- -y --default-host x86_64-unknown-linux-gnu --default-toolchain nightly --no-modify-path
|
||||||
|
- !Env PATH: /bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/work/.vagga/nightly-home/.cargo/bin:/work/.vagga/nightly-home/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/
|
||||||
|
- !Sh rustup self update
|
||||||
|
- !Sh rustup component add rustfmt-preview
|
||||||
|
|
||||||
commands:
|
commands:
|
||||||
update-stable: !Command
|
update-stable: !Command
|
||||||
|
@ -58,8 +61,7 @@ commands:
|
||||||
update-nightly: !Command
|
update-nightly: !Command
|
||||||
description: Update container
|
description: Update container
|
||||||
container: nightly
|
container: nightly
|
||||||
run: |
|
run: rustup update
|
||||||
rustup update
|
|
||||||
|
|
||||||
cargo-stable: !Command
|
cargo-stable: !Command
|
||||||
description: Run cargo
|
description: Run cargo
|
||||||
|
|
Loading…
Reference in New Issue