Merge pull request #116 from Smithay/refactor/backend
Refactor drm backend (v3)
This commit is contained in:
commit
60bb5e8d5a
|
@ -36,11 +36,15 @@ env:
|
|||
# test individual features
|
||||
- FEATURES="backend_winit"
|
||||
- FEATURES="backend_drm"
|
||||
- FEATURES="backend_drm_legacy"
|
||||
- FEATURES="backend_drm_gbm"
|
||||
- FEATURES="backend_drm_egl"
|
||||
- FEATURES="backend_egl"
|
||||
- FEATURES="backend_libinput"
|
||||
- FEATURES="backend_udev"
|
||||
- FEATURES="backend_session"
|
||||
- FEATURES="backend_session_udev"
|
||||
- FEATURES="backend_session_logind"
|
||||
- FEATURES="renderer_gl"
|
||||
- FEATURES="renderer_glium"
|
||||
- FEATURES="xwayland"
|
||||
# test default features
|
||||
|
|
34
Cargo.toml
34
Cargo.toml
|
@ -10,9 +10,9 @@ repository = "https://github.com/Smithay/smithay"
|
|||
members = [ "anvil" ]
|
||||
|
||||
[dependencies]
|
||||
wayland-server = "0.21.1"
|
||||
wayland-sys = "0.21.1"
|
||||
wayland-server = "0.21.6"
|
||||
wayland-commons = "0.21.1"
|
||||
wayland-sys = { version = "0.21.6", optional = true }
|
||||
nix = "0.11"
|
||||
xkbcommon = "0.2.1"
|
||||
tempfile = "2.1.5"
|
||||
|
@ -21,29 +21,37 @@ slog-stdlog = "3.0.2"
|
|||
libloading = "0.4.0"
|
||||
wayland-client = { version = "0.21.1", features = ["egl"], optional = true }
|
||||
winit = { version = "0.18.0", optional = true }
|
||||
drm = { version = "^0.3.1", optional = true }
|
||||
gbm = { version = "^0.4.0", optional = true, default-features = false, features = ["drm-support"] }
|
||||
drm = { version = "^0.3.4", optional = true }
|
||||
gbm = { version = "^0.5.0", optional = true, default-features = false, features = ["drm-support"] }
|
||||
glium = { version = "0.19.0", optional = true, default-features = false }
|
||||
input = { version = "0.4.0", optional = true }
|
||||
input = { version = "0.4.1", optional = true }
|
||||
udev = { version = "0.2.0", optional = true }
|
||||
dbus = { version = "0.6.1", optional = true }
|
||||
systemd = { version = "^0.2.0", optional = true }
|
||||
wayland-protocols = { version = "0.21.1", features = ["unstable_protocols", "native_server"] }
|
||||
wayland-protocols = { version = "0.21.3", features = ["unstable_protocols", "server"] }
|
||||
image = "0.17.0"
|
||||
error-chain = "0.11.0"
|
||||
lazy_static = "1.0.0"
|
||||
|
||||
[dev-dependencies]
|
||||
slog-term = "2.3"
|
||||
|
||||
[build-dependencies]
|
||||
gl_generator = "0.9"
|
||||
gl_generator = { version = "0.9", optional = true }
|
||||
|
||||
[features]
|
||||
default = ["backend_winit", "backend_drm", "backend_libinput", "backend_udev", "renderer_glium", "xwayland"]
|
||||
backend_winit = ["winit", "wayland-server/dlopen", "wayland-client/dlopen"]
|
||||
backend_drm = ["drm", "gbm"]
|
||||
default = ["backend_winit", "backend_drm_legacy", "backend_drm_gbm", "backend_drm_egl", "backend_libinput", "backend_udev", "backend_session", "renderer_glium", "xwayland"]
|
||||
backend_winit = ["winit", "wayland-server/dlopen", "wayland-client/dlopen", "backend_egl", "renderer_gl", "native_lib"]
|
||||
backend_drm = ["drm"]
|
||||
backend_drm_legacy = ["backend_drm"]
|
||||
backend_drm_gbm = ["backend_drm", "gbm"]
|
||||
backend_drm_egl = ["backend_drm", "backend_egl"]
|
||||
backend_egl = ["gl_generator"]
|
||||
backend_libinput = ["input"]
|
||||
backend_session = []
|
||||
backend_session_udev = ["udev", "backend_session"]
|
||||
backend_udev = ["udev"]
|
||||
backend_session_logind = ["dbus", "systemd", "backend_session"]
|
||||
backend_udev = ["udev", "backend_drm", "backend_session_udev"]
|
||||
renderer_glium = ["glium"]
|
||||
renderer_gl = ["gl_generator"]
|
||||
renderer_glium = ["renderer_gl", "glium"]
|
||||
native_lib = ["wayland-sys", "wayland-server/native_lib", "wayland-protocols/native_server"]
|
||||
xwayland = []
|
|
@ -23,8 +23,8 @@ features = [ "renderer_glium" ]
|
|||
gl_generator = "0.9"
|
||||
|
||||
[features]
|
||||
default = [ "winit", "tty_launch", "udev" ]
|
||||
default = [ "winit", "egl", "udev" ]
|
||||
egl = [ "smithay/native_lib" ]
|
||||
winit = [ "smithay/backend_winit" ]
|
||||
tty_launch = [ "smithay/backend_libinput", "smithay/backend_drm" ]
|
||||
udev = [ "tty_launch", "smithay/backend_udev" ]
|
||||
udev = [ "smithay/backend_libinput", "smithay/backend_drm_legacy", "smithay/backend_drm_gbm", "smithay/backend_drm_egl", "smithay/backend_udev", "smithay/backend_session" ]
|
||||
logind = [ "smithay/backend_session_logind" ]
|
||||
|
|
|
@ -11,20 +11,18 @@ use glium::{
|
|||
};
|
||||
use slog::Logger;
|
||||
|
||||
#[cfg(feature = "egl")]
|
||||
use smithay::backend::egl::EGLDisplay;
|
||||
use smithay::{
|
||||
backend::graphics::{
|
||||
egl::{
|
||||
error::Result as EGLResult,
|
||||
wayland::{BufferAccessError, EGLDisplay, EGLImages, EGLWaylandExtensions, Format},
|
||||
EGLGraphicsBackend,
|
||||
},
|
||||
glium::GliumGraphicsBackend,
|
||||
backend::{
|
||||
egl::{BufferAccessError, EGLImages, Format},
|
||||
graphics::{gl::GLGraphicsBackend, glium::GliumGraphicsBackend},
|
||||
},
|
||||
wayland::{
|
||||
compositor::{roles::Role, SubsurfaceRole, TraversalAction},
|
||||
shm::with_buffer_contents as shm_buffer_contents,
|
||||
},
|
||||
wayland_server::{protocol::wl_buffer, Display, Resource},
|
||||
wayland_server::{protocol::wl_buffer, Resource},
|
||||
};
|
||||
|
||||
use shaders;
|
||||
|
@ -38,22 +36,24 @@ struct Vertex {
|
|||
|
||||
implement_vertex!(Vertex, position, tex_coords);
|
||||
|
||||
pub struct GliumDrawer<F: EGLGraphicsBackend + 'static> {
|
||||
pub struct GliumDrawer<F: GLGraphicsBackend + 'static> {
|
||||
display: GliumGraphicsBackend<F>,
|
||||
vertex_buffer: glium::VertexBuffer<Vertex>,
|
||||
index_buffer: glium::IndexBuffer<u16>,
|
||||
programs: [glium::Program; shaders::FRAGMENT_COUNT],
|
||||
#[cfg(feature = "egl")]
|
||||
egl_display: Rc<RefCell<Option<EGLDisplay>>>,
|
||||
log: Logger,
|
||||
}
|
||||
|
||||
impl<F: EGLGraphicsBackend + 'static> GliumDrawer<F> {
|
||||
impl<F: GLGraphicsBackend + 'static> GliumDrawer<F> {
|
||||
pub fn borrow(&self) -> Ref<F> {
|
||||
self.display.borrow()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Into<GliumGraphicsBackend<T>> + EGLGraphicsBackend + 'static> GliumDrawer<T> {
|
||||
impl<T: Into<GliumGraphicsBackend<T>> + GLGraphicsBackend + 'static> GliumDrawer<T> {
|
||||
#[cfg(feature = "egl")]
|
||||
pub fn init(backend: T, egl_display: Rc<RefCell<Option<EGLDisplay>>>, log: Logger) -> GliumDrawer<T> {
|
||||
let display = backend.into();
|
||||
|
||||
|
@ -78,7 +78,8 @@ impl<T: Into<GliumGraphicsBackend<T>> + EGLGraphicsBackend + 'static> GliumDrawe
|
|||
tex_coords: [1.0, 0.0],
|
||||
},
|
||||
],
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// building the index buffer
|
||||
let index_buffer =
|
||||
|
@ -95,9 +96,53 @@ impl<T: Into<GliumGraphicsBackend<T>> + EGLGraphicsBackend + 'static> GliumDrawe
|
|||
log,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "egl"))]
|
||||
pub fn init(backend: T, log: Logger) -> GliumDrawer<T> {
|
||||
let display = backend.into();
|
||||
|
||||
// building the vertex buffer, which contains all the vertices that we will draw
|
||||
let vertex_buffer = glium::VertexBuffer::new(
|
||||
&display,
|
||||
&[
|
||||
Vertex {
|
||||
position: [0.0, 0.0],
|
||||
tex_coords: [0.0, 0.0],
|
||||
},
|
||||
Vertex {
|
||||
position: [0.0, 1.0],
|
||||
tex_coords: [0.0, 1.0],
|
||||
},
|
||||
Vertex {
|
||||
position: [1.0, 1.0],
|
||||
tex_coords: [1.0, 1.0],
|
||||
},
|
||||
Vertex {
|
||||
position: [1.0, 0.0],
|
||||
tex_coords: [1.0, 0.0],
|
||||
},
|
||||
],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// building the index buffer
|
||||
let index_buffer =
|
||||
glium::IndexBuffer::new(&display, PrimitiveType::TriangleStrip, &[1 as u16, 2, 0, 3]).unwrap();
|
||||
|
||||
let programs = opengl_programs!(&display);
|
||||
|
||||
GliumDrawer {
|
||||
display,
|
||||
vertex_buffer,
|
||||
index_buffer,
|
||||
programs,
|
||||
log,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: EGLGraphicsBackend + 'static> GliumDrawer<F> {
|
||||
impl<F: GLGraphicsBackend + 'static> GliumDrawer<F> {
|
||||
#[cfg(feature = "egl")]
|
||||
pub fn texture_from_buffer(&self, buffer: Resource<wl_buffer::WlBuffer>) -> Result<TextureMetadata, ()> {
|
||||
// try to retrieve the egl contents of this buffer
|
||||
let images = if let Some(display) = &self.egl_display.borrow().as_ref() {
|
||||
|
@ -122,7 +167,8 @@ impl<F: EGLGraphicsBackend + 'static> GliumDrawer<F> {
|
|||
MipmapsOption::NoMipmap,
|
||||
images.width,
|
||||
images.height,
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
unsafe {
|
||||
images
|
||||
.bind_to_texture(0, opengl_texture.get_id())
|
||||
|
@ -138,6 +184,21 @@ impl<F: EGLGraphicsBackend + 'static> GliumDrawer<F> {
|
|||
}
|
||||
Err(BufferAccessError::NotManaged(buffer)) => {
|
||||
// this is not an EGL buffer, try SHM
|
||||
self.texture_from_shm_buffer(buffer)
|
||||
}
|
||||
Err(err) => {
|
||||
error!(self.log, "EGL error"; "err" => format!("{:?}", err));
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "egl"))]
|
||||
pub fn texture_from_buffer(&self, buffer: Resource<wl_buffer::WlBuffer>) -> Result<TextureMetadata, ()> {
|
||||
self.texture_from_shm_buffer(buffer)
|
||||
}
|
||||
|
||||
fn texture_from_shm_buffer(&self, buffer: Resource<wl_buffer::WlBuffer>) -> Result<TextureMetadata, ()> {
|
||||
match shm_buffer_contents(&buffer, |slice, data| {
|
||||
::shm_load::load_shm_buffer(data, slice)
|
||||
.map(|(image, kind)| (Texture2d::new(&self.display, image).unwrap(), kind, data))
|
||||
|
@ -147,6 +208,7 @@ impl<F: EGLGraphicsBackend + 'static> GliumDrawer<F> {
|
|||
fragment: kind,
|
||||
y_inverted: false,
|
||||
dimensions: (data.width as u32, data.height as u32),
|
||||
#[cfg(feature = "egl")]
|
||||
images: None,
|
||||
}),
|
||||
Ok(Err(format)) => {
|
||||
|
@ -159,12 +221,6 @@ impl<F: EGLGraphicsBackend + 'static> GliumDrawer<F> {
|
|||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
error!(self.log, "EGL error"; "err" => format!("{:?}", err));
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_texture(
|
||||
&self,
|
||||
|
@ -208,7 +264,8 @@ impl<F: EGLGraphicsBackend + 'static> GliumDrawer<F> {
|
|||
blend: blending,
|
||||
..Default::default()
|
||||
},
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -217,21 +274,16 @@ impl<F: EGLGraphicsBackend + 'static> GliumDrawer<F> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<G: EGLWaylandExtensions + EGLGraphicsBackend + 'static> EGLWaylandExtensions for GliumDrawer<G> {
|
||||
fn bind_wl_display(&self, display: &Display) -> EGLResult<EGLDisplay> {
|
||||
self.display.bind_wl_display(display)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TextureMetadata {
|
||||
pub texture: Texture2d,
|
||||
pub fragment: usize,
|
||||
pub y_inverted: bool,
|
||||
pub dimensions: (u32, u32),
|
||||
#[cfg(feature = "egl")]
|
||||
images: Option<EGLImages>,
|
||||
}
|
||||
|
||||
impl<F: EGLGraphicsBackend + 'static> GliumDrawer<F> {
|
||||
impl<F: GLGraphicsBackend + 'static> GliumDrawer<F> {
|
||||
pub fn draw_windows(&self, window_map: &MyWindowMap, compositor_token: MyCompositorToken, log: &Logger) {
|
||||
let mut frame = self.draw();
|
||||
frame.clear(None, Some((0.8, 0.8, 0.9, 1.0)), false, Some(1.0), None);
|
||||
|
@ -290,7 +342,8 @@ impl<F: EGLGraphicsBackend + 'static> GliumDrawer<F> {
|
|||
TraversalAction::SkipChildren
|
||||
}
|
||||
},
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ use std::{
|
|||
use slog::Logger;
|
||||
|
||||
#[cfg(feature = "udev")]
|
||||
use smithay::backend::session::auto::AutoSession;
|
||||
use smithay::backend::session::{auto::AutoSession, Session};
|
||||
use smithay::{
|
||||
backend::input::{
|
||||
self, Event, InputBackend, InputHandler, KeyState, KeyboardKeyEvent, PointerAxisEvent,
|
||||
|
@ -131,13 +131,15 @@ impl<B: InputBackend> InputHandler<B> for AnvilInputHandler {
|
|||
info!(self.log, "Quitting.");
|
||||
self.running.store(false, Ordering::SeqCst);
|
||||
}
|
||||
#[cfg(feature = "tty_lauch")]
|
||||
KeyAction::VtSwitch(vt) => if let Some(ref mut session) = self.session {
|
||||
#[cfg(feature = "udev")]
|
||||
KeyAction::VtSwitch(vt) => {
|
||||
if let Some(ref mut session) = self.session {
|
||||
info!(log, "Trying to switch to vt {}", vt);
|
||||
if let Err(err) = session.change_vt(vt) {
|
||||
error!(log, "Error switching to vt {}: {}", vt, err);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
KeyAction::Run(cmd) => {
|
||||
info!(self.log, "Starting program"; "cmd" => cmd.clone());
|
||||
if let Err(e) = Command::new(&cmd).spawn() {
|
||||
|
|
|
@ -16,8 +16,6 @@ use smithay::wayland_server::{calloop::EventLoop, Display};
|
|||
mod shaders;
|
||||
mod glium_drawer;
|
||||
mod input_handler;
|
||||
#[cfg(feature = "tty_launch")]
|
||||
mod raw_drm;
|
||||
mod shell;
|
||||
mod shm_load;
|
||||
#[cfg(feature = "udev")]
|
||||
|
@ -29,8 +27,6 @@ mod winit;
|
|||
static POSSIBLE_BACKENDS: &'static [&'static str] = &[
|
||||
#[cfg(feature = "winit")]
|
||||
"--winit : Run anvil as a X11 or Wayland client using winit.",
|
||||
#[cfg(feature = "tty_launch")]
|
||||
"--tty-raw : Run anvil as a raw DRM client (requires root).",
|
||||
#[cfg(feature = "udev")]
|
||||
"--tty-udev : Run anvil as a tty udev client (requires root if without logind).",
|
||||
];
|
||||
|
@ -54,13 +50,6 @@ fn main() {
|
|||
crit!(log, "Failed to initialize winit backend.");
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "tty_launch")]
|
||||
Some("--tty-raw") => {
|
||||
info!(log, "Starting anvil on a tty using raw DRM");
|
||||
if let Err(()) = raw_drm::run_raw_drm(display, event_loop, log.clone()) {
|
||||
crit!(log, "Failed to initialize tty backend.");
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "udev")]
|
||||
Some("--tty-udev") => {
|
||||
info!(log, "Starting anvil on a tty using udev");
|
||||
|
|
|
@ -1,169 +0,0 @@
|
|||
use std::{
|
||||
cell::RefCell,
|
||||
fs::{File, OpenOptions},
|
||||
os::unix::io::{AsRawFd, RawFd},
|
||||
rc::Rc,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use smithay::{
|
||||
backend::{
|
||||
drm::{drm_device_bind, DrmBackend, DrmDevice, DrmHandler},
|
||||
graphics::egl::wayland::EGLWaylandExtensions,
|
||||
},
|
||||
drm::{
|
||||
control::{
|
||||
connector::{Info as ConnectorInfo, State as ConnectorState},
|
||||
crtc,
|
||||
encoder::Info as EncoderInfo,
|
||||
Device as ControlDevice, ResourceInfo,
|
||||
},
|
||||
result::Error as DrmError,
|
||||
Device as BasicDevice,
|
||||
},
|
||||
wayland::{compositor::CompositorToken, shm::init_shm_global},
|
||||
wayland_server::{calloop::EventLoop, Display},
|
||||
};
|
||||
|
||||
use glium::Surface;
|
||||
use slog::Logger;
|
||||
|
||||
use glium_drawer::GliumDrawer;
|
||||
use shell::{init_shell, MyWindowMap, Roles, SurfaceData};
|
||||
|
||||
#[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 {}
|
||||
|
||||
pub fn run_raw_drm(mut display: Display, mut event_loop: EventLoop<()>, log: Logger) -> Result<(), ()> {
|
||||
/*
|
||||
* Initialize the drm backend
|
||||
*/
|
||||
// "Find" a suitable drm device
|
||||
let mut options = OpenOptions::new();
|
||||
options.read(true);
|
||||
options.write(true);
|
||||
let mut device =
|
||||
DrmDevice::new(Card(options.clone().open("/dev/dri/card0").unwrap()), log.clone()).unwrap();
|
||||
|
||||
// Get a set of all modesetting resource handles (excluding planes):
|
||||
let res_handles = device.resource_handles().unwrap();
|
||||
|
||||
// Use first connected connector
|
||||
let connector_info = res_handles
|
||||
.connectors()
|
||||
.iter()
|
||||
.map(|conn| ConnectorInfo::load_from_device(&device, *conn).unwrap())
|
||||
.find(|conn| conn.connection_state() == ConnectorState::Connected)
|
||||
.unwrap();
|
||||
|
||||
// Use the first encoder
|
||||
let encoder_info = EncoderInfo::load_from_device(&device, connector_info.encoders()[0]).unwrap();
|
||||
|
||||
// use the connected crtc if any
|
||||
let crtc = encoder_info
|
||||
.current_crtc()
|
||||
// or use the first one that is compatible with the encoder
|
||||
.unwrap_or_else(|| {
|
||||
*res_handles
|
||||
.filter_crtcs(encoder_info.possible_crtcs())
|
||||
.iter()
|
||||
.next()
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
// Assuming we found a good connector and loaded the info into `connector_info`
|
||||
let mode = connector_info.modes()[0]; // Use first mode (usually highest resoltion, but in reality you should filter and sort and check and match with other connectors, if you use more then one.)
|
||||
|
||||
// Initialize the hardware backend
|
||||
let backend = device
|
||||
.create_backend(crtc, mode, vec![connector_info.handle()])
|
||||
.unwrap();
|
||||
let egl_display = Rc::new(RefCell::new(
|
||||
if let Ok(egl_display) = backend.bind_wl_display(&display) {
|
||||
info!(log, "EGL hardware-acceleration enabled");
|
||||
Some(egl_display)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
));
|
||||
let renderer = GliumDrawer::init(backend, egl_display, log.clone());
|
||||
{
|
||||
/*
|
||||
* Initialize Glium
|
||||
*/
|
||||
let mut frame = renderer.draw();
|
||||
frame.clear_color(0.8, 0.8, 0.9, 1.0);
|
||||
frame.finish().unwrap();
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the globals
|
||||
*/
|
||||
|
||||
init_shm_global(&mut display, vec![], log.clone());
|
||||
|
||||
let (compositor_token, _, _, window_map) = init_shell(&mut display, log.clone());
|
||||
|
||||
/*
|
||||
* Add a listening socket:
|
||||
*/
|
||||
let name = display.add_socket_auto().unwrap().into_string().unwrap();
|
||||
println!("Listening on socket: {}", name);
|
||||
|
||||
/*
|
||||
* Register the DrmDevice on the EventLoop
|
||||
*/
|
||||
let _source = drm_device_bind(
|
||||
&event_loop.handle(),
|
||||
device,
|
||||
DrmHandlerImpl {
|
||||
compositor_token,
|
||||
window_map: window_map.clone(),
|
||||
drawer: renderer,
|
||||
logger: log,
|
||||
},
|
||||
).map_err(|(err, _)| err)
|
||||
.unwrap();
|
||||
|
||||
loop {
|
||||
event_loop
|
||||
.dispatch(Some(::std::time::Duration::from_millis(16)), &mut ())
|
||||
.unwrap();
|
||||
display.flush_clients();
|
||||
|
||||
window_map.borrow_mut().refresh();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DrmHandlerImpl {
|
||||
compositor_token: CompositorToken<SurfaceData, Roles>,
|
||||
window_map: Rc<RefCell<MyWindowMap>>,
|
||||
drawer: GliumDrawer<DrmBackend<Card>>,
|
||||
logger: ::slog::Logger,
|
||||
}
|
||||
|
||||
impl DrmHandler<Card> for DrmHandlerImpl {
|
||||
fn ready(
|
||||
&mut self,
|
||||
_device: &mut DrmDevice<Card>,
|
||||
_crtc: crtc::Handle,
|
||||
_frame: u32,
|
||||
_duration: Duration,
|
||||
) {
|
||||
self.drawer
|
||||
.draw_windows(&*self.window_map.borrow(), self.compositor_token, &self.logger);
|
||||
}
|
||||
|
||||
fn error(&mut self, _device: &mut DrmDevice<Card>, error: DrmError) {
|
||||
panic!("{:?}", error);
|
||||
}
|
||||
}
|
|
@ -2,41 +2,42 @@ use std::{
|
|||
cell::RefCell,
|
||||
collections::HashMap,
|
||||
io::Error as IoError,
|
||||
os::unix::io::{AsRawFd, RawFd},
|
||||
path::PathBuf,
|
||||
rc::Rc,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use glium::Surface;
|
||||
use glium::Surface as GliumSurface;
|
||||
use slog::Logger;
|
||||
|
||||
#[cfg(feature = "egl")]
|
||||
use smithay::backend::egl::{EGLDisplay, EGLGraphicsBackend};
|
||||
use smithay::{
|
||||
backend::{
|
||||
drm::{DevPath, DrmBackend, DrmDevice, DrmHandler},
|
||||
graphics::{
|
||||
egl::wayland::{EGLDisplay, EGLWaylandExtensions},
|
||||
GraphicsBackend,
|
||||
drm::{
|
||||
dev_t, device_bind,
|
||||
egl::{EglDevice, EglSurface},
|
||||
gbm::{egl::Gbm as EglGbmBackend, GbmDevice},
|
||||
legacy::LegacyDrmDevice,
|
||||
DevPath, Device, DeviceHandler, Surface,
|
||||
},
|
||||
graphics::CursorBackend,
|
||||
input::InputBackend,
|
||||
libinput::{libinput_bind, LibinputInputBackend, LibinputSessionInterface},
|
||||
session::{
|
||||
auto::{auto_session_bind, AutoSession},
|
||||
Session, SessionNotifier,
|
||||
notify_multiplexer, AsSessionObserver, OFlag, Session, SessionNotifier,
|
||||
},
|
||||
udev::{primary_gpu, udev_backend_bind, SessionFdDrmDevice, UdevBackend, UdevHandler},
|
||||
udev::{primary_gpu, udev_backend_bind, UdevBackend, UdevHandler},
|
||||
},
|
||||
drm::{
|
||||
control::{
|
||||
drm::control::{
|
||||
connector::{Info as ConnectorInfo, State as ConnectorState},
|
||||
crtc,
|
||||
encoder::Info as EncoderInfo,
|
||||
Device as ControlDevice, ResourceInfo,
|
||||
},
|
||||
result::Error as DrmError,
|
||||
},
|
||||
image::{ImageBuffer, Rgba},
|
||||
input::Libinput,
|
||||
|
@ -47,18 +48,38 @@ use smithay::{
|
|||
seat::{Seat, XkbConfig},
|
||||
shm::init_shm_global,
|
||||
},
|
||||
wayland_server::{calloop::EventLoop, protocol::wl_output, Display},
|
||||
wayland_server::{
|
||||
calloop::{
|
||||
generic::{EventedFd, Generic},
|
||||
EventLoop, LoopHandle, Source,
|
||||
},
|
||||
protocol::wl_output,
|
||||
Display,
|
||||
},
|
||||
};
|
||||
|
||||
use glium_drawer::GliumDrawer;
|
||||
use input_handler::AnvilInputHandler;
|
||||
use shell::{init_shell, MyWindowMap, Roles, SurfaceData};
|
||||
|
||||
pub struct SessionFd(RawFd);
|
||||
impl AsRawFd for SessionFd {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
type RenderDevice =
|
||||
EglDevice<EglGbmBackend<LegacyDrmDevice<SessionFd>>, GbmDevice<LegacyDrmDevice<SessionFd>>>;
|
||||
type RenderSurface =
|
||||
EglSurface<EglGbmBackend<LegacyDrmDevice<SessionFd>>, GbmDevice<LegacyDrmDevice<SessionFd>>>;
|
||||
|
||||
pub fn run_udev(mut display: Display, mut event_loop: EventLoop<()>, log: Logger) -> Result<(), ()> {
|
||||
let name = display.add_socket_auto().unwrap().into_string().unwrap();
|
||||
info!(log, "Listening on wayland socket"; "name" => name.clone());
|
||||
::std::env::set_var("WAYLAND_DISPLAY", name);
|
||||
|
||||
#[cfg(feature = "egl")]
|
||||
let active_egl_context = Rc::new(RefCell::new(None));
|
||||
|
||||
let display = Rc::new(RefCell::new(display));
|
||||
|
@ -74,6 +95,8 @@ pub fn run_udev(mut display: Display, mut event_loop: EventLoop<()>, log: Logger
|
|||
* Initialize session
|
||||
*/
|
||||
let (session, mut notifier) = AutoSession::new(log.clone()).ok_or(())?;
|
||||
let (udev_observer, udev_notifier) = notify_multiplexer();
|
||||
let udev_session_id = notifier.register(udev_observer);
|
||||
|
||||
let running = Arc::new(AtomicBool::new(true));
|
||||
|
||||
|
@ -88,26 +111,31 @@ pub fn run_udev(mut display: Display, mut event_loop: EventLoop<()>, log: Logger
|
|||
let primary_gpu = primary_gpu(&context, &seat).unwrap_or_default();
|
||||
|
||||
let bytes = include_bytes!("../resources/cursor2.rgba");
|
||||
let mut udev_backend = UdevBackend::new(
|
||||
event_loop.handle(),
|
||||
let udev_backend = UdevBackend::new(
|
||||
&context,
|
||||
session.clone(),
|
||||
UdevHandlerImpl {
|
||||
compositor_token,
|
||||
#[cfg(feature = "egl")]
|
||||
active_egl_context,
|
||||
session: session.clone(),
|
||||
backends: HashMap::new(),
|
||||
display: display.clone(),
|
||||
primary_gpu,
|
||||
window_map: window_map.clone(),
|
||||
pointer_location: pointer_location.clone(),
|
||||
pointer_image: ImageBuffer::from_raw(64, 64, bytes.to_vec()).unwrap(),
|
||||
loop_handle: event_loop.handle(),
|
||||
notifier: udev_notifier,
|
||||
logger: log.clone(),
|
||||
},
|
||||
seat.clone(),
|
||||
log.clone(),
|
||||
).map_err(|_| ())?;
|
||||
|
||||
let udev_session_id = notifier.register(&mut udev_backend);
|
||||
)
|
||||
.map_err(|_| ())?;
|
||||
|
||||
/*
|
||||
* Initialize wayland clipboard
|
||||
*/
|
||||
init_data_device(
|
||||
&mut display.borrow_mut(),
|
||||
|_| {},
|
||||
|
@ -115,14 +143,21 @@ pub fn run_udev(mut display: Display, mut event_loop: EventLoop<()>, log: Logger
|
|||
log.clone(),
|
||||
);
|
||||
|
||||
/*
|
||||
* Initialize wayland input object
|
||||
*/
|
||||
let (mut w_seat, _) = Seat::new(&mut display.borrow_mut(), session.seat(), log.clone());
|
||||
|
||||
let pointer = w_seat.add_pointer();
|
||||
let keyboard = w_seat
|
||||
.add_keyboard(XkbConfig::default(), 1000, 500, |seat, focus| {
|
||||
set_data_device_focus(seat, focus.and_then(|s| s.client()))
|
||||
}).expect("Failed to initialize the keyboard");
|
||||
})
|
||||
.expect("Failed to initialize the keyboard");
|
||||
|
||||
/*
|
||||
* Initialize a fake output (we render one screen to every device in this example)
|
||||
*/
|
||||
let (output, _output_global) = Output::new(
|
||||
&mut display.borrow_mut(),
|
||||
"Drm".into(),
|
||||
|
@ -157,7 +192,7 @@ pub fn run_udev(mut display: Display, mut event_loop: EventLoop<()>, log: Logger
|
|||
*/
|
||||
let mut libinput_context =
|
||||
Libinput::new_from_udev::<LibinputSessionInterface<AutoSession>>(session.clone().into(), &context);
|
||||
let libinput_session_id = notifier.register(&mut libinput_context);
|
||||
let libinput_session_id = notifier.register(libinput_context.observer());
|
||||
libinput_context.udev_assign_seat(&seat).unwrap();
|
||||
let mut libinput_backend = LibinputInputBackend::new(libinput_context, log.clone());
|
||||
libinput_backend.set_handler(AnvilInputHandler::new_with_session(
|
||||
|
@ -170,15 +205,23 @@ pub fn run_udev(mut display: Display, mut event_loop: EventLoop<()>, log: Logger
|
|||
pointer_location,
|
||||
session,
|
||||
));
|
||||
let libinput_event_source = libinput_bind(libinput_backend, event_loop.handle())
|
||||
.map_err(|(e, _)| e)
|
||||
.unwrap();
|
||||
|
||||
/*
|
||||
* Bind all our objects that get driven by the event loop
|
||||
*/
|
||||
let libinput_event_source = libinput_bind(libinput_backend, event_loop.handle())
|
||||
.map_err(|e| -> IoError { e.into() })
|
||||
.unwrap();
|
||||
let session_event_source = auto_session_bind(notifier, &event_loop.handle())
|
||||
.map_err(|(e, _)| e)
|
||||
.unwrap();
|
||||
let udev_event_source = udev_backend_bind(udev_backend).unwrap();
|
||||
let udev_event_source = udev_backend_bind(udev_backend, &event_loop.handle())
|
||||
.map_err(|e| -> IoError { e.into() })
|
||||
.unwrap();
|
||||
|
||||
/*
|
||||
* And run our loop
|
||||
*/
|
||||
while running.load(Ordering::SeqCst) {
|
||||
if event_loop
|
||||
.dispatch(Some(::std::time::Duration::from_millis(16)), &mut ())
|
||||
|
@ -191,9 +234,12 @@ pub fn run_udev(mut display: Display, mut event_loop: EventLoop<()>, log: Logger
|
|||
}
|
||||
}
|
||||
|
||||
// Cleanup stuff
|
||||
window_map.borrow_mut().clear();
|
||||
|
||||
let mut notifier = session_event_source.unbind();
|
||||
notifier.unregister(udev_session_id);
|
||||
notifier.unregister(libinput_session_id);
|
||||
notifier.unregister(udev_session_id);
|
||||
|
||||
libinput_event_source.remove();
|
||||
udev_event_source.remove();
|
||||
|
@ -201,24 +247,36 @@ pub fn run_udev(mut display: Display, mut event_loop: EventLoop<()>, log: Logger
|
|||
Ok(())
|
||||
}
|
||||
|
||||
struct UdevHandlerImpl {
|
||||
struct UdevHandlerImpl<S: SessionNotifier, Data: 'static> {
|
||||
compositor_token: CompositorToken<SurfaceData, Roles>,
|
||||
#[cfg(feature = "egl")]
|
||||
active_egl_context: Rc<RefCell<Option<EGLDisplay>>>,
|
||||
backends: HashMap<u64, Rc<RefCell<HashMap<crtc::Handle, GliumDrawer<DrmBackend<SessionFdDrmDevice>>>>>>,
|
||||
session: AutoSession,
|
||||
backends: HashMap<
|
||||
dev_t,
|
||||
(
|
||||
S::Id,
|
||||
Source<Generic<EventedFd<RenderDevice>>>,
|
||||
Rc<RefCell<HashMap<crtc::Handle, GliumDrawer<RenderSurface>>>>,
|
||||
),
|
||||
>,
|
||||
display: Rc<RefCell<Display>>,
|
||||
primary_gpu: Option<PathBuf>,
|
||||
window_map: Rc<RefCell<MyWindowMap>>,
|
||||
pointer_location: Rc<RefCell<(f64, f64)>>,
|
||||
pointer_image: ImageBuffer<Rgba<u8>, Vec<u8>>,
|
||||
loop_handle: LoopHandle<Data>,
|
||||
notifier: S,
|
||||
logger: ::slog::Logger,
|
||||
}
|
||||
|
||||
impl UdevHandlerImpl {
|
||||
impl<S: SessionNotifier, Data: 'static> UdevHandlerImpl<S, Data> {
|
||||
#[cfg(feature = "egl")]
|
||||
pub fn scan_connectors(
|
||||
&self,
|
||||
device: &mut DrmDevice<SessionFdDrmDevice>,
|
||||
device: &mut RenderDevice,
|
||||
egl_display: Rc<RefCell<Option<EGLDisplay>>>,
|
||||
) -> HashMap<crtc::Handle, GliumDrawer<DrmBackend<SessionFdDrmDevice>>> {
|
||||
logger: &::slog::Logger,
|
||||
) -> HashMap<crtc::Handle, GliumDrawer<RenderSurface>> {
|
||||
// Get a set of all modesetting resource handles (excluding planes):
|
||||
let res_handles = device.resource_handles().unwrap();
|
||||
|
||||
|
@ -226,9 +284,9 @@ impl UdevHandlerImpl {
|
|||
let connector_infos: Vec<ConnectorInfo> = res_handles
|
||||
.connectors()
|
||||
.iter()
|
||||
.map(|conn| ConnectorInfo::load_from_device(device, *conn).unwrap())
|
||||
.map(|conn| device.resource_info::<ConnectorInfo>(*conn).unwrap())
|
||||
.filter(|conn| conn.connection_state() == ConnectorState::Connected)
|
||||
.inspect(|conn| info!(self.logger, "Connected: {:?}", conn.connector_type()))
|
||||
.inspect(|conn| info!(logger, "Connected: {:?}", conn.connector_type()))
|
||||
.collect();
|
||||
|
||||
let mut backends = HashMap::new();
|
||||
|
@ -238,33 +296,58 @@ impl UdevHandlerImpl {
|
|||
let encoder_infos = connector_info
|
||||
.encoders()
|
||||
.iter()
|
||||
.flat_map(|encoder_handle| EncoderInfo::load_from_device(device, *encoder_handle))
|
||||
.flat_map(|encoder_handle| device.resource_info::<EncoderInfo>(*encoder_handle))
|
||||
.collect::<Vec<EncoderInfo>>();
|
||||
for encoder_info in encoder_infos {
|
||||
for crtc in res_handles.filter_crtcs(encoder_info.possible_crtcs()) {
|
||||
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.)
|
||||
// create a backend
|
||||
let renderer = GliumDrawer::init(
|
||||
device
|
||||
.create_backend(crtc, mode, vec![connector_info.handle()])
|
||||
.unwrap(),
|
||||
device.create_surface(crtc).unwrap(),
|
||||
egl_display.clone(),
|
||||
self.logger.clone(),
|
||||
logger.clone(),
|
||||
);
|
||||
|
||||
// create cursor
|
||||
renderer
|
||||
.borrow()
|
||||
.set_cursor_representation(&self.pointer_image, (2, 2))
|
||||
.unwrap();
|
||||
|
||||
// render first frame
|
||||
{
|
||||
let mut frame = renderer.draw();
|
||||
frame.clear_color(0.8, 0.8, 0.9, 1.0);
|
||||
frame.finish().unwrap();
|
||||
backends.insert(crtc, renderer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
backends
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "egl"))]
|
||||
pub fn scan_connectors(
|
||||
device: &mut RenderDevice,
|
||||
logger: &::slog::Logger,
|
||||
) -> HashMap<crtc::Handle, GliumDrawer<RenderSurface>> {
|
||||
// Get a set of all modesetting resource handles (excluding planes):
|
||||
let res_handles = device.resource_handles().unwrap();
|
||||
|
||||
// Use first connected connector
|
||||
let connector_infos: Vec<ConnectorInfo> = res_handles
|
||||
.connectors()
|
||||
.iter()
|
||||
.map(|conn| device.resource_info::<ConnectorInfo>(*conn).unwrap())
|
||||
.filter(|conn| conn.connection_state() == ConnectorState::Connected)
|
||||
.inspect(|conn| info!(logger, "Connected: {:?}", conn.connector_type()))
|
||||
.collect();
|
||||
|
||||
let mut backends = HashMap::new();
|
||||
|
||||
// very naive way of finding good crtc/encoder/connector combinations. This problem is np-complete
|
||||
for connector_info in connector_infos {
|
||||
let encoder_infos = connector_info
|
||||
.encoders()
|
||||
.iter()
|
||||
.flat_map(|encoder_handle| device.resource_info::<EncoderInfo>(*encoder_handle))
|
||||
.collect::<Vec<EncoderInfo>>();
|
||||
for encoder_info in encoder_infos {
|
||||
for crtc in res_handles.filter_crtcs(encoder_info.possible_crtcs()) {
|
||||
if !backends.contains_key(&crtc) {
|
||||
let renderer =
|
||||
GliumDrawer::init(device.create_surface(crtc).unwrap(), logger.clone());
|
||||
|
||||
backends.insert(crtc, renderer);
|
||||
break;
|
||||
|
@ -277,64 +360,151 @@ impl UdevHandlerImpl {
|
|||
}
|
||||
}
|
||||
|
||||
impl UdevHandler<DrmHandlerImpl> for UdevHandlerImpl {
|
||||
fn device_added(&mut self, device: &mut DrmDevice<SessionFdDrmDevice>) -> Option<DrmHandlerImpl> {
|
||||
impl<S: SessionNotifier, Data: 'static> UdevHandler for UdevHandlerImpl<S, Data> {
|
||||
fn device_added(&mut self, _device: dev_t, path: PathBuf) {
|
||||
// Try to open the device
|
||||
if let Some(mut device) = self
|
||||
.session
|
||||
.open(
|
||||
&path,
|
||||
OFlag::O_RDWR | OFlag::O_CLOEXEC | OFlag::O_NOCTTY | OFlag::O_NONBLOCK,
|
||||
)
|
||||
.ok()
|
||||
.and_then(|fd| LegacyDrmDevice::new(SessionFd(fd), self.logger.clone()).ok())
|
||||
.and_then(|drm| GbmDevice::new(drm, self.logger.clone()).ok())
|
||||
.and_then(|gbm| EglDevice::new(gbm, self.logger.clone()).ok())
|
||||
{
|
||||
// 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.borrow()).ok();
|
||||
#[cfg(feature = "egl")]
|
||||
{
|
||||
if path.canonicalize().ok() == self.primary_gpu {
|
||||
*self.active_egl_context.borrow_mut() =
|
||||
device.bind_wl_display(&*self.display.borrow()).ok();
|
||||
}
|
||||
}
|
||||
|
||||
let backends = Rc::new(RefCell::new(
|
||||
self.scan_connectors(device, self.active_egl_context.clone()),
|
||||
));
|
||||
self.backends.insert(device.device_id(), backends.clone());
|
||||
#[cfg(feature = "egl")]
|
||||
let backends = Rc::new(RefCell::new(UdevHandlerImpl::<S, Data>::scan_connectors(
|
||||
&mut device,
|
||||
self.active_egl_context.clone(),
|
||||
&self.logger,
|
||||
)));
|
||||
|
||||
Some(DrmHandlerImpl {
|
||||
#[cfg(not(feature = "egl"))]
|
||||
let backends = Rc::new(RefCell::new(UdevHandlerImpl::<S, Data>::scan_connectors(
|
||||
&mut device,
|
||||
&self.logger,
|
||||
)));
|
||||
|
||||
// Set the handler.
|
||||
// Note: if you replicate this (very simple) structure, it is rather easy
|
||||
// to introduce reference cycles with Rc. Be sure about your drop order
|
||||
device.set_handler(DrmHandlerImpl {
|
||||
compositor_token: self.compositor_token,
|
||||
backends,
|
||||
backends: backends.clone(),
|
||||
window_map: self.window_map.clone(),
|
||||
pointer_location: self.pointer_location.clone(),
|
||||
logger: self.logger.clone(),
|
||||
})
|
||||
});
|
||||
|
||||
let device_session_id = self.notifier.register(device.observer());
|
||||
let dev_id = device.device_id();
|
||||
let event_source = device_bind(&self.loop_handle, device)
|
||||
.map_err(|e| -> IoError { e.into() })
|
||||
.unwrap();
|
||||
|
||||
for renderer in backends.borrow_mut().values() {
|
||||
// create cursor
|
||||
renderer
|
||||
.borrow()
|
||||
.set_cursor_representation(&self.pointer_image, (2, 2))
|
||||
.unwrap();
|
||||
|
||||
// render first frame
|
||||
{
|
||||
let mut frame = renderer.draw();
|
||||
frame.clear_color(0.8, 0.8, 0.9, 1.0);
|
||||
frame.finish().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn device_changed(&mut self, device: &mut DrmDevice<SessionFdDrmDevice>) {
|
||||
//quick and dirt, just re-init all backends
|
||||
let backends = &self.backends[&device.device_id()];
|
||||
*backends.borrow_mut() = self.scan_connectors(device, self.active_egl_context.clone());
|
||||
self.backends
|
||||
.insert(dev_id, (device_session_id, event_source, backends));
|
||||
}
|
||||
}
|
||||
|
||||
fn device_removed(&mut self, device: &mut DrmDevice<SessionFdDrmDevice>) {
|
||||
fn device_changed(&mut self, device: dev_t) {
|
||||
//quick and dirty, just re-init all backends
|
||||
if let Some((_, ref mut evt_source, ref backends)) = self.backends.get_mut(&device) {
|
||||
let source = evt_source.clone_inner();
|
||||
let mut evented = source.borrow_mut();
|
||||
let mut backends = backends.borrow_mut();
|
||||
#[cfg(feature = "egl")]
|
||||
let new_backends = UdevHandlerImpl::<S, Data>::scan_connectors(
|
||||
&mut (*evented).0,
|
||||
self.active_egl_context.clone(),
|
||||
&self.logger,
|
||||
);
|
||||
#[cfg(not(feature = "egl"))]
|
||||
let new_backends = UdevHandlerImpl::<S, Data>::scan_connectors(&mut (*evented).0, &self.logger);
|
||||
*backends = new_backends;
|
||||
|
||||
for renderer in backends.values() {
|
||||
// create cursor
|
||||
renderer
|
||||
.borrow()
|
||||
.set_cursor_representation(&self.pointer_image, (2, 2))
|
||||
.unwrap();
|
||||
|
||||
// render first frame
|
||||
{
|
||||
let mut frame = renderer.draw();
|
||||
frame.clear_color(0.8, 0.8, 0.9, 1.0);
|
||||
frame.finish().unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn device_removed(&mut self, device: dev_t) {
|
||||
// drop the backends on this side
|
||||
self.backends.remove(&device.device_id());
|
||||
if let Some((id, evt_source, renderers)) = self.backends.remove(&device) {
|
||||
// drop surfaces
|
||||
renderers.borrow_mut().clear();
|
||||
debug!(self.logger, "Surfaces dropped");
|
||||
|
||||
let device = Rc::try_unwrap(evt_source.remove().unwrap())
|
||||
.map_err(|_| "This should not happend")
|
||||
.unwrap()
|
||||
.into_inner()
|
||||
.0;
|
||||
|
||||
// don't use hardware acceleration anymore, if this was the primary gpu
|
||||
#[cfg(feature = "egl")]
|
||||
{
|
||||
if device.dev_path().and_then(|path| path.canonicalize().ok()) == self.primary_gpu {
|
||||
*self.active_egl_context.borrow_mut() = None;
|
||||
}
|
||||
}
|
||||
|
||||
fn error(&mut self, error: IoError) {
|
||||
error!(self.logger, "{:?}", error);
|
||||
self.notifier.unregister(id);
|
||||
debug!(self.logger, "Dropping device");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DrmHandlerImpl {
|
||||
compositor_token: CompositorToken<SurfaceData, Roles>,
|
||||
backends: Rc<RefCell<HashMap<crtc::Handle, GliumDrawer<DrmBackend<SessionFdDrmDevice>>>>>,
|
||||
backends: Rc<RefCell<HashMap<crtc::Handle, GliumDrawer<RenderSurface>>>>,
|
||||
window_map: Rc<RefCell<MyWindowMap>>,
|
||||
pointer_location: Rc<RefCell<(f64, f64)>>,
|
||||
logger: ::slog::Logger,
|
||||
}
|
||||
|
||||
impl DrmHandler<SessionFdDrmDevice> for DrmHandlerImpl {
|
||||
fn ready(
|
||||
&mut self,
|
||||
_device: &mut DrmDevice<SessionFdDrmDevice>,
|
||||
crtc: crtc::Handle,
|
||||
_frame: u32,
|
||||
_duration: Duration,
|
||||
) {
|
||||
impl DeviceHandler for DrmHandlerImpl {
|
||||
type Device = RenderDevice;
|
||||
|
||||
fn vblank(&mut self, crtc: crtc::Handle) {
|
||||
if let Some(drawer) = self.backends.borrow().get(&crtc) {
|
||||
{
|
||||
let (x, y) = *self.pointer_location.borrow();
|
||||
|
@ -343,11 +513,12 @@ impl DrmHandler<SessionFdDrmDevice> for DrmHandlerImpl {
|
|||
.set_cursor_position(x.trunc().abs() as u32, y.trunc().abs() as u32);
|
||||
}
|
||||
|
||||
// and draw in sync with our monitor
|
||||
drawer.draw_windows(&*self.window_map.borrow(), self.compositor_token, &self.logger);
|
||||
}
|
||||
}
|
||||
|
||||
fn error(&mut self, _device: &mut DrmDevice<SessionFdDrmDevice>, error: DrmError) {
|
||||
fn error(&mut self, error: <RenderSurface as Surface>::Error) {
|
||||
error!(self.logger, "{:?}", error);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,11 +5,7 @@ use std::{
|
|||
};
|
||||
|
||||
use smithay::{
|
||||
backend::{
|
||||
graphics::egl::{wayland::EGLWaylandExtensions, EGLGraphicsBackend},
|
||||
input::InputBackend,
|
||||
winit,
|
||||
},
|
||||
backend::{egl::EGLGraphicsBackend, graphics::gl::GLGraphicsBackend, input::InputBackend, winit},
|
||||
wayland::{
|
||||
data_device::{default_action_chooser, init_data_device, set_data_device_focus},
|
||||
output::{Mode, Output, PhysicalProperties},
|
||||
|
@ -63,7 +59,8 @@ pub fn run_winit(display: &mut Display, event_loop: &mut EventLoop<()>, log: Log
|
|||
let keyboard = seat
|
||||
.add_keyboard(XkbConfig::default(), 1000, 500, |seat, focus| {
|
||||
set_data_device_focus(seat, focus.and_then(|s| s.client()))
|
||||
}).expect("Failed to initialize the keyboard");
|
||||
})
|
||||
.expect("Failed to initialize the keyboard");
|
||||
|
||||
let (output, _) = Output::new(
|
||||
display,
|
||||
|
|
16
build.rs
16
build.rs
|
@ -1,13 +1,17 @@
|
|||
#[cfg(any(feature = "backend_egl", feature = "renderer_gl"))]
|
||||
extern crate gl_generator;
|
||||
|
||||
#[cfg(any(feature = "backend_egl", feature = "renderer_gl"))]
|
||||
use gl_generator::{Api, Fallbacks, Profile, Registry};
|
||||
use std::{env, fs::File, path::PathBuf};
|
||||
|
||||
#[cfg(any(feature = "backend_egl", feature = "renderer_gl"))]
|
||||
fn main() {
|
||||
let dest = PathBuf::from(&env::var("OUT_DIR").unwrap());
|
||||
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
|
||||
if env::var_os("CARGO_FEATURE_BACKEND_EGL").is_some() {
|
||||
let mut file = File::create(&dest.join("egl_bindings.rs")).unwrap();
|
||||
Registry::new(
|
||||
Api::Egl,
|
||||
|
@ -29,9 +33,12 @@ fn main() {
|
|||
"EGL_EXT_platform_device",
|
||||
"EGL_KHR_image_base",
|
||||
],
|
||||
).write_bindings(gl_generator::GlobalGenerator, &mut file)
|
||||
)
|
||||
.write_bindings(gl_generator::GlobalGenerator, &mut file)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
if env::var_os("CARGO_FEATURE_RENDERER_GL").is_some() {
|
||||
let mut file = File::create(&dest.join("gl_bindings.rs")).unwrap();
|
||||
Registry::new(
|
||||
Api::Gles2,
|
||||
|
@ -39,6 +46,11 @@ fn main() {
|
|||
Profile::Compatibility,
|
||||
Fallbacks::None,
|
||||
["GL_OES_EGL_image"],
|
||||
).write_bindings(gl_generator::GlobalGenerator, &mut file)
|
||||
)
|
||||
.write_bindings(gl_generator::StructGenerator, &mut file)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(feature = "backend_egl", feature = "renderer_gl")))]
|
||||
fn main() {}
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
extern crate drm;
|
||||
extern crate smithay;
|
||||
#[macro_use]
|
||||
extern crate slog;
|
||||
extern crate slog_term;
|
||||
|
||||
use drm::{buffer::PixelFormat, control::dumbbuffer::DumbBuffer};
|
||||
use slog::Drain;
|
||||
use smithay::{
|
||||
backend::drm::{
|
||||
connector::{self, State as ConnectorState},
|
||||
crtc, device_bind, encoder, framebuffer,
|
||||
legacy::{error::Error, LegacyDrmDevice, LegacyDrmSurface},
|
||||
ControlDevice, Device, DeviceHandler, RawSurface, ResourceInfo, Surface,
|
||||
},
|
||||
wayland_server::calloop::EventLoop,
|
||||
};
|
||||
use std::{
|
||||
fs::{File, OpenOptions},
|
||||
io::Error as IoError,
|
||||
rc::Rc,
|
||||
sync::Mutex,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
let log = slog::Logger::root(Mutex::new(slog_term::term_full().fuse()).fuse(), o!());
|
||||
|
||||
/*
|
||||
* Initialize the drm backend
|
||||
*/
|
||||
|
||||
// "Find" a suitable drm device
|
||||
let mut options = OpenOptions::new();
|
||||
options.read(true);
|
||||
options.write(true);
|
||||
let mut device = LegacyDrmDevice::new(options.open("/dev/dri/card0").unwrap(), log.clone()).unwrap();
|
||||
|
||||
// Get a set of all modesetting resource handles (excluding planes):
|
||||
let res_handles = Device::resource_handles(&device).unwrap();
|
||||
|
||||
// Use first connected connector
|
||||
let connector_info = res_handles
|
||||
.connectors()
|
||||
.iter()
|
||||
.map(|conn| Device::resource_info::<connector::Info>(&device, *conn).unwrap())
|
||||
.find(|conn| conn.connection_state() == ConnectorState::Connected)
|
||||
.unwrap();
|
||||
|
||||
// Use the first encoder
|
||||
let encoder_info = Device::resource_info::<encoder::Info>(&device, connector_info.encoders()[0]).unwrap();
|
||||
|
||||
// use the connected crtc if any
|
||||
let crtc = encoder_info
|
||||
.current_crtc()
|
||||
// or use the first one that is compatible with the encoder
|
||||
.unwrap_or_else(|| {
|
||||
*res_handles
|
||||
.filter_crtcs(encoder_info.possible_crtcs())
|
||||
.iter()
|
||||
.next()
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
// Assuming we found a good connector and loaded the info into `connector_info`
|
||||
let mode = connector_info.modes()[0]; // Use first mode (usually highest resoltion, but in reality you should filter and sort and check and match with other connectors, if you use more then one.)
|
||||
|
||||
// Initialize the hardware backend
|
||||
let surface = Rc::new(device.create_surface(crtc).unwrap());
|
||||
|
||||
surface.use_mode(Some(mode)).unwrap();
|
||||
for conn in surface.current_connectors().into_iter() {
|
||||
if conn != connector_info.handle() {
|
||||
surface.remove_connector(conn).unwrap();
|
||||
}
|
||||
}
|
||||
surface.add_connector(connector_info.handle()).unwrap();
|
||||
|
||||
/*
|
||||
* Lets create buffers and framebuffers.
|
||||
* We use drm-rs DumbBuffers, because they always work and require little to no setup.
|
||||
* But they are very slow, this is just for demonstration purposes.
|
||||
*/
|
||||
let (w, h) = mode.size();
|
||||
let front_buffer =
|
||||
DumbBuffer::create_from_device(&device, (w as u32, h as u32), PixelFormat::XRGB8888).unwrap();
|
||||
let front_framebuffer = device.create_framebuffer(&front_buffer).unwrap();
|
||||
let back_buffer =
|
||||
DumbBuffer::create_from_device(&device, (w as u32, h as u32), PixelFormat::XRGB8888).unwrap();
|
||||
let back_framebuffer = device.create_framebuffer(&back_buffer).unwrap();
|
||||
|
||||
device.set_handler(DrmHandlerImpl {
|
||||
current: front_framebuffer.handle(),
|
||||
front: (front_buffer, front_framebuffer.clone()),
|
||||
back: (back_buffer, back_framebuffer),
|
||||
surface: surface.clone(),
|
||||
});
|
||||
|
||||
/*
|
||||
* Register the DrmDevice on the EventLoop
|
||||
*/
|
||||
let mut event_loop = EventLoop::<()>::new().unwrap();
|
||||
let _source = device_bind(&event_loop.handle(), device)
|
||||
.map_err(|err| -> IoError { err.into() })
|
||||
.unwrap();
|
||||
|
||||
// Start rendering
|
||||
if surface.commit_pending() {
|
||||
surface.commit(front_framebuffer.handle()).unwrap();
|
||||
}
|
||||
surface.page_flip(front_framebuffer.handle()).unwrap();
|
||||
|
||||
// Run
|
||||
event_loop.run(None, &mut (), |_| {}).unwrap();
|
||||
}
|
||||
|
||||
pub struct DrmHandlerImpl {
|
||||
front: (DumbBuffer, framebuffer::Info),
|
||||
back: (DumbBuffer, framebuffer::Info),
|
||||
current: framebuffer::Handle,
|
||||
surface: Rc<LegacyDrmSurface<File>>,
|
||||
}
|
||||
|
||||
impl DeviceHandler for DrmHandlerImpl {
|
||||
type Device = LegacyDrmDevice<File>;
|
||||
|
||||
fn vblank(&mut self, _crtc: crtc::Handle) {
|
||||
{
|
||||
// Swap and map buffer
|
||||
let mut mapping = if self.current == self.front.1.handle() {
|
||||
self.current = self.back.1.handle();
|
||||
self.back.0.map(&*self.surface).unwrap()
|
||||
} else {
|
||||
self.current = self.front.1.handle();
|
||||
self.front.0.map(&*self.surface).unwrap()
|
||||
};
|
||||
|
||||
// now we could render to the mapping via software rendering.
|
||||
// this example just sets some grey color
|
||||
|
||||
for mut x in mapping.as_mut() {
|
||||
*x = 128;
|
||||
}
|
||||
}
|
||||
self.surface.page_flip(self.current).unwrap();
|
||||
}
|
||||
|
||||
fn error(&mut self, error: Error) {
|
||||
panic!("{:?}", error);
|
||||
}
|
||||
}
|
|
@ -1,537 +0,0 @@
|
|||
use super::{error::*, DevPath};
|
||||
use backend::graphics::{
|
||||
egl::{
|
||||
error::Result as EGLResult,
|
||||
native::{Gbm, GbmSurfaceArguments},
|
||||
wayland::{EGLDisplay, EGLWaylandExtensions},
|
||||
EGLContext, EGLGraphicsBackend, EGLSurface, PixelFormat, SwapBuffersError,
|
||||
},
|
||||
GraphicsBackend,
|
||||
};
|
||||
use drm::{
|
||||
control::{connector, crtc, encoder, framebuffer, Device, Mode, ResourceInfo},
|
||||
Device as BasicDevice,
|
||||
};
|
||||
use gbm::{
|
||||
BufferObject, BufferObjectFlags, Device as GbmDevice, Format as GbmFormat, Surface as GbmSurface,
|
||||
SurfaceBufferHandle,
|
||||
};
|
||||
use image::{ImageBuffer, Rgba};
|
||||
use nix::libc::c_void;
|
||||
use std::{
|
||||
cell::Cell,
|
||||
os::unix::io::{AsRawFd, RawFd},
|
||||
rc::{Rc, Weak},
|
||||
};
|
||||
use wayland_server::Display;
|
||||
|
||||
/// Backend based on a `DrmDevice` and a given crtc
|
||||
pub struct DrmBackend<A: Device + 'static> {
|
||||
backend: Rc<DrmBackendInternal<A>>,
|
||||
surface: EGLSurface<GbmSurface<framebuffer::Info>>,
|
||||
mode: Mode,
|
||||
connectors: Vec<connector::Handle>,
|
||||
}
|
||||
|
||||
pub(crate) struct DrmBackendInternal<A: Device + 'static> {
|
||||
pub(crate) context: Rc<EGLContext<Gbm<framebuffer::Info>, GbmDevice<A>>>,
|
||||
pub(crate) cursor: Cell<(BufferObject<()>, (u32, u32))>,
|
||||
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,
|
||||
}
|
||||
|
||||
impl<A: Device + 'static> DrmBackend<A> {
|
||||
pub(crate) fn new(
|
||||
context: Rc<EGLContext<Gbm<framebuffer::Info>, GbmDevice<A>>>,
|
||||
crtc: crtc::Handle,
|
||||
mode: Mode,
|
||||
connectors: Vec<connector::Handle>,
|
||||
log: ::slog::Logger,
|
||||
) -> Result<Self> {
|
||||
// logger already initialized by the DrmDevice
|
||||
info!(log, "Initializing DrmBackend");
|
||||
|
||||
let (w, h) = mode.size();
|
||||
|
||||
debug!(log, "Creating Surface");
|
||||
let surface = context
|
||||
.create_surface(GbmSurfaceArguments {
|
||||
size: (w as u32, h as u32),
|
||||
format: GbmFormat::XRGB8888,
|
||||
flags: BufferObjectFlags::SCANOUT | BufferObjectFlags::RENDERING,
|
||||
}).chain_err(|| ErrorKind::GbmInitFailed)?;
|
||||
|
||||
// make it active for the first `crtc::set`
|
||||
// (which is needed before the first page_flip)
|
||||
unsafe { surface.make_current().chain_err(|| ErrorKind::FailedToSwap)? };
|
||||
surface.swap_buffers().chain_err(|| ErrorKind::FailedToSwap)?;
|
||||
|
||||
// init the first screen
|
||||
// (must be done before calling page_flip for the first time)
|
||||
let mut front_bo = surface
|
||||
.lock_front_buffer()
|
||||
.chain_err(|| ErrorKind::FailedToSwap)?;
|
||||
|
||||
debug!(log, "FrontBuffer color format: {:?}", front_bo.format());
|
||||
|
||||
// we need a framebuffer for the front buffer
|
||||
let fb = framebuffer::create(&*context, &*front_bo).chain_err(|| {
|
||||
ErrorKind::DrmDev(format!("Error creating framebuffer on {:?}", context.dev_path()))
|
||||
})?;
|
||||
|
||||
debug!(log, "Initialize screen");
|
||||
crtc::set(&*context, crtc, fb.handle(), &connectors, (0, 0), Some(mode)).chain_err(|| {
|
||||
ErrorKind::DrmDev(format!(
|
||||
"Error setting crtc {:?} on {:?}",
|
||||
crtc,
|
||||
context.dev_path()
|
||||
))
|
||||
})?;
|
||||
front_bo.set_userdata(fb).unwrap();
|
||||
|
||||
let cursor = Cell::new((
|
||||
context
|
||||
.create_buffer_object(
|
||||
1,
|
||||
1,
|
||||
GbmFormat::ARGB8888,
|
||||
BufferObjectFlags::CURSOR | BufferObjectFlags::WRITE,
|
||||
).chain_err(|| ErrorKind::GbmInitFailed)?,
|
||||
(0, 0),
|
||||
));
|
||||
|
||||
Ok(DrmBackend {
|
||||
backend: Rc::new(DrmBackendInternal {
|
||||
context,
|
||||
cursor,
|
||||
current_frame_buffer: Cell::new(fb),
|
||||
front_buffer: Cell::new(front_bo),
|
||||
next_buffer: Cell::new(None),
|
||||
crtc,
|
||||
logger: log,
|
||||
}),
|
||||
surface,
|
||||
mode,
|
||||
connectors,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn weak(&self) -> Weak<DrmBackendInternal<A>> {
|
||||
Rc::downgrade(&self.backend)
|
||||
}
|
||||
|
||||
/// Add a connector to backend
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Errors if the new connector does not support the currently set `Mode`
|
||||
pub fn add_connector(&mut self, connector: connector::Handle) -> Result<()> {
|
||||
let info = connector::Info::load_from_device(&*self.backend.context, connector).chain_err(|| {
|
||||
ErrorKind::DrmDev(format!(
|
||||
"Error loading connector info on {:?}",
|
||||
self.backend.context.dev_path()
|
||||
))
|
||||
})?;
|
||||
|
||||
// check if the connector can handle the current mode
|
||||
if info.modes().contains(&self.mode) {
|
||||
// check if there is a valid encoder
|
||||
let encoders = info
|
||||
.encoders()
|
||||
.iter()
|
||||
.map(|encoder| {
|
||||
encoder::Info::load_from_device(&*self.backend.context, *encoder).chain_err(|| {
|
||||
ErrorKind::DrmDev(format!(
|
||||
"Error loading encoder info on {:?}",
|
||||
self.backend.context.dev_path()
|
||||
))
|
||||
})
|
||||
}).collect::<Result<Vec<encoder::Info>>>()?;
|
||||
|
||||
// and if any encoder supports the selected crtc
|
||||
let resource_handles = self.backend.context.resource_handles().chain_err(|| {
|
||||
ErrorKind::DrmDev(format!(
|
||||
"Error loading resources on {:?}",
|
||||
self.backend.context.dev_path()
|
||||
))
|
||||
})?;
|
||||
if !encoders
|
||||
.iter()
|
||||
.map(|encoder| encoder.possible_crtcs())
|
||||
.all(|crtc_list| {
|
||||
resource_handles
|
||||
.filter_crtcs(crtc_list)
|
||||
.contains(&self.backend.crtc)
|
||||
}) {
|
||||
bail!(ErrorKind::NoSuitableEncoder(info, self.backend.crtc));
|
||||
}
|
||||
|
||||
info!(
|
||||
self.backend.logger,
|
||||
"Adding new connector: {:?}",
|
||||
info.connector_type()
|
||||
);
|
||||
self.connectors.push(connector);
|
||||
Ok(())
|
||||
} else {
|
||||
bail!(ErrorKind::ModeNotSuitable(self.mode))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the currently set connectors
|
||||
pub fn used_connectors(&self) -> &[connector::Handle] {
|
||||
&*self.connectors
|
||||
}
|
||||
|
||||
/// Removes a currently set connector
|
||||
pub fn remove_connector(&mut self, connector: connector::Handle) {
|
||||
if let Ok(info) = connector::Info::load_from_device(&*self.backend.context, connector) {
|
||||
info!(
|
||||
self.backend.logger,
|
||||
"Removing connector: {:?}",
|
||||
info.connector_type()
|
||||
);
|
||||
} else {
|
||||
info!(self.backend.logger, "Removing unknown connector");
|
||||
}
|
||||
|
||||
self.connectors.retain(|x| *x != connector);
|
||||
}
|
||||
|
||||
/// Gets the currently used mode
|
||||
pub fn current_mode(&self) -> Mode {
|
||||
self.mode
|
||||
}
|
||||
|
||||
/// Changes the currently set mode
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This will fail if not all set connectors support the new `Mode`.
|
||||
/// Several internal resources will need to be recreated to fit the new `Mode`.
|
||||
/// Other errors might occur.
|
||||
pub fn use_mode(&mut self, mode: Mode) -> Result<()> {
|
||||
// check the connectors
|
||||
for connector in &self.connectors {
|
||||
if !connector::Info::load_from_device(&*self.backend.context, *connector)
|
||||
.chain_err(|| {
|
||||
ErrorKind::DrmDev(format!(
|
||||
"Error loading connector info on {:?}",
|
||||
self.backend.context.dev_path()
|
||||
))
|
||||
})?.modes()
|
||||
.contains(&mode)
|
||||
{
|
||||
bail!(ErrorKind::ModeNotSuitable(mode));
|
||||
}
|
||||
}
|
||||
|
||||
info!(self.backend.logger, "Setting new mode: {:?}", mode.name());
|
||||
let (w, h) = mode.size();
|
||||
|
||||
// Recreate the surface and the related resources to match the new
|
||||
// resolution.
|
||||
debug!(
|
||||
self.backend.logger,
|
||||
"Reinitializing surface for new mode: {}:{}", w, h
|
||||
);
|
||||
let surface = self
|
||||
.backend
|
||||
.context
|
||||
.create_surface(GbmSurfaceArguments {
|
||||
size: (w as u32, h as u32),
|
||||
format: GbmFormat::XRGB8888,
|
||||
flags: BufferObjectFlags::SCANOUT | BufferObjectFlags::RENDERING,
|
||||
}).chain_err(|| ErrorKind::GbmInitFailed)?;
|
||||
|
||||
// make it active for the first `crtc::set`
|
||||
// (which is needed before the first page_flip)
|
||||
unsafe { surface.make_current().chain_err(|| ErrorKind::FailedToSwap)? };
|
||||
surface.swap_buffers().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
|
||||
.lock_front_buffer()
|
||||
.chain_err(|| ErrorKind::FailedToSwap)?;
|
||||
|
||||
debug!(
|
||||
self.backend.logger,
|
||||
"FrontBuffer color format: {:?}",
|
||||
front_bo.format()
|
||||
);
|
||||
|
||||
// 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(
|
||||
&*self.backend.context,
|
||||
self.backend.crtc,
|
||||
fb.handle(),
|
||||
&self.connectors,
|
||||
(0, 0),
|
||||
Some(mode),
|
||||
).chain_err(|| {
|
||||
ErrorKind::DrmDev(format!(
|
||||
"Error setting crtc {:?} on {:?}",
|
||||
self.backend.crtc,
|
||||
self.backend.context.dev_path()
|
||||
))
|
||||
})?;
|
||||
|
||||
front_bo
|
||||
});
|
||||
if let Ok(Some(fb)) = old_front_bo.take_userdata() {
|
||||
if let Err(err) = framebuffer::destroy(&*self.backend.context, fb.handle()) {
|
||||
warn!(
|
||||
self.backend.logger,
|
||||
"Error releasing old front_buffer framebuffer: {:?}", err
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Drop the old surface after cleanup
|
||||
self.surface = surface;
|
||||
self.mode = mode;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the crtc id used by this backend
|
||||
pub fn crtc(&self) -> crtc::Handle {
|
||||
self.backend.crtc
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Device + 'static> DrmBackendInternal<A> {
|
||||
pub(crate) fn unlock_buffer(&self) {
|
||||
// after the page swap is finished we need to release the rendered buffer.
|
||||
// this is called from the PageFlipHandler
|
||||
if let Some(next_buffer) = self.next_buffer.replace(None) {
|
||||
trace!(self.logger, "Releasing old front buffer");
|
||||
self.front_buffer.set(next_buffer);
|
||||
// drop and release the old buffer
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn page_flip(
|
||||
&self,
|
||||
fb: Option<&framebuffer::Info>,
|
||||
) -> ::std::result::Result<(), SwapBuffersError> {
|
||||
trace!(self.logger, "Queueing Page flip");
|
||||
|
||||
let fb = *fb.unwrap_or(&self.current_frame_buffer.get());
|
||||
|
||||
// and flip
|
||||
crtc::page_flip(
|
||||
&*self.context,
|
||||
self.crtc,
|
||||
fb.handle(),
|
||||
&[crtc::PageFlipFlags::PageFlipEvent],
|
||||
).map_err(|_| SwapBuffersError::ContextLost)?;
|
||||
|
||||
self.current_frame_buffer.set(fb);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Device + 'static> Drop for DrmBackend<A> {
|
||||
fn drop(&mut self) {
|
||||
// Drop framebuffers attached to the userdata of the gbm surface buffers.
|
||||
// (They don't implement drop, as they need the device)
|
||||
if let Ok(Some(fb)) = {
|
||||
if let Some(mut next) = self.backend.next_buffer.take() {
|
||||
next.take_userdata()
|
||||
} else if let Ok(mut next) = self.surface.lock_front_buffer() {
|
||||
next.take_userdata()
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
} {
|
||||
// ignore failure at this point
|
||||
let _ = framebuffer::destroy(&*self.backend.context, fb.handle());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Device + 'static> Drop for DrmBackendInternal<A> {
|
||||
fn drop(&mut self) {
|
||||
if let Ok(Some(fb)) = self.front_buffer.get_mut().take_userdata() {
|
||||
// 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 Error = Error;
|
||||
|
||||
fn set_cursor_position(&self, x: u32, y: u32) -> Result<()> {
|
||||
trace!(self.backend.logger, "Move the cursor to {},{}", x, y);
|
||||
crtc::move_cursor(&*self.backend.context, self.backend.crtc, (x as i32, y as i32)).chain_err(|| {
|
||||
ErrorKind::DrmDev(format!(
|
||||
"Error moving cursor on {:?}",
|
||||
self.backend.context.dev_path()
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
fn set_cursor_representation(
|
||||
&self,
|
||||
buffer: &ImageBuffer<Rgba<u8>, Vec<u8>>,
|
||||
hotspot: (u32, u32),
|
||||
) -> Result<()> {
|
||||
let (w, h) = buffer.dimensions();
|
||||
debug!(self.backend.logger, "Importing cursor");
|
||||
|
||||
// import the cursor into a buffer we can render
|
||||
let mut cursor = self
|
||||
.backend
|
||||
.context
|
||||
.create_buffer_object(
|
||||
w,
|
||||
h,
|
||||
GbmFormat::ARGB8888,
|
||||
BufferObjectFlags::CURSOR | BufferObjectFlags::WRITE,
|
||||
).chain_err(|| ErrorKind::GbmInitFailed)?;
|
||||
cursor
|
||||
.write(&**buffer)
|
||||
.chain_err(|| ErrorKind::GbmInitFailed)?
|
||||
.chain_err(|| ErrorKind::GbmInitFailed)?;
|
||||
|
||||
trace!(self.backend.logger, "Setting the new imported cursor");
|
||||
|
||||
// and set it
|
||||
if crtc::set_cursor2(
|
||||
&*self.backend.context,
|
||||
self.backend.crtc,
|
||||
&cursor,
|
||||
(hotspot.0 as i32, hotspot.1 as i32),
|
||||
).is_err()
|
||||
{
|
||||
crtc::set_cursor(&*self.backend.context, self.backend.crtc, &cursor).chain_err(|| {
|
||||
ErrorKind::DrmDev(format!(
|
||||
"Failed to set cursor on {:?}",
|
||||
self.backend.context.dev_path()
|
||||
))
|
||||
})?;
|
||||
}
|
||||
|
||||
// and store it
|
||||
self.backend.cursor.set((cursor, hotspot));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Device + 'static> EGLGraphicsBackend for DrmBackend<A> {
|
||||
fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
|
||||
let res = {
|
||||
let nb = self.backend.next_buffer.take();
|
||||
let res = nb.is_some();
|
||||
self.backend.next_buffer.set(nb);
|
||||
res
|
||||
};
|
||||
if res {
|
||||
// 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);
|
||||
}
|
||||
|
||||
// flip normally
|
||||
self.surface.swap_buffers()?;
|
||||
|
||||
// supporting only one buffer would cause a lot of inconvinience and
|
||||
// would most likely result in a lot of flickering.
|
||||
// neither weston, wlc or wlroots bother with that as well.
|
||||
// so we just assume we got at least two buffers to do flipping.
|
||||
let mut next_bo = self
|
||||
.surface
|
||||
.lock_front_buffer()
|
||||
.expect("Surface only has one front buffer. Not supported by smithay");
|
||||
|
||||
// create a framebuffer if the front buffer does not have one already
|
||||
// (they are reused by gbm)
|
||||
let maybe_fb = next_bo
|
||||
.userdata()
|
||||
.map_err(|_| SwapBuffersError::ContextLost)?
|
||||
.cloned();
|
||||
let fb = if let Some(info) = maybe_fb {
|
||||
info
|
||||
} else {
|
||||
let fb = framebuffer::create(&*self.backend.context, &*next_bo)
|
||||
.map_err(|_| SwapBuffersError::ContextLost)?;
|
||||
next_bo.set_userdata(fb).unwrap();
|
||||
fb
|
||||
};
|
||||
self.backend.next_buffer.set(Some(next_bo));
|
||||
|
||||
self.backend.page_flip(Some(&fb))
|
||||
}
|
||||
|
||||
unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void {
|
||||
self.backend.context.get_proc_address(symbol)
|
||||
}
|
||||
|
||||
fn get_framebuffer_dimensions(&self) -> (u32, u32) {
|
||||
let (w, h) = self.mode.size();
|
||||
(w as u32, h as u32)
|
||||
}
|
||||
|
||||
fn is_current(&self) -> bool {
|
||||
self.backend.context.is_current() && self.surface.is_current()
|
||||
}
|
||||
|
||||
unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
|
||||
self.surface.make_current()
|
||||
}
|
||||
|
||||
fn get_pixel_format(&self) -> PixelFormat {
|
||||
self.backend.context.get_pixel_format()
|
||||
}
|
||||
}
|
||||
|
||||
// for users convenience
|
||||
impl<A: Device + 'static> AsRawFd for DrmBackend<A> {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.backend.context.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Device + 'static> BasicDevice for DrmBackend<A> {}
|
||||
impl<A: Device + 'static> Device for DrmBackend<A> {}
|
||||
|
||||
impl<A: Device + 'static> EGLWaylandExtensions for DrmBackend<A> {
|
||||
fn bind_wl_display(&self, display: &Display) -> EGLResult<EGLDisplay> {
|
||||
self.backend.context.bind_wl_display(display)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
//!
|
||||
//! Errors thrown by the `DrmDevice` and `DrmBackend`
|
||||
//!
|
||||
|
||||
use backend::egl::error as egl;
|
||||
|
||||
error_chain! {
|
||||
errors {
|
||||
#[doc = "Underlying backend failed"]
|
||||
UnderlyingBackendError {
|
||||
description("The underlying backend reported an error"),
|
||||
display("The underlying backend reported an error"),
|
||||
}
|
||||
}
|
||||
|
||||
links {
|
||||
EGL(egl::Error, egl::ErrorKind) #[doc = "EGL error"];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,204 @@
|
|||
//!
|
||||
//! [`Device`](../trait.Device.html) and [`Surface`](../trait.Surface.html)
|
||||
//! implementations using egl contexts and surfaces for efficient rendering.
|
||||
//!
|
||||
//! Usually this implementation's [`EglSurface`](struct.EglSurface.html)s implementation
|
||||
//! of [`GlGraphicsBackend`](../../graphics/gl/trait.GlGraphicsBackend.html) will be used
|
||||
//! to let your compositor render.
|
||||
//! Take a look at `anvil`s source code for an example of this.
|
||||
//!
|
||||
|
||||
use drm::control::{crtc, ResourceHandles, ResourceInfo};
|
||||
use nix::libc::dev_t;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::rc::Rc;
|
||||
use wayland_server::Display;
|
||||
|
||||
use super::{Device, DeviceHandler, Surface};
|
||||
use backend::egl::context::GlAttributes;
|
||||
use backend::egl::error::Result as EGLResult;
|
||||
use backend::egl::native::{Backend, NativeDisplay, NativeSurface};
|
||||
use backend::egl::EGLContext;
|
||||
#[cfg(feature = "native_lib")]
|
||||
use backend::egl::{EGLDisplay, EGLGraphicsBackend};
|
||||
|
||||
pub mod error;
|
||||
use self::error::*;
|
||||
|
||||
mod surface;
|
||||
pub use self::surface::*;
|
||||
|
||||
#[cfg(feature = "backend_session")]
|
||||
pub mod session;
|
||||
|
||||
/// Representation of an egl device to create egl rendering surfaces
|
||||
pub struct EglDevice<B, D>
|
||||
where
|
||||
B: Backend<Surface = <D as Device>::Surface> + 'static,
|
||||
D: Device + NativeDisplay<B, Arguments = crtc::Handle> + 'static,
|
||||
<D as Device>::Surface: NativeSurface,
|
||||
{
|
||||
dev: Rc<EGLContext<B, D>>,
|
||||
logger: ::slog::Logger,
|
||||
}
|
||||
|
||||
impl<B, D> AsRawFd for EglDevice<B, D>
|
||||
where
|
||||
B: Backend<Surface = <D as Device>::Surface> + 'static,
|
||||
D: Device + NativeDisplay<B, Arguments = crtc::Handle> + 'static,
|
||||
<D as Device>::Surface: NativeSurface,
|
||||
{
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.dev.borrow().as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, D> EglDevice<B, D>
|
||||
where
|
||||
B: Backend<Surface = <D as Device>::Surface> + 'static,
|
||||
D: Device + NativeDisplay<B, Arguments = crtc::Handle> + 'static,
|
||||
<D as Device>::Surface: NativeSurface,
|
||||
{
|
||||
/// Try to create a new `EglDevice` from an open device.
|
||||
///
|
||||
/// Returns an error if the file is no valid device or context
|
||||
/// creation was not successful.
|
||||
pub fn new<L>(dev: D, logger: L) -> Result<Self>
|
||||
where
|
||||
L: Into<Option<::slog::Logger>>,
|
||||
{
|
||||
EglDevice::new_with_gl_attr(
|
||||
dev,
|
||||
GlAttributes {
|
||||
version: None,
|
||||
profile: None,
|
||||
debug: cfg!(debug_assertions),
|
||||
vsync: true,
|
||||
},
|
||||
logger,
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a new `EglDevice` from an open device and given `GlAttributes`
|
||||
///
|
||||
/// Returns an error if the file is no valid device or context
|
||||
/// creation was not successful.
|
||||
pub fn new_with_gl_attr<L>(mut dev: D, attributes: GlAttributes, logger: L) -> Result<Self>
|
||||
where
|
||||
L: Into<Option<::slog::Logger>>,
|
||||
{
|
||||
let log = ::slog_or_stdlog(logger).new(o!("smithay_module" => "backend_egl"));
|
||||
|
||||
dev.clear_handler();
|
||||
|
||||
debug!(log, "Creating egl context from device");
|
||||
Ok(EglDevice {
|
||||
// Open the gbm device from the drm device and create a context based on that
|
||||
dev: Rc::new(
|
||||
EGLContext::new(dev, attributes, Default::default(), log.clone()).map_err(Error::from)?,
|
||||
),
|
||||
logger: log,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct InternalDeviceHandler<B, D>
|
||||
where
|
||||
B: Backend<Surface = <D as Device>::Surface> + 'static,
|
||||
D: Device + NativeDisplay<B, Arguments = crtc::Handle> + 'static,
|
||||
<D as Device>::Surface: NativeSurface,
|
||||
{
|
||||
handler: Box<DeviceHandler<Device = EglDevice<B, D>> + 'static>,
|
||||
}
|
||||
|
||||
impl<B, D> DeviceHandler for InternalDeviceHandler<B, D>
|
||||
where
|
||||
B: Backend<Surface = <D as Device>::Surface> + 'static,
|
||||
D: Device + NativeDisplay<B, Arguments = crtc::Handle> + 'static,
|
||||
<D as Device>::Surface: NativeSurface,
|
||||
{
|
||||
type Device = D;
|
||||
|
||||
fn vblank(&mut self, crtc: crtc::Handle) {
|
||||
self.handler.vblank(crtc)
|
||||
}
|
||||
fn error(&mut self, error: <<D as Device>::Surface as Surface>::Error) {
|
||||
self.handler
|
||||
.error(ResultExt::<()>::chain_err(Err(error), || ErrorKind::UnderlyingBackendError).unwrap_err())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, D> Device for EglDevice<B, D>
|
||||
where
|
||||
B: Backend<Surface = <D as Device>::Surface> + 'static,
|
||||
D: Device + NativeDisplay<B, Arguments = crtc::Handle> + 'static,
|
||||
<D as Device>::Surface: NativeSurface,
|
||||
{
|
||||
type Surface = EglSurface<B, D>;
|
||||
|
||||
fn device_id(&self) -> dev_t {
|
||||
self.dev.borrow().device_id()
|
||||
}
|
||||
|
||||
fn set_handler(&mut self, handler: impl DeviceHandler<Device = Self> + 'static) {
|
||||
self.dev.borrow_mut().set_handler(InternalDeviceHandler {
|
||||
handler: Box::new(handler),
|
||||
});
|
||||
}
|
||||
|
||||
fn clear_handler(&mut self) {
|
||||
self.dev.borrow_mut().clear_handler()
|
||||
}
|
||||
|
||||
fn create_surface(&mut self, crtc: crtc::Handle) -> Result<EglSurface<B, D>> {
|
||||
info!(self.logger, "Initializing EglSurface");
|
||||
|
||||
let surface = self.dev.create_surface(crtc)?;
|
||||
|
||||
Ok(EglSurface {
|
||||
dev: self.dev.clone(),
|
||||
surface,
|
||||
})
|
||||
}
|
||||
|
||||
fn process_events(&mut self) {
|
||||
self.dev.borrow_mut().process_events()
|
||||
}
|
||||
|
||||
fn resource_info<T: ResourceInfo>(&self, handle: T::Handle) -> Result<T> {
|
||||
self.dev
|
||||
.borrow()
|
||||
.resource_info(handle)
|
||||
.chain_err(|| ErrorKind::UnderlyingBackendError)
|
||||
}
|
||||
|
||||
fn resource_handles(&self) -> Result<ResourceHandles> {
|
||||
self.dev
|
||||
.borrow()
|
||||
.resource_handles()
|
||||
.chain_err(|| ErrorKind::UnderlyingBackendError)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "native_lib")]
|
||||
impl<B, D> EGLGraphicsBackend for EglDevice<B, D>
|
||||
where
|
||||
B: Backend<Surface = <D as Device>::Surface> + 'static,
|
||||
D: Device + NativeDisplay<B, Arguments = crtc::Handle> + 'static,
|
||||
<D as Device>::Surface: NativeSurface,
|
||||
{
|
||||
fn bind_wl_display(&self, display: &Display) -> EGLResult<EGLDisplay> {
|
||||
self.dev.bind_wl_display(display)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, D> Drop for EglDevice<B, D>
|
||||
where
|
||||
B: Backend<Surface = <D as Device>::Surface> + 'static,
|
||||
D: Device + NativeDisplay<B, Arguments = crtc::Handle> + 'static,
|
||||
<D as Device>::Surface: NativeSurface,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
self.clear_handler();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
//!
|
||||
//! Support to register an [`EglDevice`](../struct.EglDevice.html)
|
||||
//! to an open [`Session`](../../session/trait.Session.html).
|
||||
//!
|
||||
|
||||
use drm::control::crtc;
|
||||
use std::os::unix::io::RawFd;
|
||||
|
||||
use super::EglDevice;
|
||||
use backend::drm::Device;
|
||||
use backend::egl::native::{Backend, NativeDisplay, NativeSurface};
|
||||
use backend::session::{AsSessionObserver, SessionObserver};
|
||||
|
||||
/// [`SessionObserver`](../../session/trait.SessionObserver.html)
|
||||
/// linked to the [`EglDevice`](../struct.EglDevice.html) it was
|
||||
/// created from.
|
||||
pub struct EglDeviceObserver<S: SessionObserver + 'static> {
|
||||
observer: S,
|
||||
}
|
||||
|
||||
impl<S, B, D> AsSessionObserver<EglDeviceObserver<S>> for EglDevice<B, D>
|
||||
where
|
||||
S: SessionObserver + 'static,
|
||||
B: Backend<Surface = <D as Device>::Surface> + 'static,
|
||||
D: Device + NativeDisplay<B, Arguments = crtc::Handle> + AsSessionObserver<S> + 'static,
|
||||
<D as Device>::Surface: NativeSurface,
|
||||
{
|
||||
fn observer(&mut self) -> EglDeviceObserver<S> {
|
||||
EglDeviceObserver {
|
||||
observer: self.dev.borrow_mut().observer(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: SessionObserver + 'static> SessionObserver for EglDeviceObserver<S> {
|
||||
fn pause(&mut self, devnum: Option<(u32, u32)>) {
|
||||
self.observer.pause(devnum);
|
||||
}
|
||||
|
||||
fn activate(&mut self, devnum: Option<(u32, u32, Option<RawFd>)>) {
|
||||
self.observer.activate(devnum);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
use drm::control::{connector, crtc, Mode};
|
||||
use nix::libc::c_void;
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::error::*;
|
||||
use backend::drm::{Device, Surface};
|
||||
use backend::egl::native::{Backend, NativeDisplay, NativeSurface};
|
||||
use backend::egl::{EGLContext, EGLSurface};
|
||||
#[cfg(feature = "renderer_gl")]
|
||||
use backend::graphics::gl::GLGraphicsBackend;
|
||||
#[cfg(feature = "renderer_gl")]
|
||||
use backend::graphics::PixelFormat;
|
||||
use backend::graphics::{CursorBackend, SwapBuffersError};
|
||||
|
||||
/// Egl surface for rendering
|
||||
pub struct EglSurface<B, D>
|
||||
where
|
||||
B: Backend<Surface = <D as Device>::Surface> + 'static,
|
||||
D: Device + NativeDisplay<B> + 'static,
|
||||
<D as Device>::Surface: NativeSurface,
|
||||
{
|
||||
pub(super) dev: Rc<EGLContext<B, D>>,
|
||||
pub(super) surface: EGLSurface<B::Surface>,
|
||||
}
|
||||
|
||||
impl<B, D> Surface for EglSurface<B, D>
|
||||
where
|
||||
B: Backend<Surface = <D as Device>::Surface> + 'static,
|
||||
D: Device + NativeDisplay<B> + 'static,
|
||||
<D as Device>::Surface: NativeSurface,
|
||||
{
|
||||
type Error = Error;
|
||||
type Connectors = <<D as Device>::Surface as Surface>::Connectors;
|
||||
|
||||
fn crtc(&self) -> crtc::Handle {
|
||||
(*self.surface).crtc()
|
||||
}
|
||||
|
||||
fn current_connectors(&self) -> Self::Connectors {
|
||||
self.surface.current_connectors()
|
||||
}
|
||||
|
||||
fn pending_connectors(&self) -> Self::Connectors {
|
||||
self.surface.pending_connectors()
|
||||
}
|
||||
|
||||
fn add_connector(&self, connector: connector::Handle) -> Result<()> {
|
||||
self.surface
|
||||
.add_connector(connector)
|
||||
.chain_err(|| ErrorKind::UnderlyingBackendError)
|
||||
}
|
||||
|
||||
fn remove_connector(&self, connector: connector::Handle) -> Result<()> {
|
||||
self.surface
|
||||
.remove_connector(connector)
|
||||
.chain_err(|| ErrorKind::UnderlyingBackendError)
|
||||
}
|
||||
|
||||
fn current_mode(&self) -> Option<Mode> {
|
||||
self.surface.current_mode()
|
||||
}
|
||||
|
||||
fn pending_mode(&self) -> Option<Mode> {
|
||||
self.surface.pending_mode()
|
||||
}
|
||||
|
||||
fn use_mode(&self, mode: Option<Mode>) -> Result<()> {
|
||||
self.surface
|
||||
.use_mode(mode)
|
||||
.chain_err(|| ErrorKind::UnderlyingBackendError)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, B, D> CursorBackend<'a> for EglSurface<B, D>
|
||||
where
|
||||
B: Backend<Surface = <D as Device>::Surface> + 'static,
|
||||
D: Device + NativeDisplay<B> + 'static,
|
||||
<D as Device>::Surface: NativeSurface + CursorBackend<'a>,
|
||||
{
|
||||
type CursorFormat = <D::Surface as CursorBackend<'a>>::CursorFormat;
|
||||
type Error = <D::Surface as CursorBackend<'a>>::Error;
|
||||
|
||||
fn set_cursor_position(&self, x: u32, y: u32) -> ::std::result::Result<(), Self::Error> {
|
||||
self.surface.set_cursor_position(x, y)
|
||||
}
|
||||
|
||||
fn set_cursor_representation<'b>(
|
||||
&'b self,
|
||||
buffer: Self::CursorFormat,
|
||||
hotspot: (u32, u32),
|
||||
) -> ::std::result::Result<(), Self::Error>
|
||||
where
|
||||
'a: 'b,
|
||||
{
|
||||
self.surface.set_cursor_representation(buffer, hotspot)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "renderer_gl")]
|
||||
impl<B, D> GLGraphicsBackend for EglSurface<B, D>
|
||||
where
|
||||
B: Backend<Surface = <D as Device>::Surface> + 'static,
|
||||
D: Device + NativeDisplay<B> + 'static,
|
||||
<D as Device>::Surface: NativeSurface,
|
||||
{
|
||||
fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
|
||||
self.surface.swap_buffers()
|
||||
}
|
||||
|
||||
unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void {
|
||||
self.dev.get_proc_address(symbol)
|
||||
}
|
||||
|
||||
fn get_framebuffer_dimensions(&self) -> (u32, u32) {
|
||||
let (w, h) = self.pending_mode().map(|mode| mode.size()).unwrap_or((1, 1));
|
||||
(w as u32, h as u32)
|
||||
}
|
||||
|
||||
fn is_current(&self) -> bool {
|
||||
self.dev.is_current() && self.surface.is_current()
|
||||
}
|
||||
|
||||
unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
|
||||
self.surface.make_current()
|
||||
}
|
||||
|
||||
fn get_pixel_format(&self) -> PixelFormat {
|
||||
self.dev.get_pixel_format()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
//!
|
||||
//! Egl [`NativeDisplay`](../../egl/native/trait.NativeDisplay.html) and
|
||||
//! [`NativeSurface`](../../egl/native/trait.NativeSurface.html) support for
|
||||
//! [`GbmDevice`](../struct.GbmDevice.html) and [`GbmSurface`](../struct.GbmSurface.html).
|
||||
//!
|
||||
|
||||
use backend::drm::{Device, RawDevice};
|
||||
use backend::egl::error::Result as EglResult;
|
||||
use backend::egl::ffi;
|
||||
use backend::egl::native::{Backend, NativeDisplay, NativeSurface};
|
||||
use backend::graphics::SwapBuffersError;
|
||||
|
||||
use super::error::{Error, Result};
|
||||
use super::{GbmDevice, GbmSurface};
|
||||
|
||||
use drm::control::{crtc, Device as ControlDevice};
|
||||
use gbm::AsRaw;
|
||||
use std::marker::PhantomData;
|
||||
use std::ptr;
|
||||
|
||||
/// Egl Gbm backend type
|
||||
///
|
||||
/// See [`Backend`](../../egl/native/trait.Backend.html).
|
||||
pub struct Gbm<D: RawDevice + 'static> {
|
||||
_userdata: PhantomData<D>,
|
||||
}
|
||||
|
||||
impl<D: RawDevice + 'static> Backend for Gbm<D> {
|
||||
type Surface = GbmSurface<D>;
|
||||
|
||||
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 _)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<D: RawDevice + ControlDevice + 'static> NativeDisplay<Gbm<D>> for GbmDevice<D> {
|
||||
type Arguments = crtc::Handle;
|
||||
type Error = Error;
|
||||
|
||||
fn is_backend(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn ptr(&self) -> EglResult<ffi::NativeDisplayType> {
|
||||
Ok(self.dev.borrow().as_raw() as *const _)
|
||||
}
|
||||
|
||||
fn create_surface(&mut self, crtc: crtc::Handle) -> Result<GbmSurface<D>> {
|
||||
Device::create_surface(self, crtc)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<D: RawDevice + 'static> NativeSurface for GbmSurface<D> {
|
||||
fn ptr(&self) -> ffi::NativeWindowType {
|
||||
self.0.surface.borrow().as_raw() as *const _
|
||||
}
|
||||
|
||||
fn needs_recreation(&self) -> bool {
|
||||
self.needs_recreation()
|
||||
}
|
||||
|
||||
fn recreate(&self) -> bool {
|
||||
if let Err(err) = GbmSurface::recreate(self) {
|
||||
error!(self.0.logger, "Failure recreating internal resources: {:?}", err);
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
|
||||
// this is safe since `eglSwapBuffers` will have been called exactly once
|
||||
// if this is used by our egl module, which is why this trait is unsafe.
|
||||
unsafe { self.page_flip() }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
//!
|
||||
//! Errors thrown by the `DrmDevice` and `DrmBackend`
|
||||
//!
|
||||
|
||||
error_chain! {
|
||||
errors {
|
||||
#[doc = "Creation of gbm device failed"]
|
||||
InitFailed {
|
||||
description("Creation of gbm device failed"),
|
||||
display("Creation of gbm device failed"),
|
||||
}
|
||||
|
||||
#[doc = "Creation of gbm surface failed"]
|
||||
SurfaceCreationFailed {
|
||||
description("Creation of gbm surface failed"),
|
||||
display("Creation of gbm surface failed"),
|
||||
}
|
||||
|
||||
#[doc = "No mode is set, blocking the current operation"]
|
||||
NoModeSet {
|
||||
description("No mode is currently set"),
|
||||
display("No mode is currently set"),
|
||||
}
|
||||
|
||||
#[doc = "Creation of gbm buffer object failed"]
|
||||
BufferCreationFailed {
|
||||
description("Creation of gbm buffer object failed"),
|
||||
display("Creation of gbm buffer object failed"),
|
||||
}
|
||||
|
||||
#[doc = "Writing to gbm buffer failed"]
|
||||
BufferWriteFailed {
|
||||
description("Writing to gbm buffer failed"),
|
||||
display("Writing to gbm buffer failed"),
|
||||
}
|
||||
|
||||
#[doc = "Lock of gbm surface front buffer failed"]
|
||||
FrontBufferLockFailed {
|
||||
description("Lock of gbm surface front buffer failed"),
|
||||
display("Lock of gbm surface front buffer failed"),
|
||||
}
|
||||
|
||||
#[doc = "Underlying backend failed"]
|
||||
UnderlyingBackendError {
|
||||
description("The underlying backend reported an error"),
|
||||
display("The underlying backend reported an error"),
|
||||
}
|
||||
}
|
||||
|
||||
foreign_links {
|
||||
FailedToSwap(::backend::graphics::SwapBuffersError) #[doc = "Swapping front buffers failed"];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,213 @@
|
|||
//!
|
||||
//! [`Device`](../trait.Device.html) and [`Surface`](../trait.Surface.html)
|
||||
//! implementations using gbm buffers for efficient rendering.
|
||||
//!
|
||||
//! Usually this implementation will be wrapped into a [`EglDevice`](../egl/struct.EglDevice.html).
|
||||
//! Take a look at `anvil`s source code for an example of this.
|
||||
//!
|
||||
//! To use these types standalone, you will need to consider the special requirements
|
||||
//! of [`GbmSurface::page_flip`](struct.GbmSurface.html#method.page_flip).
|
||||
//!
|
||||
|
||||
use super::{Device, DeviceHandler, RawDevice, ResourceHandles, ResourceInfo, Surface};
|
||||
|
||||
use drm::control::{crtc, Device as ControlDevice};
|
||||
use gbm::{self, BufferObjectFlags, Format as GbmFormat};
|
||||
use nix::libc::dev_t;
|
||||
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::HashMap;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::rc::{Rc, Weak};
|
||||
use std::sync::{Once, ONCE_INIT};
|
||||
|
||||
pub mod error;
|
||||
use self::error::*;
|
||||
|
||||
mod surface;
|
||||
pub use self::surface::GbmSurface;
|
||||
use self::surface::GbmSurfaceInternal;
|
||||
|
||||
#[cfg(feature = "backend_egl")]
|
||||
pub mod egl;
|
||||
|
||||
#[cfg(feature = "backend_session")]
|
||||
pub mod session;
|
||||
|
||||
static LOAD: Once = ONCE_INIT;
|
||||
|
||||
/// Representation of an open gbm device to create rendering surfaces
|
||||
pub struct GbmDevice<D: RawDevice + ControlDevice + 'static> {
|
||||
pub(self) dev: Rc<RefCell<gbm::Device<D>>>,
|
||||
backends: Rc<RefCell<HashMap<crtc::Handle, Weak<GbmSurfaceInternal<D>>>>>,
|
||||
logger: ::slog::Logger,
|
||||
}
|
||||
|
||||
impl<D: RawDevice + ControlDevice + 'static> GbmDevice<D> {
|
||||
/// Create a new `GbmDevice` from an open drm node
|
||||
///
|
||||
/// Returns an error if the file is no valid drm node or context creation was not
|
||||
/// successful.
|
||||
pub fn new<L>(mut dev: D, logger: L) -> Result<Self>
|
||||
where
|
||||
L: Into<Option<::slog::Logger>>,
|
||||
{
|
||||
/* GBM will load a dri driver, but even though they need symbols from
|
||||
* libglapi, in some version of Mesa they are not linked to it. Since
|
||||
* only the gl-renderer module links to it, these symbols won't be
|
||||
* globally available, and loading the DRI driver fails.
|
||||
* Workaround this by dlopen()'ing libglapi with RTLD_GLOBAL.
|
||||
*/
|
||||
LOAD.call_once(|| unsafe {
|
||||
nix::libc::dlopen(
|
||||
"libglapi.so.0".as_ptr() as *const _,
|
||||
nix::libc::RTLD_LAZY | nix::libc::RTLD_GLOBAL,
|
||||
);
|
||||
});
|
||||
|
||||
let log = ::slog_or_stdlog(logger).new(o!("smithay_module" => "backend_gbm"));
|
||||
|
||||
dev.clear_handler();
|
||||
|
||||
debug!(log, "Creating gbm device");
|
||||
Ok(GbmDevice {
|
||||
// Open the gbm device from the drm device
|
||||
dev: Rc::new(RefCell::new(
|
||||
gbm::Device::new(dev).chain_err(|| ErrorKind::InitFailed)?,
|
||||
)),
|
||||
backends: Rc::new(RefCell::new(HashMap::new())),
|
||||
logger: log,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct InternalDeviceHandler<D: RawDevice + ControlDevice + 'static> {
|
||||
handler: Box<DeviceHandler<Device = GbmDevice<D>> + 'static>,
|
||||
backends: Weak<RefCell<HashMap<crtc::Handle, Weak<GbmSurfaceInternal<D>>>>>,
|
||||
logger: ::slog::Logger,
|
||||
}
|
||||
|
||||
impl<D: RawDevice + ControlDevice + 'static> DeviceHandler for InternalDeviceHandler<D> {
|
||||
type Device = D;
|
||||
|
||||
fn vblank(&mut self, crtc: crtc::Handle) {
|
||||
if let Some(backends) = self.backends.upgrade() {
|
||||
if let Some(surface) = backends.borrow().get(&crtc) {
|
||||
if let Some(surface) = surface.upgrade() {
|
||||
surface.unlock_buffer();
|
||||
self.handler.vblank(crtc);
|
||||
}
|
||||
} else {
|
||||
warn!(
|
||||
self.logger,
|
||||
"Surface ({:?}) not managed by gbm, event not handled.", crtc
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
fn error(&mut self, error: <<D as Device>::Surface as Surface>::Error) {
|
||||
self.handler
|
||||
.error(ResultExt::<()>::chain_err(Err(error), || ErrorKind::UnderlyingBackendError).unwrap_err())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: RawDevice + ControlDevice + 'static> Device for GbmDevice<D> {
|
||||
type Surface = GbmSurface<D>;
|
||||
|
||||
fn device_id(&self) -> dev_t {
|
||||
self.dev.borrow().device_id()
|
||||
}
|
||||
|
||||
fn set_handler(&mut self, handler: impl DeviceHandler<Device = Self> + 'static) {
|
||||
self.dev.borrow_mut().set_handler(InternalDeviceHandler {
|
||||
handler: Box::new(handler),
|
||||
backends: Rc::downgrade(&self.backends),
|
||||
logger: self.logger.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
fn clear_handler(&mut self) {
|
||||
self.dev.borrow_mut().clear_handler();
|
||||
}
|
||||
|
||||
fn create_surface(&mut self, crtc: crtc::Handle) -> Result<GbmSurface<D>> {
|
||||
info!(self.logger, "Initializing GbmSurface");
|
||||
|
||||
let drm_surface = Device::create_surface(&mut **self.dev.borrow_mut(), crtc)
|
||||
.chain_err(|| ErrorKind::UnderlyingBackendError)?;
|
||||
|
||||
// initialize the surface
|
||||
let (w, h) = drm_surface
|
||||
.pending_mode()
|
||||
.map(|mode| mode.size())
|
||||
.unwrap_or((1, 1));
|
||||
let surface = self
|
||||
.dev
|
||||
.borrow()
|
||||
.create_surface(
|
||||
w as u32,
|
||||
h as u32,
|
||||
GbmFormat::XRGB8888,
|
||||
BufferObjectFlags::SCANOUT | BufferObjectFlags::RENDERING,
|
||||
)
|
||||
.chain_err(|| ErrorKind::SurfaceCreationFailed)?;
|
||||
|
||||
// initialize a buffer for the cursor image
|
||||
let cursor = Cell::new((
|
||||
self.dev
|
||||
.borrow()
|
||||
.create_buffer_object(
|
||||
1,
|
||||
1,
|
||||
GbmFormat::ARGB8888,
|
||||
BufferObjectFlags::CURSOR | BufferObjectFlags::WRITE,
|
||||
)
|
||||
.chain_err(|| ErrorKind::BufferCreationFailed)?,
|
||||
(0, 0),
|
||||
));
|
||||
|
||||
let backend = Rc::new(GbmSurfaceInternal {
|
||||
dev: self.dev.clone(),
|
||||
surface: RefCell::new(surface),
|
||||
crtc: drm_surface,
|
||||
cursor,
|
||||
current_frame_buffer: Cell::new(None),
|
||||
front_buffer: Cell::new(None),
|
||||
next_buffer: Cell::new(None),
|
||||
recreated: Cell::new(true),
|
||||
logger: self.logger.new(o!("crtc" => format!("{:?}", crtc))),
|
||||
});
|
||||
self.backends.borrow_mut().insert(crtc, Rc::downgrade(&backend));
|
||||
Ok(GbmSurface(backend))
|
||||
}
|
||||
|
||||
fn process_events(&mut self) {
|
||||
self.dev.borrow_mut().process_events()
|
||||
}
|
||||
|
||||
fn resource_info<T: ResourceInfo>(&self, handle: T::Handle) -> Result<T> {
|
||||
self.dev
|
||||
.borrow()
|
||||
.resource_info(handle)
|
||||
.chain_err(|| ErrorKind::UnderlyingBackendError)
|
||||
}
|
||||
|
||||
fn resource_handles(&self) -> Result<ResourceHandles> {
|
||||
self.dev
|
||||
.borrow()
|
||||
.resource_handles()
|
||||
.chain_err(|| ErrorKind::UnderlyingBackendError)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: RawDevice + ControlDevice + 'static> AsRawFd for GbmDevice<D> {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.dev.borrow().as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: RawDevice + ControlDevice + 'static> Drop for GbmDevice<D> {
|
||||
fn drop(&mut self) {
|
||||
self.clear_handler();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
//!
|
||||
//! Support to register a [`GbmDevice`](../struct.GbmDevice.html)
|
||||
//! to an open [`Session`](../../session/trait.Session.html).
|
||||
//!
|
||||
|
||||
use drm::control::{crtc, Device as ControlDevice, ResourceInfo};
|
||||
use gbm::BufferObject;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::os::unix::io::RawFd;
|
||||
use std::rc::{Rc, Weak};
|
||||
|
||||
use super::{GbmDevice, GbmSurfaceInternal};
|
||||
use backend::drm::{RawDevice, RawSurface};
|
||||
use backend::session::{AsSessionObserver, SessionObserver};
|
||||
|
||||
/// [`SessionObserver`](../../session/trait.SessionObserver.html)
|
||||
/// linked to the [`GbmDevice`](../struct.GbmDevice.html) it was
|
||||
/// created from.
|
||||
pub struct GbmDeviceObserver<
|
||||
S: SessionObserver + 'static,
|
||||
D: RawDevice + ControlDevice + AsSessionObserver<S> + 'static,
|
||||
> {
|
||||
observer: S,
|
||||
backends: Weak<RefCell<HashMap<crtc::Handle, Weak<GbmSurfaceInternal<D>>>>>,
|
||||
logger: ::slog::Logger,
|
||||
}
|
||||
|
||||
impl<S: SessionObserver + 'static, D: RawDevice + ControlDevice + AsSessionObserver<S> + 'static>
|
||||
AsSessionObserver<GbmDeviceObserver<S, D>> for GbmDevice<D>
|
||||
{
|
||||
fn observer(&mut self) -> GbmDeviceObserver<S, D> {
|
||||
GbmDeviceObserver {
|
||||
observer: (**self.dev.borrow_mut()).observer(),
|
||||
backends: Rc::downgrade(&self.backends),
|
||||
logger: self.logger.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: SessionObserver + 'static, D: RawDevice + ControlDevice + AsSessionObserver<S> + 'static>
|
||||
SessionObserver for GbmDeviceObserver<S, D>
|
||||
{
|
||||
fn pause(&mut self, devnum: Option<(u32, u32)>) {
|
||||
self.observer.pause(devnum);
|
||||
}
|
||||
|
||||
fn activate(&mut self, devnum: Option<(u32, u32, Option<RawFd>)>) {
|
||||
self.observer.activate(devnum);
|
||||
let mut crtcs = Vec::new();
|
||||
if let Some(backends) = self.backends.upgrade() {
|
||||
for (crtc, backend) in backends.borrow().iter() {
|
||||
if let Some(backend) = backend.upgrade() {
|
||||
// restart rendering loop, if it was previously running
|
||||
if let Some(Err(err)) = backend
|
||||
.current_frame_buffer
|
||||
.get()
|
||||
.map(|fb| backend.crtc.page_flip(fb.handle()))
|
||||
{
|
||||
warn!(self.logger, "Failed to restart rendering loop. Error: {}", err);
|
||||
}
|
||||
// reset cursor
|
||||
{
|
||||
let &(ref cursor, ref hotspot): &(BufferObject<()>, (u32, u32)) =
|
||||
unsafe { &*backend.cursor.as_ptr() };
|
||||
if crtc::set_cursor2(
|
||||
&*backend.dev.borrow(),
|
||||
*crtc,
|
||||
cursor,
|
||||
((*hotspot).0 as i32, (*hotspot).1 as i32),
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
if let Err(err) = crtc::set_cursor(&*backend.dev.borrow(), *crtc, cursor) {
|
||||
error!(self.logger, "Failed to reset cursor. Error: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
crtcs.push(*crtc);
|
||||
}
|
||||
}
|
||||
for crtc in crtcs {
|
||||
backends.borrow_mut().remove(&crtc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,373 @@
|
|||
use super::super::{Device, RawDevice, RawSurface, Surface};
|
||||
use super::error::*;
|
||||
|
||||
use drm::control::{connector, crtc, framebuffer, Mode, ResourceInfo};
|
||||
use gbm::{self, BufferObject, BufferObjectFlags, Format as GbmFormat, SurfaceBufferHandle};
|
||||
use image::{ImageBuffer, Rgba};
|
||||
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[cfg(feature = "backend_drm_legacy")]
|
||||
use backend::drm::legacy::LegacyDrmDevice;
|
||||
use backend::graphics::CursorBackend;
|
||||
use backend::graphics::SwapBuffersError;
|
||||
|
||||
pub(super) struct GbmSurfaceInternal<D: RawDevice + 'static> {
|
||||
pub(super) dev: Rc<RefCell<gbm::Device<D>>>,
|
||||
pub(super) surface: RefCell<gbm::Surface<framebuffer::Info>>,
|
||||
pub(super) crtc: <D as Device>::Surface,
|
||||
pub(super) cursor: Cell<(BufferObject<()>, (u32, u32))>,
|
||||
pub(super) current_frame_buffer: Cell<Option<framebuffer::Info>>,
|
||||
pub(super) front_buffer: Cell<Option<SurfaceBufferHandle<framebuffer::Info>>>,
|
||||
pub(super) next_buffer: Cell<Option<SurfaceBufferHandle<framebuffer::Info>>>,
|
||||
pub(super) recreated: Cell<bool>,
|
||||
pub(super) logger: ::slog::Logger,
|
||||
}
|
||||
|
||||
impl<D: RawDevice + 'static> GbmSurfaceInternal<D> {
|
||||
pub(super) fn unlock_buffer(&self) {
|
||||
// after the page swap is finished we need to release the rendered buffer.
|
||||
// this is called from the PageFlipHandler
|
||||
trace!(self.logger, "Releasing old front buffer");
|
||||
self.front_buffer.set(self.next_buffer.replace(None));
|
||||
// drop and release the old buffer
|
||||
}
|
||||
|
||||
pub unsafe fn page_flip(&self) -> ::std::result::Result<(), SwapBuffersError> {
|
||||
let res = {
|
||||
let nb = self.next_buffer.take();
|
||||
let res = nb.is_some();
|
||||
self.next_buffer.set(nb);
|
||||
res
|
||||
};
|
||||
if res {
|
||||
// We cannot call lock_front_buffer anymore without releasing the previous buffer, which will happen when the page flip is done
|
||||
warn!(self.logger, "Tried to swap with an already queued flip");
|
||||
return Err(SwapBuffersError::AlreadySwapped);
|
||||
}
|
||||
|
||||
// supporting only one buffer would cause a lot of inconvinience and
|
||||
// would most likely result in a lot of flickering.
|
||||
// neither weston, wlc or wlroots bother with that as well.
|
||||
// so we just assume we got at least two buffers to do flipping.
|
||||
let mut next_bo = self
|
||||
.surface
|
||||
.borrow()
|
||||
.lock_front_buffer()
|
||||
.expect("Surface only has one front buffer. Not supported by smithay");
|
||||
|
||||
// create a framebuffer if the front buffer does not have one already
|
||||
// (they are reused by gbm)
|
||||
let maybe_fb = next_bo
|
||||
.userdata()
|
||||
.map_err(|_| SwapBuffersError::ContextLost)?
|
||||
.cloned();
|
||||
let fb = if let Some(info) = maybe_fb {
|
||||
info
|
||||
} else {
|
||||
let fb = framebuffer::create(&self.crtc, &*next_bo).map_err(|_| SwapBuffersError::ContextLost)?;
|
||||
next_bo.set_userdata(fb).unwrap();
|
||||
fb
|
||||
};
|
||||
self.next_buffer.set(Some(next_bo));
|
||||
|
||||
if self.recreated.get() {
|
||||
debug!(self.logger, "Commiting new state");
|
||||
self.crtc
|
||||
.commit(fb.handle())
|
||||
.map_err(|_| SwapBuffersError::ContextLost)?;
|
||||
self.recreated.set(false);
|
||||
}
|
||||
|
||||
trace!(self.logger, "Queueing Page flip");
|
||||
self.crtc.page_flip(fb.handle())?;
|
||||
|
||||
self.current_frame_buffer.set(Some(fb));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn recreate(&self) -> Result<()> {
|
||||
let (w, h) = self.pending_mode().chain_err(|| ErrorKind::NoModeSet)?.size();
|
||||
|
||||
// Recreate the surface and the related resources to match the new
|
||||
// resolution.
|
||||
debug!(self.logger, "(Re-)Initializing surface for mode: {}:{}", w, h);
|
||||
let surface = self
|
||||
.dev
|
||||
.borrow_mut()
|
||||
.create_surface(
|
||||
w as u32,
|
||||
h as u32,
|
||||
GbmFormat::XRGB8888,
|
||||
BufferObjectFlags::SCANOUT | BufferObjectFlags::RENDERING,
|
||||
)
|
||||
.chain_err(|| ErrorKind::SurfaceCreationFailed)?;
|
||||
|
||||
// Clean up buffers
|
||||
if let Some(Ok(Some(fb))) = self.next_buffer.take().map(|mut bo| bo.take_userdata()) {
|
||||
if let Err(err) = framebuffer::destroy(&self.crtc, fb.handle()) {
|
||||
warn!(
|
||||
self.logger,
|
||||
"Error releasing old back_buffer framebuffer: {:?}", err
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(Ok(Some(fb))) = self.front_buffer.take().map(|mut bo| bo.take_userdata()) {
|
||||
if let Err(err) = framebuffer::destroy(&self.crtc, fb.handle()) {
|
||||
warn!(
|
||||
self.logger,
|
||||
"Error releasing old front_buffer framebuffer: {:?}", err
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Drop the old surface after cleanup
|
||||
*self.surface.borrow_mut() = surface;
|
||||
|
||||
self.recreated.set(true);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: RawDevice + 'static> Surface for GbmSurfaceInternal<D> {
|
||||
type Connectors = <<D as Device>::Surface as Surface>::Connectors;
|
||||
type Error = Error;
|
||||
|
||||
fn crtc(&self) -> crtc::Handle {
|
||||
self.crtc.crtc()
|
||||
}
|
||||
|
||||
fn current_connectors(&self) -> Self::Connectors {
|
||||
self.crtc.current_connectors()
|
||||
}
|
||||
|
||||
fn pending_connectors(&self) -> Self::Connectors {
|
||||
self.crtc.pending_connectors()
|
||||
}
|
||||
|
||||
fn add_connector(&self, connector: connector::Handle) -> Result<()> {
|
||||
self.crtc
|
||||
.add_connector(connector)
|
||||
.chain_err(|| ErrorKind::UnderlyingBackendError)
|
||||
}
|
||||
|
||||
fn remove_connector(&self, connector: connector::Handle) -> Result<()> {
|
||||
self.crtc
|
||||
.remove_connector(connector)
|
||||
.chain_err(|| ErrorKind::UnderlyingBackendError)
|
||||
}
|
||||
|
||||
fn current_mode(&self) -> Option<Mode> {
|
||||
self.crtc.current_mode()
|
||||
}
|
||||
|
||||
fn pending_mode(&self) -> Option<Mode> {
|
||||
self.crtc.pending_mode()
|
||||
}
|
||||
|
||||
fn use_mode(&self, mode: Option<Mode>) -> Result<()> {
|
||||
self.crtc
|
||||
.use_mode(mode)
|
||||
.chain_err(|| ErrorKind::UnderlyingBackendError)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME:
|
||||
//
|
||||
// Option 1: When there is GAT support, impl `GraphicsBackend` for `LegacyDrmBackend`
|
||||
// using a new generic `B: Buffer` and use this:
|
||||
/*
|
||||
impl<'a, D: RawDevice + 'static> CursorBackend<'a> for GbmSurfaceInternal<D>
|
||||
where
|
||||
<D as RawDevice>::Surface: CursorBackend<'a>,
|
||||
<<D as RawDevice>::Surface as CursorBackend<'a>>::CursorFormat: Buffer,
|
||||
<<D as RawDevice>::Surface as CursorBackend<'a>>::Error: ::std::error::Error + Send
|
||||
{
|
||||
*/
|
||||
//
|
||||
// Option 2: When equality checks in where clauses are supported, we could at least do this:
|
||||
/*
|
||||
impl<'a, D: RawDevice + 'static> GraphicsBackend<'a> for GbmSurfaceInternal<D>
|
||||
where
|
||||
<D as RawDevice>::Surface: CursorBackend<'a>,
|
||||
<<D as RawDevice>::Surface as CursorBackend<'a>>::CursorFormat=&'a Buffer,
|
||||
<<D as RawDevice>::Surface as CursorBackend<'a>>::Error: ::std::error::Error + Send
|
||||
{
|
||||
*/
|
||||
// But for now got to do this:
|
||||
|
||||
#[cfg(feature = "backend_drm_legacy")]
|
||||
impl<'a, A: AsRawFd + 'static> CursorBackend<'a> for GbmSurfaceInternal<LegacyDrmDevice<A>> {
|
||||
type CursorFormat = &'a ImageBuffer<Rgba<u8>, Vec<u8>>;
|
||||
type Error = Error;
|
||||
|
||||
fn set_cursor_position(&self, x: u32, y: u32) -> Result<()> {
|
||||
ResultExt::chain_err(self.crtc.set_cursor_position(x, y), || {
|
||||
ErrorKind::UnderlyingBackendError
|
||||
})
|
||||
}
|
||||
|
||||
fn set_cursor_representation<'b>(
|
||||
&'b self,
|
||||
buffer: &ImageBuffer<Rgba<u8>, Vec<u8>>,
|
||||
hotspot: (u32, u32),
|
||||
) -> Result<()>
|
||||
where
|
||||
'a: 'b,
|
||||
{
|
||||
let (w, h) = buffer.dimensions();
|
||||
debug!(self.logger, "Importing cursor");
|
||||
|
||||
// import the cursor into a buffer we can render
|
||||
let mut cursor = self
|
||||
.dev
|
||||
.borrow_mut()
|
||||
.create_buffer_object(
|
||||
w,
|
||||
h,
|
||||
GbmFormat::ARGB8888,
|
||||
BufferObjectFlags::CURSOR | BufferObjectFlags::WRITE,
|
||||
)
|
||||
.chain_err(|| ErrorKind::BufferCreationFailed)?;
|
||||
|
||||
cursor
|
||||
.write(&**buffer)
|
||||
.chain_err(|| ErrorKind::BufferWriteFailed)?
|
||||
.chain_err(|| ErrorKind::BufferWriteFailed)?;
|
||||
|
||||
trace!(self.logger, "Setting the new imported cursor");
|
||||
|
||||
ResultExt::chain_err(self.crtc.set_cursor_representation(&cursor, hotspot), || {
|
||||
ErrorKind::UnderlyingBackendError
|
||||
})?;
|
||||
|
||||
// and store it
|
||||
self.cursor.set((cursor, hotspot));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: RawDevice + 'static> Drop for GbmSurfaceInternal<D> {
|
||||
fn drop(&mut self) {
|
||||
// Drop framebuffers attached to the userdata of the gbm surface buffers.
|
||||
// (They don't implement drop, as they need the device)
|
||||
if let Ok(Some(fb)) = {
|
||||
if let Some(mut next) = self.next_buffer.take() {
|
||||
next.take_userdata()
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
} {
|
||||
// ignore failure at this point
|
||||
let _ = framebuffer::destroy(&self.crtc, fb.handle());
|
||||
}
|
||||
|
||||
if let Ok(Some(fb)) = {
|
||||
if let Some(mut next) = self.front_buffer.take() {
|
||||
next.take_userdata()
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
} {
|
||||
// ignore failure at this point
|
||||
let _ = framebuffer::destroy(&self.crtc, fb.handle());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Gbm surface for rendering
|
||||
pub struct GbmSurface<D: RawDevice + 'static>(pub(super) Rc<GbmSurfaceInternal<D>>);
|
||||
|
||||
impl<D: RawDevice + 'static> GbmSurface<D> {
|
||||
/// Flips the underlying buffers.
|
||||
///
|
||||
/// The surface will report being already flipped until the matching event
|
||||
/// was processed either by calling `GbmDevice::process_events` manually after the flip
|
||||
/// (bad idea performance-wise) or by binding the device to an event-loop by using
|
||||
/// `device_bind`.
|
||||
///
|
||||
/// *Note*: This might trigger a full modeset on the underlying device,
|
||||
/// potentially causing some flickering. In that case this operation is
|
||||
/// blocking until the crtc is in the desired state.
|
||||
pub unsafe fn page_flip(&self) -> ::std::result::Result<(), SwapBuffersError> {
|
||||
self.0.page_flip()
|
||||
}
|
||||
|
||||
/// Recreate underlying gbm resources.
|
||||
///
|
||||
/// This recreates the gbm surfaces resources, which might be needed after e.g.
|
||||
/// calling [`Surface::use_mode`](../trait.Surface.html#method.use_mode).
|
||||
/// You may check if your `GbmSurface` needs recreation through
|
||||
/// [`needs_recreation`](#method.needs_recreation).
|
||||
pub fn recreate(&self) -> Result<()> {
|
||||
self.0.recreate()
|
||||
}
|
||||
|
||||
/// Check if underlying gbm resources need to be recreated.
|
||||
pub fn needs_recreation(&self) -> bool {
|
||||
self.0.crtc.commit_pending()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: RawDevice + 'static> Surface for GbmSurface<D> {
|
||||
type Connectors = <<D as Device>::Surface as Surface>::Connectors;
|
||||
type Error = Error;
|
||||
|
||||
fn crtc(&self) -> crtc::Handle {
|
||||
self.0.crtc()
|
||||
}
|
||||
|
||||
fn current_connectors(&self) -> Self::Connectors {
|
||||
self.0.current_connectors()
|
||||
}
|
||||
|
||||
fn pending_connectors(&self) -> Self::Connectors {
|
||||
self.0.pending_connectors()
|
||||
}
|
||||
|
||||
fn add_connector(&self, connector: connector::Handle) -> Result<()> {
|
||||
self.0.add_connector(connector)
|
||||
}
|
||||
|
||||
fn remove_connector(&self, connector: connector::Handle) -> Result<()> {
|
||||
self.0.remove_connector(connector)
|
||||
}
|
||||
|
||||
fn current_mode(&self) -> Option<Mode> {
|
||||
self.0.current_mode()
|
||||
}
|
||||
|
||||
fn pending_mode(&self) -> Option<Mode> {
|
||||
self.0.pending_mode()
|
||||
}
|
||||
|
||||
fn use_mode(&self, mode: Option<Mode>) -> Result<()> {
|
||||
self.0.use_mode(mode)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "backend_drm_legacy")]
|
||||
impl<'a, A: AsRawFd + 'static> CursorBackend<'a> for GbmSurface<LegacyDrmDevice<A>> {
|
||||
type CursorFormat = &'a ImageBuffer<Rgba<u8>, Vec<u8>>;
|
||||
type Error = Error;
|
||||
|
||||
fn set_cursor_position(&self, x: u32, y: u32) -> Result<()> {
|
||||
self.0.set_cursor_position(x, y)
|
||||
}
|
||||
|
||||
fn set_cursor_representation<'b>(
|
||||
&'b self,
|
||||
buffer: &ImageBuffer<Rgba<u8>, Vec<u8>>,
|
||||
hotspot: (u32, u32),
|
||||
) -> Result<()>
|
||||
where
|
||||
'a: 'b,
|
||||
{
|
||||
self.0.set_cursor_representation(buffer, hotspot)
|
||||
}
|
||||
}
|
|
@ -1,8 +1,7 @@
|
|||
//!
|
||||
//! Errors thrown by the `DrmDevice` and `DrmBackend`
|
||||
//! Errors thrown by the `LegacyDrmDevice` and `LegacyDrmSurface`
|
||||
//!
|
||||
|
||||
use backend::graphics::egl::error as egl;
|
||||
use drm::control::{connector, crtc, Mode};
|
||||
|
||||
error_chain! {
|
||||
|
@ -23,18 +22,6 @@ error_chain! {
|
|||
description("Unable to determine device id of drm device"),
|
||||
}
|
||||
|
||||
#[doc = "Creation of gbm resource failed"]
|
||||
GbmInitFailed {
|
||||
description("Creation of gbm resource failed"),
|
||||
display("Creation of gbm resource failed"),
|
||||
}
|
||||
|
||||
#[doc = "Swapping front buffers failed"]
|
||||
FailedToSwap {
|
||||
description("Swapping front buffers failed"),
|
||||
display("Swapping front buffers failed"),
|
||||
}
|
||||
|
||||
#[doc = "Device is currently paused"]
|
||||
DeviceInactive {
|
||||
description("Device is currently paused, operation rejected"),
|
||||
|
@ -60,7 +47,7 @@ error_chain! {
|
|||
}
|
||||
}
|
||||
|
||||
links {
|
||||
EGL(egl::Error, egl::ErrorKind) #[doc = "EGL error"];
|
||||
foreign_links {
|
||||
FailedToSwap(::backend::graphics::SwapBuffersError) #[doc = "Swapping front buffers failed"];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,286 @@
|
|||
//!
|
||||
//! [`RawDevice`](../trait.RawDevice.html) and [`RawSurface`](../trait.RawSurface.html)
|
||||
//! implementations using the legacy mode-setting infrastructure.
|
||||
//!
|
||||
//! Usually this implementation will be wrapped into a [`GbmDevice`](../gbm/struct.GbmDevice.html).
|
||||
//! Take a look at `anvil`s source code for an example of this.
|
||||
//!
|
||||
//! For an example how to use this standalone, take a look at the `raw_drm` example.
|
||||
//!
|
||||
|
||||
use super::{DevPath, Device, DeviceHandler, RawDevice};
|
||||
|
||||
use drm::control::{connector, crtc, encoder, Device as ControlDevice, ResourceHandles, ResourceInfo};
|
||||
use drm::Device as BasicDevice;
|
||||
use nix::libc::dev_t;
|
||||
use nix::sys::stat::fstat;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::rc::{Rc, Weak};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
mod surface;
|
||||
pub use self::surface::LegacyDrmSurface;
|
||||
use self::surface::{LegacyDrmSurfaceInternal, State};
|
||||
|
||||
pub mod error;
|
||||
use self::error::*;
|
||||
|
||||
#[cfg(feature = "backend_session")]
|
||||
pub mod session;
|
||||
|
||||
/// Open raw drm device utilizing legacy mode-setting
|
||||
pub struct LegacyDrmDevice<A: AsRawFd + 'static> {
|
||||
dev: Rc<Dev<A>>,
|
||||
dev_id: dev_t,
|
||||
active: Arc<AtomicBool>,
|
||||
backends: Rc<RefCell<HashMap<crtc::Handle, Weak<LegacyDrmSurfaceInternal<A>>>>>,
|
||||
handler: Option<RefCell<Box<DeviceHandler<Device = LegacyDrmDevice<A>>>>>,
|
||||
logger: ::slog::Logger,
|
||||
}
|
||||
|
||||
pub(in crate::backend::drm) struct Dev<A: AsRawFd + 'static> {
|
||||
fd: A,
|
||||
priviledged: bool,
|
||||
active: Arc<AtomicBool>,
|
||||
old_state: HashMap<crtc::Handle, (crtc::Info, Vec<connector::Handle>)>,
|
||||
logger: ::slog::Logger,
|
||||
}
|
||||
impl<A: AsRawFd + 'static> AsRawFd for Dev<A> {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.fd.as_raw_fd()
|
||||
}
|
||||
}
|
||||
impl<A: AsRawFd + 'static> BasicDevice for Dev<A> {}
|
||||
impl<A: AsRawFd + 'static> ControlDevice for Dev<A> {}
|
||||
impl<A: AsRawFd + 'static> Drop for Dev<A> {
|
||||
fn drop(&mut self) {
|
||||
info!(self.logger, "Dropping device: {:?}", self.dev_path());
|
||||
if self.active.load(Ordering::SeqCst) {
|
||||
// Here we restore the tty to it's previous state.
|
||||
// In case e.g. getty was running on the tty sets the correct framebuffer again,
|
||||
// so that getty will be visible.
|
||||
let old_state = self.old_state.clone();
|
||||
for (handle, (info, connectors)) in old_state {
|
||||
if let Err(err) = crtc::set(
|
||||
&*self,
|
||||
handle,
|
||||
info.fb(),
|
||||
&connectors,
|
||||
info.position(),
|
||||
info.mode(),
|
||||
) {
|
||||
error!(self.logger, "Failed to reset crtc ({:?}). Error: {}", handle, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
if self.priviledged {
|
||||
if let Err(err) = self.drop_master() {
|
||||
error!(self.logger, "Failed to drop drm master state. Error: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: AsRawFd + 'static> LegacyDrmDevice<A> {
|
||||
/// Create a new `LegacyDrmDevice` from an open drm node
|
||||
///
|
||||
/// Returns an error if the file is no valid drm node or context creation was not
|
||||
/// successful.
|
||||
pub fn new<L>(dev: A, logger: L) -> Result<Self>
|
||||
where
|
||||
L: Into<Option<::slog::Logger>>,
|
||||
{
|
||||
let log = ::slog_or_stdlog(logger).new(o!("smithay_module" => "backend_drm"));
|
||||
info!(log, "DrmDevice initializing");
|
||||
|
||||
let dev_id = fstat(dev.as_raw_fd())
|
||||
.chain_err(|| ErrorKind::UnableToGetDeviceId)?
|
||||
.st_rdev;
|
||||
|
||||
let active = Arc::new(AtomicBool::new(true));
|
||||
let mut dev = Dev {
|
||||
fd: dev,
|
||||
priviledged: true,
|
||||
old_state: HashMap::new(),
|
||||
active: active.clone(),
|
||||
logger: log.clone(),
|
||||
};
|
||||
|
||||
// we want to modeset, so we better be the master, if we run via a tty session
|
||||
if dev.set_master().is_err() {
|
||||
warn!(log, "Unable to become drm master, assuming unpriviledged mode");
|
||||
dev.priviledged = false;
|
||||
};
|
||||
|
||||
// enumerate (and save) the current device state
|
||||
let res_handles = ControlDevice::resource_handles(&dev).chain_err(|| {
|
||||
ErrorKind::DrmDev(format!("Error loading drm resources on {:?}", dev.dev_path()))
|
||||
})?;
|
||||
for &con in res_handles.connectors() {
|
||||
let con_info = connector::Info::load_from_device(&dev, con).chain_err(|| {
|
||||
ErrorKind::DrmDev(format!("Error loading connector info on {:?}", dev.dev_path()))
|
||||
})?;
|
||||
if let Some(enc) = con_info.current_encoder() {
|
||||
let enc_info = encoder::Info::load_from_device(&dev, enc).chain_err(|| {
|
||||
ErrorKind::DrmDev(format!("Error loading encoder info on {:?}", dev.dev_path()))
|
||||
})?;
|
||||
if let Some(crtc) = enc_info.current_crtc() {
|
||||
let info = crtc::Info::load_from_device(&dev, crtc).chain_err(|| {
|
||||
ErrorKind::DrmDev(format!("Error loading crtc info on {:?}", dev.dev_path()))
|
||||
})?;
|
||||
dev.old_state
|
||||
.entry(crtc)
|
||||
.or_insert((info, Vec::new()))
|
||||
.1
|
||||
.push(con);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(LegacyDrmDevice {
|
||||
dev: Rc::new(dev),
|
||||
dev_id,
|
||||
active,
|
||||
backends: Rc::new(RefCell::new(HashMap::new())),
|
||||
handler: None,
|
||||
logger: log.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: AsRawFd + 'static> AsRawFd for LegacyDrmDevice<A> {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.dev.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: AsRawFd + 'static> BasicDevice for LegacyDrmDevice<A> {}
|
||||
impl<A: AsRawFd + 'static> ControlDevice for LegacyDrmDevice<A> {}
|
||||
|
||||
impl<A: AsRawFd + 'static> Device for LegacyDrmDevice<A> {
|
||||
type Surface = LegacyDrmSurface<A>;
|
||||
|
||||
fn device_id(&self) -> dev_t {
|
||||
self.dev_id
|
||||
}
|
||||
|
||||
fn set_handler(&mut self, handler: impl DeviceHandler<Device = Self> + 'static) {
|
||||
self.handler = Some(RefCell::new(Box::new(handler)));
|
||||
}
|
||||
|
||||
fn clear_handler(&mut self) {
|
||||
let _ = self.handler.take();
|
||||
}
|
||||
|
||||
fn create_surface(&mut self, crtc: crtc::Handle) -> Result<LegacyDrmSurface<A>> {
|
||||
if self.backends.borrow().contains_key(&crtc) {
|
||||
bail!(ErrorKind::CrtcAlreadyInUse(crtc));
|
||||
}
|
||||
|
||||
if !self.active.load(Ordering::SeqCst) {
|
||||
bail!(ErrorKind::DeviceInactive);
|
||||
}
|
||||
|
||||
// Try to enumarate the current state to set the initial state variable correctly
|
||||
|
||||
let crtc_info = crtc::Info::load_from_device(self, crtc)
|
||||
.chain_err(|| ErrorKind::DrmDev(format!("Error loading crtc info on {:?}", self.dev_path())))?;
|
||||
|
||||
let mode = crtc_info.mode();
|
||||
|
||||
let mut connectors = HashSet::new();
|
||||
let res_handles = ControlDevice::resource_handles(self).chain_err(|| {
|
||||
ErrorKind::DrmDev(format!("Error loading drm resources on {:?}", self.dev_path()))
|
||||
})?;
|
||||
for &con in res_handles.connectors() {
|
||||
let con_info = connector::Info::load_from_device(self, con).chain_err(|| {
|
||||
ErrorKind::DrmDev(format!("Error loading connector info on {:?}", self.dev_path()))
|
||||
})?;
|
||||
if let Some(enc) = con_info.current_encoder() {
|
||||
let enc_info = encoder::Info::load_from_device(self, enc).chain_err(|| {
|
||||
ErrorKind::DrmDev(format!("Error loading encoder info on {:?}", self.dev_path()))
|
||||
})?;
|
||||
if let Some(current_crtc) = enc_info.current_crtc() {
|
||||
if crtc == current_crtc {
|
||||
connectors.insert(con);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let state = State { mode, connectors };
|
||||
let backend = Rc::new(LegacyDrmSurfaceInternal {
|
||||
dev: self.dev.clone(),
|
||||
crtc,
|
||||
state: RwLock::new(state.clone()),
|
||||
pending: RwLock::new(state),
|
||||
logger: self.logger.new(o!("crtc" => format!("{:?}", crtc))),
|
||||
});
|
||||
|
||||
self.backends.borrow_mut().insert(crtc, Rc::downgrade(&backend));
|
||||
Ok(LegacyDrmSurface(backend))
|
||||
}
|
||||
|
||||
fn process_events(&mut self) {
|
||||
match crtc::receive_events(self) {
|
||||
Ok(events) => {
|
||||
for event in events {
|
||||
if let crtc::Event::PageFlip(event) = event {
|
||||
if self.active.load(Ordering::SeqCst) {
|
||||
if self
|
||||
.backends
|
||||
.borrow()
|
||||
.get(&event.crtc)
|
||||
.iter()
|
||||
.flat_map(|x| x.upgrade())
|
||||
.next()
|
||||
.is_some()
|
||||
{
|
||||
trace!(self.logger, "Handling event for backend {:?}", event.crtc);
|
||||
if let Some(handler) = self.handler.as_ref() {
|
||||
handler.borrow_mut().vblank(event.crtc);
|
||||
}
|
||||
} else {
|
||||
self.backends.borrow_mut().remove(&event.crtc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
if let Some(handler) = self.handler.as_ref() {
|
||||
handler.borrow_mut().error(
|
||||
ResultExt::<()>::chain_err(Err(err), || {
|
||||
ErrorKind::DrmDev(format!("Error processing drm events on {:?}", self.dev_path()))
|
||||
})
|
||||
.unwrap_err(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn resource_info<T: ResourceInfo>(&self, handle: T::Handle) -> Result<T> {
|
||||
T::load_from_device(self, handle)
|
||||
.chain_err(|| ErrorKind::DrmDev(format!("Error loading resource info on {:?}", self.dev_path())))
|
||||
}
|
||||
|
||||
fn resource_handles(&self) -> Result<ResourceHandles> {
|
||||
ControlDevice::resource_handles(self)
|
||||
.chain_err(|| ErrorKind::DrmDev(format!("Error loading resource info on {:?}", self.dev_path())))
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: AsRawFd + 'static> RawDevice for LegacyDrmDevice<A> {
|
||||
type Surface = LegacyDrmSurface<A>;
|
||||
}
|
||||
|
||||
impl<A: AsRawFd + 'static> Drop for LegacyDrmDevice<A> {
|
||||
fn drop(&mut self) {
|
||||
self.clear_handler();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
//!
|
||||
//! Support to register an open [`LegacyDrmDevice`](../struct.LegacyDrmDevice.html)
|
||||
//! to an open [`Session`](../../session/trait.Session.html).
|
||||
//!
|
||||
|
||||
use drm::control::crtc;
|
||||
use drm::Device as BasicDevice;
|
||||
use nix::libc::dev_t;
|
||||
use nix::sys::stat;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::rc::{Rc, Weak};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::{Dev, LegacyDrmDevice, LegacyDrmSurfaceInternal};
|
||||
use backend::session::{AsSessionObserver, SessionObserver};
|
||||
|
||||
/// [`SessionObserver`](../../session/trait.SessionObserver.html)
|
||||
/// linked to the [`LegacyDrmDevice`](../struct.LegacyDrmDevice.html)
|
||||
/// it was created from.
|
||||
pub struct LegacyDrmDeviceObserver<A: AsRawFd + 'static> {
|
||||
dev: Weak<Dev<A>>,
|
||||
dev_id: dev_t,
|
||||
priviledged: bool,
|
||||
active: Arc<AtomicBool>,
|
||||
backends: Weak<RefCell<HashMap<crtc::Handle, Weak<LegacyDrmSurfaceInternal<A>>>>>,
|
||||
logger: ::slog::Logger,
|
||||
}
|
||||
|
||||
impl<A: AsRawFd + 'static> AsSessionObserver<LegacyDrmDeviceObserver<A>> for LegacyDrmDevice<A> {
|
||||
fn observer(&mut self) -> LegacyDrmDeviceObserver<A> {
|
||||
LegacyDrmDeviceObserver {
|
||||
dev: Rc::downgrade(&self.dev),
|
||||
dev_id: self.dev_id,
|
||||
active: self.active.clone(),
|
||||
priviledged: self.dev.priviledged,
|
||||
backends: Rc::downgrade(&self.backends),
|
||||
logger: self.logger.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: AsRawFd + 'static> SessionObserver for LegacyDrmDeviceObserver<A> {
|
||||
fn pause(&mut self, devnum: Option<(u32, u32)>) {
|
||||
if let Some((major, minor)) = devnum {
|
||||
if major as u64 != stat::major(self.dev_id) || minor as u64 != stat::minor(self.dev_id) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if let Some(device) = self.dev.upgrade() {
|
||||
if let Some(backends) = self.backends.upgrade() {
|
||||
for surface in backends.borrow().values().filter_map(Weak::upgrade) {
|
||||
// other ttys that use no cursor, might not clear it themselves.
|
||||
// This makes sure our cursor won't stay visible.
|
||||
let _ = crtc::clear_cursor(&*device, surface.crtc);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.active.store(false, Ordering::SeqCst);
|
||||
if self.priviledged {
|
||||
if let Some(device) = self.dev.upgrade() {
|
||||
if let Err(err) = device.drop_master() {
|
||||
error!(self.logger, "Failed to drop drm master state. Error: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn activate(&mut self, devnum: Option<(u32, u32, Option<RawFd>)>) {
|
||||
if let Some((major, minor, fd)) = devnum {
|
||||
if major as u64 != stat::major(self.dev_id) || minor as u64 != stat::minor(self.dev_id) {
|
||||
return;
|
||||
} else if let Some(fd) = fd {
|
||||
info!(self.logger, "Replacing fd");
|
||||
if let Some(device) = self.dev.upgrade() {
|
||||
::nix::unistd::dup2(device.as_raw_fd(), fd)
|
||||
.expect("Failed to replace file descriptor of drm device");
|
||||
}
|
||||
}
|
||||
}
|
||||
self.active.store(true, Ordering::SeqCst);
|
||||
if self.priviledged {
|
||||
if let Some(device) = self.dev.upgrade() {
|
||||
if let Err(err) = device.set_master() {
|
||||
crit!(self.logger, "Failed to acquire drm master again. Error: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,317 @@
|
|||
pub use drm::buffer::Buffer;
|
||||
use drm::control::{connector, crtc, encoder, framebuffer, Device as ControlDevice, Mode, ResourceInfo};
|
||||
use drm::Device as BasicDevice;
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::rc::Rc;
|
||||
use std::sync::RwLock;
|
||||
|
||||
use backend::drm::{DevPath, RawSurface, Surface};
|
||||
use backend::graphics::CursorBackend;
|
||||
use backend::graphics::SwapBuffersError;
|
||||
|
||||
use super::{error::*, Dev};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct State {
|
||||
pub mode: Option<Mode>,
|
||||
pub connectors: HashSet<connector::Handle>,
|
||||
}
|
||||
|
||||
pub(super) struct LegacyDrmSurfaceInternal<A: AsRawFd + 'static> {
|
||||
pub(super) dev: Rc<Dev<A>>,
|
||||
pub(super) crtc: crtc::Handle,
|
||||
pub(super) state: RwLock<State>,
|
||||
pub(super) pending: RwLock<State>,
|
||||
pub(super) logger: ::slog::Logger,
|
||||
}
|
||||
|
||||
impl<A: AsRawFd + 'static> AsRawFd for LegacyDrmSurfaceInternal<A> {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.dev.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: AsRawFd + 'static> BasicDevice for LegacyDrmSurfaceInternal<A> {}
|
||||
impl<A: AsRawFd + 'static> ControlDevice for LegacyDrmSurfaceInternal<A> {}
|
||||
|
||||
impl<'a, A: AsRawFd + 'static> CursorBackend<'a> for LegacyDrmSurfaceInternal<A> {
|
||||
type CursorFormat = &'a Buffer;
|
||||
type Error = Error;
|
||||
|
||||
fn set_cursor_position(&self, x: u32, y: u32) -> Result<()> {
|
||||
trace!(self.logger, "Move the cursor to {},{}", x, y);
|
||||
crtc::move_cursor(self, self.crtc, (x as i32, y as i32))
|
||||
.chain_err(|| ErrorKind::DrmDev(format!("Error moving cursor on {:?}", self.dev_path())))
|
||||
}
|
||||
|
||||
fn set_cursor_representation<'b>(&'b self, buffer: Self::CursorFormat, hotspot: (u32, u32)) -> Result<()>
|
||||
where
|
||||
'a: 'b,
|
||||
{
|
||||
trace!(self.logger, "Setting the new imported cursor");
|
||||
|
||||
if crtc::set_cursor2(self, self.crtc, buffer, (hotspot.0 as i32, hotspot.1 as i32)).is_err() {
|
||||
crtc::set_cursor(self, self.crtc, buffer)
|
||||
.chain_err(|| ErrorKind::DrmDev(format!("Failed to set cursor on {:?}", self.dev_path())))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: AsRawFd + 'static> Surface for LegacyDrmSurfaceInternal<A> {
|
||||
type Error = Error;
|
||||
type Connectors = HashSet<connector::Handle>;
|
||||
|
||||
fn crtc(&self) -> crtc::Handle {
|
||||
self.crtc
|
||||
}
|
||||
|
||||
fn current_connectors(&self) -> Self::Connectors {
|
||||
self.state.read().unwrap().connectors.clone()
|
||||
}
|
||||
|
||||
fn pending_connectors(&self) -> Self::Connectors {
|
||||
self.pending.read().unwrap().connectors.clone()
|
||||
}
|
||||
|
||||
fn current_mode(&self) -> Option<Mode> {
|
||||
self.state.read().unwrap().mode.clone()
|
||||
}
|
||||
|
||||
fn pending_mode(&self) -> Option<Mode> {
|
||||
self.pending.read().unwrap().mode.clone()
|
||||
}
|
||||
|
||||
fn add_connector(&self, connector: connector::Handle) -> Result<()> {
|
||||
let info = connector::Info::load_from_device(self, connector).chain_err(|| {
|
||||
ErrorKind::DrmDev(format!("Error loading connector info on {:?}", self.dev_path()))
|
||||
})?;
|
||||
|
||||
let mut pending = self.pending.write().unwrap();
|
||||
|
||||
// check if the connector can handle the current mode
|
||||
if info.modes().contains(pending.mode.as_ref().unwrap()) {
|
||||
// check if there is a valid encoder
|
||||
let encoders = info
|
||||
.encoders()
|
||||
.iter()
|
||||
.map(|encoder| {
|
||||
encoder::Info::load_from_device(self, *encoder).chain_err(|| {
|
||||
ErrorKind::DrmDev(format!("Error loading encoder info on {:?}", self.dev_path()))
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<encoder::Info>>>()?;
|
||||
|
||||
// and if any encoder supports the selected crtc
|
||||
let resource_handles = self.resource_handles().chain_err(|| {
|
||||
ErrorKind::DrmDev(format!("Error loading resources on {:?}", self.dev_path()))
|
||||
})?;
|
||||
if !encoders
|
||||
.iter()
|
||||
.map(|encoder| encoder.possible_crtcs())
|
||||
.all(|crtc_list| resource_handles.filter_crtcs(crtc_list).contains(&self.crtc))
|
||||
{
|
||||
bail!(ErrorKind::NoSuitableEncoder(info, self.crtc));
|
||||
}
|
||||
|
||||
pending.connectors.insert(connector);
|
||||
Ok(())
|
||||
} else {
|
||||
bail!(ErrorKind::ModeNotSuitable(pending.mode.unwrap()));
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_connector(&self, connector: connector::Handle) -> Result<()> {
|
||||
self.pending.write().unwrap().connectors.remove(&connector);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn use_mode(&self, mode: Option<Mode>) -> Result<()> {
|
||||
let mut pending = self.pending.write().unwrap();
|
||||
|
||||
// check the connectors to see if this mode is supported
|
||||
if let Some(mode) = mode {
|
||||
for connector in &pending.connectors {
|
||||
if !connector::Info::load_from_device(self, *connector)
|
||||
.chain_err(|| {
|
||||
ErrorKind::DrmDev(format!("Error loading connector info on {:?}", self.dev_path()))
|
||||
})?
|
||||
.modes()
|
||||
.contains(&mode)
|
||||
{
|
||||
bail!(ErrorKind::ModeNotSuitable(mode));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pending.mode = mode;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: AsRawFd + 'static> RawSurface for LegacyDrmSurfaceInternal<A> {
|
||||
fn commit_pending(&self) -> bool {
|
||||
*self.pending.read().unwrap() != *self.state.read().unwrap()
|
||||
}
|
||||
|
||||
fn commit(&self, framebuffer: framebuffer::Handle) -> Result<()> {
|
||||
let mut current = self.state.write().unwrap();
|
||||
let pending = self.pending.read().unwrap();
|
||||
|
||||
{
|
||||
let removed = current.connectors.difference(&pending.connectors);
|
||||
let added = pending.connectors.difference(¤t.connectors);
|
||||
|
||||
for conn in removed {
|
||||
if let Ok(info) = connector::Info::load_from_device(self, *conn) {
|
||||
info!(self.logger, "Removing connector: {:?}", info.connector_type());
|
||||
} else {
|
||||
info!(self.logger, "Removing unknown connector");
|
||||
}
|
||||
}
|
||||
|
||||
for conn in added {
|
||||
if let Ok(info) = connector::Info::load_from_device(self, *conn) {
|
||||
info!(self.logger, "Adding connector: {:?}", info.connector_type());
|
||||
} else {
|
||||
info!(self.logger, "Adding unknown connector");
|
||||
}
|
||||
}
|
||||
|
||||
if current.mode != pending.mode {
|
||||
info!(
|
||||
self.logger,
|
||||
"Setting new mode: {:?}",
|
||||
pending.mode.as_ref().unwrap().name()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
debug!(self.logger, "Setting screen");
|
||||
crtc::set(
|
||||
self,
|
||||
self.crtc,
|
||||
framebuffer,
|
||||
&pending
|
||||
.connectors
|
||||
.iter()
|
||||
.map(|x| *x)
|
||||
.collect::<Vec<connector::Handle>>(),
|
||||
(0, 0),
|
||||
pending.mode,
|
||||
)
|
||||
.chain_err(|| {
|
||||
ErrorKind::DrmDev(format!(
|
||||
"Error setting crtc {:?} on {:?}",
|
||||
self.crtc,
|
||||
self.dev_path()
|
||||
))
|
||||
})?;
|
||||
|
||||
*current = pending.clone();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn page_flip(&self, framebuffer: framebuffer::Handle) -> ::std::result::Result<(), SwapBuffersError> {
|
||||
trace!(self.logger, "Queueing Page flip");
|
||||
|
||||
crtc::page_flip(
|
||||
self,
|
||||
self.crtc,
|
||||
framebuffer,
|
||||
&[crtc::PageFlipFlags::PageFlipEvent],
|
||||
)
|
||||
.map_err(|_| SwapBuffersError::ContextLost)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: AsRawFd + 'static> Drop for LegacyDrmSurfaceInternal<A> {
|
||||
fn drop(&mut self) {
|
||||
// ignore failure at this point
|
||||
let _ = crtc::clear_cursor(self, self.crtc);
|
||||
}
|
||||
}
|
||||
|
||||
/// Open raw crtc utilizing legacy mode-setting
|
||||
pub struct LegacyDrmSurface<A: AsRawFd + 'static>(pub(super) Rc<LegacyDrmSurfaceInternal<A>>);
|
||||
|
||||
impl<A: AsRawFd + 'static> AsRawFd for LegacyDrmSurface<A> {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.0.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: AsRawFd + 'static> BasicDevice for LegacyDrmSurface<A> {}
|
||||
impl<A: AsRawFd + 'static> ControlDevice for LegacyDrmSurface<A> {}
|
||||
|
||||
impl<'a, A: AsRawFd + 'static> CursorBackend<'a> for LegacyDrmSurface<A> {
|
||||
type CursorFormat = &'a Buffer;
|
||||
type Error = Error;
|
||||
|
||||
fn set_cursor_position(&self, x: u32, y: u32) -> Result<()> {
|
||||
self.0.set_cursor_position(x, y)
|
||||
}
|
||||
|
||||
fn set_cursor_representation<'b>(&'b self, buffer: Self::CursorFormat, hotspot: (u32, u32)) -> Result<()>
|
||||
where
|
||||
'a: 'b,
|
||||
{
|
||||
self.0.set_cursor_representation(buffer, hotspot)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: AsRawFd + 'static> Surface for LegacyDrmSurface<A> {
|
||||
type Error = Error;
|
||||
type Connectors = HashSet<connector::Handle>;
|
||||
|
||||
fn crtc(&self) -> crtc::Handle {
|
||||
self.0.crtc()
|
||||
}
|
||||
|
||||
fn current_connectors(&self) -> Self::Connectors {
|
||||
self.0.current_connectors()
|
||||
}
|
||||
|
||||
fn pending_connectors(&self) -> Self::Connectors {
|
||||
self.0.pending_connectors()
|
||||
}
|
||||
|
||||
fn current_mode(&self) -> Option<Mode> {
|
||||
self.0.current_mode()
|
||||
}
|
||||
|
||||
fn pending_mode(&self) -> Option<Mode> {
|
||||
self.0.pending_mode()
|
||||
}
|
||||
|
||||
fn add_connector(&self, connector: connector::Handle) -> Result<()> {
|
||||
self.0.add_connector(connector)
|
||||
}
|
||||
|
||||
fn remove_connector(&self, connector: connector::Handle) -> Result<()> {
|
||||
self.0.remove_connector(connector)
|
||||
}
|
||||
|
||||
fn use_mode(&self, mode: Option<Mode>) -> Result<()> {
|
||||
self.0.use_mode(mode)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: AsRawFd + 'static> RawSurface for LegacyDrmSurface<A> {
|
||||
fn commit_pending(&self) -> bool {
|
||||
self.0.commit_pending()
|
||||
}
|
||||
|
||||
fn commit(&self, framebuffer: framebuffer::Handle) -> Result<()> {
|
||||
self.0.commit(framebuffer)
|
||||
}
|
||||
|
||||
fn page_flip(&self, framebuffer: framebuffer::Handle) -> ::std::result::Result<(), SwapBuffersError> {
|
||||
self.0.page_flip(framebuffer)
|
||||
}
|
||||
}
|
|
@ -1,455 +1,217 @@
|
|||
//! Drm/Kms types and backend implementations
|
||||
//!
|
||||
//! This module provide a `DrmDevice` which acts as a representation for any DRM
|
||||
//! device and can be used to create the second provided structure a `DrmBackend`.
|
||||
//!
|
||||
//! Initialization happens through the types provided by [`drm-rs`](https://docs.rs/drm/).
|
||||
//!
|
||||
//! Three entities are relevant for the initialization procedure.
|
||||
//!
|
||||
//! "Crtc"s represent scanout engines of the device pointer to one framebuffer. There responsibility
|
||||
//! is to read the data of the framebuffer and export it into an "Encoder". The number of crtc's
|
||||
//! represent the number of independent output devices the hardware may handle.
|
||||
//!
|
||||
//! An "Encoder" encodes the data of connected crtcs into a video signal for a fixed set
|
||||
//! of connectors. E.g. you might have an analog encoder based on a DAG for VGA ports, but another
|
||||
//! one for digital ones. Also not every encoder might be connected to every crtc.
|
||||
//!
|
||||
//! The last entity the "Connector" represents a port on your computer, possibly with a connected
|
||||
//! monitor, TV, capture card, etc.
|
||||
//!
|
||||
//! The `DrmBackend` created from a `DrmDevice` represents a crtc of the device you can render to
|
||||
//! and that feeds a given set of connectors, that can be manipulated at runtime.
|
||||
//!
|
||||
//! From these circumstances it becomes clear, that one crtc might only send it's data to a connector,
|
||||
//! that is attached to any encoder that is attached to the crtc itself. It is the responsibility of the
|
||||
//! user to ensure that a given set of a crtc with it's connectors is valid or an error will be thrown.
|
||||
//!
|
||||
//! For more details refer to the [`drm-rs` documentation](https://docs.rs/drm).
|
||||
//!
|
||||
//!
|
||||
//! ## How to use it
|
||||
//! This module provides Traits reprensentating open devices
|
||||
//! and their surfaces to render contents.
|
||||
//!
|
||||
//! ### Initialization
|
||||
//! ---
|
||||
//!
|
||||
//! To initialize the `DrmDevice` you need either a `RawFd` or a `File` of
|
||||
//! your DRM node. The `File` is recommended as it represents the save API.
|
||||
//! Initialization of devices happens through an open file descriptor
|
||||
//! of a drm device.
|
||||
//!
|
||||
//! Once you got your `DrmDevice` you can then use it to create `DrmBackend`s.
|
||||
//! You will need to use the `drm` crate to provide the required types to create
|
||||
//! a backend.
|
||||
//! ---
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! extern crate drm;
|
||||
//! extern crate smithay;
|
||||
//! extern crate wayland_server;
|
||||
//! Initialization of surfaces happens through the types provided by
|
||||
//! [`drm-rs`](https://docs.rs/drm/0.3.4/drm/).
|
||||
//!
|
||||
//! use drm::Device as BasicDevice;
|
||||
//! use drm::control::{Device as ControlDevice, ResourceInfo};
|
||||
//! use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState};
|
||||
//! use drm::control::encoder::{Info as EncoderInfo};
|
||||
//! use std::fs::{File, OpenOptions};
|
||||
//! use std::os::unix::io::RawFd;
|
||||
//! use std::os::unix::io::AsRawFd;
|
||||
//! use smithay::backend::drm::{DrmDevice, DrmBackend};
|
||||
//! Four entities are relevant for the initialization procedure.
|
||||
//!
|
||||
//! #[derive(Debug)]
|
||||
//! pub struct Card(File);
|
||||
//! [`crtc`](https://docs.rs/drm/0.3.4/drm/control/crtc/index.html)s represent scanout engines
|
||||
//! of the device pointer to one framebuffer.
|
||||
//! Their responsibility is to read the data of the framebuffer and export it into an "Encoder".
|
||||
//! The number of crtc's represent the number of independant output devices the hardware may handle.
|
||||
//!
|
||||
//! impl AsRawFd for Card {
|
||||
//! fn as_raw_fd(&self) -> RawFd {
|
||||
//! self.0.as_raw_fd()
|
||||
//! }
|
||||
//! }
|
||||
//! An [`encoder`](https://docs.rs/drm/0.3.4/drm/control/encoder/index.html) encodes the data of
|
||||
//! connected crtcs into a video signal for a fixed set of connectors.
|
||||
//! E.g. you might have an analog encoder based on a DAG for VGA ports, but another one for digital ones.
|
||||
//! Also not every encoder might be connected to every crtc.
|
||||
//!
|
||||
//! impl BasicDevice for Card {}
|
||||
//! impl ControlDevice for Card {}
|
||||
//! A [`connector`](https://docs.rs/drm/0.3.4/drm/control/connector/index.html) represents a port
|
||||
//! on your computer, possibly with a connected monitor, TV, capture card, etc.
|
||||
//!
|
||||
//! # fn main() {
|
||||
//! // Open the drm device
|
||||
//! let mut options = OpenOptions::new();
|
||||
//! options.read(true);
|
||||
//! options.write(true);
|
||||
//! let mut device = DrmDevice::new(
|
||||
//! Card(options.open("/dev/dri/card0").unwrap()), // try to detect it properly
|
||||
//! None /*put a logger here*/
|
||||
//! ).unwrap();
|
||||
//! On surface creation a matching encoder for your `encoder`-`connector` is automatically selected,
|
||||
//! if it exists, which means you still need to check your configuration.
|
||||
//!
|
||||
//! // Get a set of all modesetting resource handles
|
||||
//! let res_handles = device.resource_handles().unwrap();
|
||||
//! At last a [`Mode`](https://docs.rs/drm/0.3.4/drm/control/struct.Mode.html) needs to be selected,
|
||||
//! supported by the `crtc` in question.
|
||||
//!
|
||||
//! // Use first connected connector for this example
|
||||
//! let connector_info = res_handles.connectors().iter()
|
||||
//! .map(|conn| ConnectorInfo::load_from_device(&device, *conn).unwrap())
|
||||
//! .find(|conn| conn.connection_state() == ConnectorState::Connected)
|
||||
//! .unwrap();
|
||||
//!
|
||||
//! // Use the first encoder
|
||||
//! let encoder_info = EncoderInfo::load_from_device(&device, connector_info.encoders()[0]).unwrap();
|
||||
//!
|
||||
//! // use the connected crtc if any
|
||||
//! let crtc = encoder_info.current_crtc()
|
||||
//! // or use the first one that is compatible with the encoder
|
||||
//! .unwrap_or_else(||
|
||||
//! *res_handles.filter_crtcs(encoder_info.possible_crtcs())
|
||||
//! .iter()
|
||||
//! .next()
|
||||
//! .unwrap());
|
||||
//!
|
||||
//! // Use first mode (usually the highest resolution)
|
||||
//! let mode = connector_info.modes()[0];
|
||||
//!
|
||||
//! // Create the backend
|
||||
//! let backend = device.create_backend(
|
||||
//! crtc,
|
||||
//! mode,
|
||||
//! vec![connector_info.handle()]
|
||||
//! ).unwrap();
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ### Page Flips / Tear-free video
|
||||
//! Calling the usual `EglGraphicsBackend::swap_buffers` function on a
|
||||
//! `DrmBackend` works the same to finish the rendering, but will return
|
||||
//! `SwapBuffersError::AlreadySwapped` for any new calls until the page flip of the
|
||||
//! crtc has happened.
|
||||
//!
|
||||
//! You can monitor the page flips by registering the `DrmDevice` as and
|
||||
//! `FdEventSourceHandler` and setting a `DrmHandler` on it. You will be notified
|
||||
//! whenever a page flip has happened, so you can render the next frame immediately
|
||||
//! and get a tear-free representation on the display.
|
||||
//!
|
||||
//! You need to render at least once to successfully trigger the first event.
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! # extern crate drm;
|
||||
//! # extern crate smithay;
|
||||
//! # extern crate wayland_server;
|
||||
//! #
|
||||
//! # use drm::Device as BasicDevice;
|
||||
//! # use drm::control::{Device as ControlDevice, ResourceInfo};
|
||||
//! # use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState};
|
||||
//! use drm::control::crtc::{Handle as CrtcHandle};
|
||||
//! use drm::result::Error as DrmError;
|
||||
//! # use std::fs::{File, OpenOptions};
|
||||
//! # use std::os::unix::io::RawFd;
|
||||
//! # use std::os::unix::io::AsRawFd;
|
||||
//! # use std::time::Duration;
|
||||
//! use smithay::backend::drm::{DrmDevice, DrmBackend, DrmHandler, drm_device_bind};
|
||||
//! use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
||||
//! #
|
||||
//! # #[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 mut event_loop = wayland_server::calloop::EventLoop::<()>::new().unwrap();
|
||||
//! # let mut display = wayland_server::Display::new(event_loop.handle());
|
||||
//! #
|
||||
//! # let mut options = OpenOptions::new();
|
||||
//! # options.read(true);
|
||||
//! # options.write(true);
|
||||
//! # let mut device = DrmDevice::new(
|
||||
//! # Card(options.open("/dev/dri/card0").unwrap()), // try to detect it properly
|
||||
//! # None /*put a logger here*/
|
||||
//! # ).unwrap();
|
||||
//! #
|
||||
//! # let res_handles = device.resource_handles().unwrap();
|
||||
//! # let connector_info = res_handles.connectors().iter()
|
||||
//! # .map(|conn| ConnectorInfo::load_from_device(&device, *conn).unwrap())
|
||||
//! # .find(|conn| conn.connection_state() == ConnectorState::Connected)
|
||||
//! # .unwrap();
|
||||
//! # let crtc = res_handles.crtcs()[0];
|
||||
//! # let mode = connector_info.modes()[0];
|
||||
//! # let backend = device.create_backend(
|
||||
//! # crtc,
|
||||
//! # mode,
|
||||
//! # vec![connector_info.handle()]
|
||||
//! # ).unwrap();
|
||||
//!
|
||||
//! struct MyDrmHandler(DrmBackend<Card>);
|
||||
//!
|
||||
//! impl DrmHandler<Card> for MyDrmHandler {
|
||||
//! fn ready(
|
||||
//! &mut self,
|
||||
//! _device: &mut DrmDevice<Card>,
|
||||
//! _crtc: CrtcHandle,
|
||||
//! _frame: u32,
|
||||
//! _duration: Duration)
|
||||
//! {
|
||||
//! // render surfaces and swap again
|
||||
//! self.0.swap_buffers().unwrap();
|
||||
//! }
|
||||
//! fn error(
|
||||
//! &mut self,
|
||||
//! device: &mut DrmDevice<Card>,
|
||||
//! error: DrmError)
|
||||
//! {
|
||||
//! panic!("DrmDevice errored: {}", error);
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! // render something (like clear_color)
|
||||
//! backend.swap_buffers().unwrap();
|
||||
//!
|
||||
//! let (_source, _device_rc) = drm_device_bind(
|
||||
//! &event_loop.handle(),
|
||||
//! device,
|
||||
//! MyDrmHandler(backend)
|
||||
//! ).map_err(|(err, _)| err).unwrap();
|
||||
//!
|
||||
//! /* And then run the event loop once all your setup is done */
|
||||
//! # }
|
||||
//! ```
|
||||
|
||||
use backend::graphics::egl::{
|
||||
context::{EGLContext, GlAttributes},
|
||||
error::Result as EGLResult,
|
||||
native::Gbm,
|
||||
wayland::{EGLDisplay, EGLWaylandExtensions},
|
||||
};
|
||||
#[cfg(feature = "backend_session")]
|
||||
use backend::session::{AsSessionObserver, SessionObserver};
|
||||
use drm::{
|
||||
control::{connector, crtc, encoder, framebuffer, Device as ControlDevice, Mode, ResourceInfo},
|
||||
result::Error as DrmError,
|
||||
pub use drm::{
|
||||
buffer::Buffer,
|
||||
control::{
|
||||
connector, crtc, encoder, framebuffer, Device as ControlDevice, Mode, ResourceHandles, ResourceInfo,
|
||||
},
|
||||
Device as BasicDevice,
|
||||
};
|
||||
use gbm::{BufferObject, Device as GbmDevice};
|
||||
use nix::{
|
||||
self,
|
||||
sys::stat::{self, dev_t, fstat},
|
||||
};
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::HashMap,
|
||||
hash::{Hash, Hasher},
|
||||
io::Error as IoError,
|
||||
os::unix::io::{AsRawFd, RawFd},
|
||||
path::PathBuf,
|
||||
rc::{Rc, Weak},
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc, Once, ONCE_INIT,
|
||||
},
|
||||
time::Duration,
|
||||
};
|
||||
pub use nix::libc::dev_t;
|
||||
|
||||
use wayland_server::{
|
||||
calloop::{
|
||||
generic::{EventedRawFd, Generic},
|
||||
mio::Ready,
|
||||
LoopHandle, Source,
|
||||
},
|
||||
Display,
|
||||
};
|
||||
use std::error::Error;
|
||||
use std::iter::IntoIterator;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::path::PathBuf;
|
||||
|
||||
mod backend;
|
||||
pub mod error;
|
||||
use wayland_server::calloop::generic::{EventedFd, Generic};
|
||||
use wayland_server::calloop::mio::Ready;
|
||||
pub use wayland_server::calloop::InsertError;
|
||||
use wayland_server::calloop::{LoopHandle, Source};
|
||||
|
||||
pub use self::backend::DrmBackend;
|
||||
use self::{backend::DrmBackendInternal, error::*};
|
||||
use super::graphics::SwapBuffersError;
|
||||
|
||||
static LOAD: Once = ONCE_INIT;
|
||||
#[cfg(feature = "backend_drm_egl")]
|
||||
pub mod egl;
|
||||
#[cfg(feature = "backend_drm_gbm")]
|
||||
pub mod gbm;
|
||||
#[cfg(feature = "backend_drm_legacy")]
|
||||
pub mod legacy;
|
||||
|
||||
/// Representation of an open DRM device node to create rendering backends
|
||||
pub struct DrmDevice<A: ControlDevice + 'static> {
|
||||
context: Rc<EGLContext<Gbm<framebuffer::Info>, GbmDevice<A>>>,
|
||||
old_state: HashMap<crtc::Handle, (crtc::Info, Vec<connector::Handle>)>,
|
||||
device_id: dev_t,
|
||||
backends: Rc<RefCell<HashMap<crtc::Handle, Weak<DrmBackendInternal<A>>>>>,
|
||||
active: Arc<AtomicBool>,
|
||||
privileged: bool,
|
||||
logger: ::slog::Logger,
|
||||
/// Trait to receive events of a bound [`Device`](trait.Device.html)
|
||||
///
|
||||
/// See [`device_bind`](fn.device_bind.html)
|
||||
pub trait DeviceHandler {
|
||||
/// The [`Device`](trait.Device.html) type this handler can handle
|
||||
type Device: Device + ?Sized;
|
||||
|
||||
/// A vblank blank event on the provided crtc has happend
|
||||
fn vblank(&mut self, crtc: crtc::Handle);
|
||||
/// An error happend while processing events
|
||||
fn error(&mut self, error: <<<Self as DeviceHandler>::Device as Device>::Surface as Surface>::Error);
|
||||
}
|
||||
|
||||
impl<A: ControlDevice + 'static> DrmDevice<A> {
|
||||
/// Create a new `DrmDevice` from an open DRM node
|
||||
/// An open drm device
|
||||
pub trait Device: AsRawFd + DevPath {
|
||||
/// Associated [`Surface`](trait.Surface.html) of this `Device` type
|
||||
type Surface: Surface;
|
||||
|
||||
/// Returns the `id` of this device node.
|
||||
fn device_id(&self) -> dev_t;
|
||||
|
||||
/// Assigns a `DeviceHandler` called during event processing.
|
||||
///
|
||||
/// Returns an error if the file is no valid DRM node or context creation was not
|
||||
/// successful.
|
||||
pub fn new<L>(dev: A, logger: L) -> Result<Self>
|
||||
where
|
||||
L: Into<Option<::slog::Logger>>,
|
||||
{
|
||||
DrmDevice::new_with_gl_attr(
|
||||
dev,
|
||||
GlAttributes {
|
||||
version: None,
|
||||
profile: None,
|
||||
debug: cfg!(debug_assertions),
|
||||
vsync: true,
|
||||
},
|
||||
logger,
|
||||
)
|
||||
}
|
||||
/// See [`device_bind`](fn.device_bind.html) and [`DeviceHandler`](trait.DeviceHandler.html)
|
||||
fn set_handler(&mut self, handler: impl DeviceHandler<Device = Self> + 'static);
|
||||
/// Clear a set [`DeviceHandler`](trait.DeviceHandler.html), if any
|
||||
fn clear_handler(&mut self);
|
||||
|
||||
/// Create a new `DrmDevice` from an open DRM node and given `GlAttributes`
|
||||
/// Creates a new rendering surface.
|
||||
///
|
||||
/// Returns an error if the file is no valid DRM node or context creation was not
|
||||
/// successful.
|
||||
pub fn new_with_gl_attr<L>(dev: A, attributes: GlAttributes, logger: L) -> Result<Self>
|
||||
where
|
||||
L: Into<Option<::slog::Logger>>,
|
||||
{
|
||||
let log = ::slog_or_stdlog(logger).new(o!("smithay_module" => "backend_drm"));
|
||||
|
||||
/* GBM will load a dri driver, but even though they need symbols from
|
||||
* libglapi, in some version of Mesa they are not linked to it. Since
|
||||
* only the gl-renderer module links to it, these symbols won't be globally available,
|
||||
* and loading the DRI driver fails.
|
||||
* Workaround this by dlopen()'ing libglapi with RTLD_GLOBAL.
|
||||
*/
|
||||
LOAD.call_once(|| unsafe {
|
||||
nix::libc::dlopen(
|
||||
"libglapi.so.0".as_ptr() as *const _,
|
||||
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: Rc::new(RefCell::new(HashMap::new())),
|
||||
device_id,
|
||||
old_state: HashMap::new(),
|
||||
active: Arc::new(AtomicBool::new(true)),
|
||||
privileged: true,
|
||||
logger: log.clone(),
|
||||
};
|
||||
|
||||
info!(log, "DrmDevice initializing");
|
||||
|
||||
// we want to mode-set, so we better be the master, if we run via a tty session
|
||||
if drm.set_master().is_err() {
|
||||
warn!(log, "Unable to become drm master, assuming unprivileged mode");
|
||||
drm.privileged = false;
|
||||
};
|
||||
|
||||
let res_handles = drm.resource_handles().chain_err(|| {
|
||||
ErrorKind::DrmDev(format!("Error loading drm resources on {:?}", drm.dev_path()))
|
||||
})?;
|
||||
for &con in res_handles.connectors() {
|
||||
let con_info = connector::Info::load_from_device(&drm, con).chain_err(|| {
|
||||
ErrorKind::DrmDev(format!("Error loading connector info on {:?}", drm.dev_path()))
|
||||
})?;
|
||||
if let Some(enc) = con_info.current_encoder() {
|
||||
let enc_info = encoder::Info::load_from_device(&drm, enc).chain_err(|| {
|
||||
ErrorKind::DrmDev(format!("Error loading encoder info on {:?}", drm.dev_path()))
|
||||
})?;
|
||||
if let Some(crtc) = enc_info.current_crtc() {
|
||||
let info = crtc::Info::load_from_device(&drm, crtc).chain_err(|| {
|
||||
ErrorKind::DrmDev(format!("Error loading crtc info on {:?}", drm.dev_path()))
|
||||
})?;
|
||||
drm.old_state
|
||||
.entry(crtc)
|
||||
.or_insert((info, Vec::new()))
|
||||
.1
|
||||
.push(con);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(drm)
|
||||
}
|
||||
|
||||
/// Create a new backend on a given crtc with a given `Mode` for a given amount
|
||||
/// of `connectors` (mirroring).
|
||||
/// Initialization of surfaces happens through the types provided by
|
||||
/// [`drm-rs`](https://docs.rs/drm/0.3.4/drm/).
|
||||
///
|
||||
/// Errors if initialization fails or the mode is not available on all given
|
||||
/// connectors.
|
||||
pub fn create_backend<I>(
|
||||
/// [`crtc`](https://docs.rs/drm/0.3.4/drm/control/crtc/index.html)s represent scanout engines
|
||||
/// of the device pointer to one framebuffer.
|
||||
/// Their responsibility is to read the data of the framebuffer and export it into an "Encoder".
|
||||
/// The number of crtc's represent the number of independant output devices the hardware may handle.
|
||||
fn create_surface(
|
||||
&mut self,
|
||||
crtc: crtc::Handle,
|
||||
mode: Mode,
|
||||
connectors: I,
|
||||
) -> Result<DrmBackend<A>>
|
||||
where
|
||||
I: Into<Vec<connector::Handle>>,
|
||||
{
|
||||
if self.backends.borrow().contains_key(&crtc) {
|
||||
bail!(ErrorKind::CrtcAlreadyInUse(crtc));
|
||||
}
|
||||
ctrc: crtc::Handle,
|
||||
) -> Result<Self::Surface, <Self::Surface as Surface>::Error>;
|
||||
|
||||
if !self.active.load(Ordering::SeqCst) {
|
||||
bail!(ErrorKind::DeviceInactive);
|
||||
}
|
||||
/// Processes any open events of the underlying file descriptor.
|
||||
///
|
||||
/// You should not call this function manually, but rather use
|
||||
/// [`device_bind`](fn.device_bind.html) to register the device
|
||||
/// to an [`EventLoop`](https://docs.rs/calloop/0.4.2/calloop/struct.EventLoop.html)
|
||||
/// to synchronize your rendering to the vblank events of the open crtc's
|
||||
fn process_events(&mut self);
|
||||
|
||||
// check if the given connectors and crtc match
|
||||
let connectors = connectors.into();
|
||||
/// Load the resource from a `Device` given its
|
||||
/// [`ResourceHandle`](https://docs.rs/drm/0.3.4/drm/control/trait.ResourceHandle.html)
|
||||
fn resource_info<T: ResourceInfo>(
|
||||
&self,
|
||||
handle: T::Handle,
|
||||
) -> Result<T, <Self::Surface as Surface>::Error>;
|
||||
|
||||
// check if we have an encoder for every connector and the mode mode
|
||||
for connector in &connectors {
|
||||
let con_info = connector::Info::load_from_device(self, *connector).chain_err(|| {
|
||||
ErrorKind::DrmDev(format!("Error loading connector info on {:?}", self.dev_path()))
|
||||
})?;
|
||||
|
||||
// check the mode
|
||||
if !con_info.modes().contains(&mode) {
|
||||
bail!(ErrorKind::ModeNotSuitable(mode));
|
||||
}
|
||||
|
||||
// check for every connector which encoders it does support
|
||||
let encoders = con_info
|
||||
.encoders()
|
||||
.iter()
|
||||
.map(|encoder| {
|
||||
encoder::Info::load_from_device(self, *encoder).chain_err(|| {
|
||||
ErrorKind::DrmDev(format!("Error loading encoder info on {:?}", self.dev_path()))
|
||||
})
|
||||
}).collect::<Result<Vec<encoder::Info>>>()?;
|
||||
|
||||
// and if any encoder supports the selected crtc
|
||||
let resource_handles = self.resource_handles().chain_err(|| {
|
||||
ErrorKind::DrmDev(format!("Error loading drm resources on {:?}", self.dev_path()))
|
||||
})?;
|
||||
if !encoders
|
||||
.iter()
|
||||
.map(|encoder| encoder.possible_crtcs())
|
||||
.any(|crtc_list| resource_handles.filter_crtcs(crtc_list).contains(&crtc))
|
||||
{
|
||||
bail!(ErrorKind::NoSuitableEncoder(con_info, crtc))
|
||||
}
|
||||
}
|
||||
|
||||
// configuration is valid, the kernel will figure out the rest
|
||||
|
||||
let logger = self.logger.new(o!("crtc" => format!("{:?}", crtc)));
|
||||
let backend = DrmBackend::new(self.context.clone(), crtc, mode, connectors, logger)?;
|
||||
self.backends.borrow_mut().insert(crtc, backend.weak());
|
||||
Ok(backend)
|
||||
}
|
||||
|
||||
/// Returns an internal device id, that is unique per boot per system
|
||||
pub fn device_id(&self) -> u64 {
|
||||
self.device_id
|
||||
}
|
||||
/// Attempts to acquire a copy of the `Device`'s
|
||||
/// [`ResourceHandles`](https://docs.rs/drm/0.3.4/drm/control/struct.ResourceHandles.html)
|
||||
fn resource_handles(&self) -> Result<ResourceHandles, <Self::Surface as Surface>::Error>;
|
||||
}
|
||||
|
||||
/// Trait for types representing open devices
|
||||
/// Marker trait for `Device`s able to provide [`RawSurface`](trait.RawSurface.html)s
|
||||
pub trait RawDevice: Device<Surface = <Self as RawDevice>::Surface> {
|
||||
/// Associated [`RawSurface`](trait.RawSurface.html) of this `RawDevice` type
|
||||
type Surface: RawSurface;
|
||||
}
|
||||
|
||||
/// An open crtc that can be used for rendering
|
||||
pub trait Surface {
|
||||
/// Type repesenting a collection of
|
||||
/// [`connector`](https://docs.rs/drm/0.3.4/drm/control/connector/index.html)s
|
||||
/// returned by [`current_connectors`](#method.current_connectors) and
|
||||
/// [`pending_connectors`](#method.pending_connectors)
|
||||
type Connectors: IntoIterator<Item = connector::Handle>;
|
||||
/// Error type returned by methods of this trait
|
||||
type Error: Error + Send;
|
||||
|
||||
/// Returns the underlying [`crtc`](https://docs.rs/drm/0.3.4/drm/control/crtc/index.html) of this surface
|
||||
fn crtc(&self) -> crtc::Handle;
|
||||
/// Currently used [`connector`](https://docs.rs/drm/0.3.4/drm/control/connector/index.html)s of this `Surface`
|
||||
fn current_connectors(&self) -> Self::Connectors;
|
||||
/// Returns the pending [`connector`](https://docs.rs/drm/0.3.4/drm/control/connector/index.html)s
|
||||
/// used after the next `commit` of this `Surface`
|
||||
///
|
||||
/// *Note*: Only on a [`RawSurface`](trait.RawSurface.html) you may directly trigger
|
||||
/// a [`commit`](trait.RawSurface.html#method.commit). Other `Surface`s provide their
|
||||
/// own methods that *may* trigger a commit, you will need to read their docs.
|
||||
fn pending_connectors(&self) -> Self::Connectors;
|
||||
/// Tries to add a new [`connector`](https://docs.rs/drm/0.3.4/drm/control/connector/index.html)
|
||||
/// to be used after the next commit.
|
||||
///
|
||||
/// Fails if the `connector` is not compatible with the underlying [`crtc`](https://docs.rs/drm/0.3.4/drm/control/crtc/index.html)
|
||||
/// (e.g. no suitable [`encoder`](https://docs.rs/drm/0.3.4/drm/control/encoder/index.html) may be found)
|
||||
/// or is not compatible with the currently pending
|
||||
/// [`Mode`](https://docs.rs/drm/0.3.4/drm/control/struct.Mode.html).
|
||||
fn add_connector(&self, connector: connector::Handle) -> Result<(), Self::Error>;
|
||||
/// Tries to mark a [`connector`](https://docs.rs/drm/0.3.4/drm/control/connector/index.html)
|
||||
/// for removal on the next commit.
|
||||
fn remove_connector(&self, connector: connector::Handle) -> Result<(), Self::Error>;
|
||||
/// Returns the currently active [`Mode`](https://docs.rs/drm/0.3.4/drm/control/struct.Mode.html)
|
||||
/// of the underlying [`crtc`](https://docs.rs/drm/0.3.4/drm/control/crtc/index.html)
|
||||
/// if any.
|
||||
fn current_mode(&self) -> Option<Mode>;
|
||||
/// Returns the currently pending [`Mode`](https://docs.rs/drm/0.3.4/drm/control/struct.Mode.html)
|
||||
/// to be used after the next commit, if any.
|
||||
fn pending_mode(&self) -> Option<Mode>;
|
||||
/// Tries to set a new [`Mode`](https://docs.rs/drm/0.3.4/drm/control/struct.Mode.html)
|
||||
/// to be used after the next commit.
|
||||
///
|
||||
/// Fails if the mode is not compatible with the underlying
|
||||
/// [`crtc`](https://docs.rs/drm/0.3.4/drm/control/crtc/index.html) or any of the
|
||||
/// pending [`connector`](https://docs.rs/drm/0.3.4/drm/control/connector/index.html)s.
|
||||
///
|
||||
/// *Note*: Only on a [`RawSurface`](trait.RawSurface.html) you may directly trigger
|
||||
/// a [`commit`](trait.RawSurface.html#method.commit). Other `Surface`s provide their
|
||||
/// own methods that *may* trigger a commit, you will need to read their docs.
|
||||
fn use_mode(&self, mode: Option<Mode>) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
/// An open bare crtc without any rendering abstractions
|
||||
pub trait RawSurface: Surface + ControlDevice + BasicDevice {
|
||||
/// Returns true whenever any state changes are pending to be commited
|
||||
///
|
||||
/// The following functions may trigger a pending commit:
|
||||
/// - [`add_connector`](trait.Surface.html#method.add_connector)
|
||||
/// - [`remove_connector`](trait.Surface.html#method.remove_connector)
|
||||
/// - [`use_mode`](trait.Surface.html#method.use_mode)
|
||||
fn commit_pending(&self) -> bool;
|
||||
/// Commit the pending state rendering a given framebuffer.
|
||||
///
|
||||
/// *Note*: This will trigger a full modeset on the underlying device,
|
||||
/// potentially causing some flickering. Check before performing this
|
||||
/// operation if a commit really is necessary using [`commit_pending`](#method.commit_pending).
|
||||
///
|
||||
/// This operation is blocking until the crtc is in the desired state.
|
||||
fn commit(&self, framebuffer: framebuffer::Handle) -> Result<(), <Self as Surface>::Error>;
|
||||
/// Page-flip the underlying [`crtc`](https://docs.rs/drm/0.3.4/drm/control/crtc/index.html)
|
||||
/// to a new given [`framebuffer`].
|
||||
///
|
||||
/// This will not cause the crtc to modeset.
|
||||
///
|
||||
/// This operation is not blocking and will produce a `vblank` event once swapping is done.
|
||||
/// Make sure to [set a `DeviceHandler`](trait.Device.html#method.set_handler) and
|
||||
/// [register the belonging `Device`](fn.device_bind.html) before to receive the event in time.
|
||||
fn page_flip(&self, framebuffer: framebuffer::Handle) -> Result<(), SwapBuffersError>;
|
||||
}
|
||||
|
||||
/// Trait representing open devices that *may* return a `Path`
|
||||
pub trait DevPath {
|
||||
/// Returns the path of the open device if possible
|
||||
fn dev_path(&self) -> Option<PathBuf>;
|
||||
|
@ -463,243 +225,22 @@ impl<A: AsRawFd> DevPath for A {
|
|||
}
|
||||
}
|
||||
|
||||
impl<A: ControlDevice + 'static> PartialEq for DrmDevice<A> {
|
||||
fn eq(&self, other: &DrmDevice<A>) -> bool {
|
||||
self.device_id == other.device_id
|
||||
}
|
||||
}
|
||||
impl<A: ControlDevice + 'static> Eq for DrmDevice<A> {}
|
||||
|
||||
impl<A: ControlDevice + 'static> Hash for DrmDevice<A> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.device_id.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
// for users convenience and FdEventSource registering
|
||||
impl<A: ControlDevice + 'static> AsRawFd for DrmDevice<A> {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.context.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: ControlDevice + 'static> BasicDevice for DrmDevice<A> {}
|
||||
impl<A: ControlDevice + 'static> ControlDevice for DrmDevice<A> {}
|
||||
|
||||
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) {
|
||||
if Rc::strong_count(&self.context) > 1 {
|
||||
panic!("Pending DrmBackends. You need to free all backends before the DrmDevice gets destroyed");
|
||||
}
|
||||
for (handle, (info, connectors)) in self.old_state.drain() {
|
||||
if let Err(err) = crtc::set(
|
||||
&*self.context,
|
||||
handle,
|
||||
info.fb(),
|
||||
&connectors,
|
||||
info.position(),
|
||||
info.mode(),
|
||||
) {
|
||||
error!(self.logger, "Failed to reset crtc ({:?}). Error: {}", handle, err);
|
||||
}
|
||||
}
|
||||
if self.privileged {
|
||||
if let Err(err) = self.drop_master() {
|
||||
error!(self.logger, "Failed to drop drm master state. Error: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Handler for DRM node events
|
||||
/// Bind a `Device` to an `EventLoop`,
|
||||
///
|
||||
/// See module-level documentation for its use
|
||||
pub trait DrmHandler<A: ControlDevice + 'static> {
|
||||
/// The `DrmBackend` of crtc has finished swapping buffers and new frame can now
|
||||
/// (and should be immediately) be rendered.
|
||||
fn ready(&mut self, device: &mut DrmDevice<A>, crtc: crtc::Handle, frame: u32, duration: Duration);
|
||||
/// The `DrmDevice` has thrown an error.
|
||||
///
|
||||
/// The related backends are most likely *not* usable anymore and
|
||||
/// the whole stack has to be recreated..
|
||||
fn error(&mut self, device: &mut DrmDevice<A>, error: DrmError);
|
||||
}
|
||||
|
||||
/// Bind a `DrmDevice` to an `EventLoop`,
|
||||
///
|
||||
/// This will cause it to recieve events and feed them into an `DrmHandler`
|
||||
pub fn drm_device_bind<A, H, Data: 'static>(
|
||||
/// This will cause it to recieve events and feed them into a previously
|
||||
/// set [`DeviceHandler`](trait.DeviceHandler.html).
|
||||
pub fn device_bind<D: Device + 'static, Data>(
|
||||
handle: &LoopHandle<Data>,
|
||||
device: DrmDevice<A>,
|
||||
mut handler: H,
|
||||
) -> ::std::result::Result<(Source<Generic<EventedRawFd>>, Rc<RefCell<DrmDevice<A>>>), (IoError, DrmDevice<A>)>
|
||||
device: D,
|
||||
) -> ::std::result::Result<Source<Generic<EventedFd<D>>>, InsertError<Generic<EventedFd<D>>>>
|
||||
where
|
||||
A: ControlDevice + 'static,
|
||||
H: DrmHandler<A> + 'static,
|
||||
D: Device,
|
||||
Data: 'static,
|
||||
{
|
||||
let fd = device.as_raw_fd();
|
||||
let device = Rc::new(RefCell::new(device));
|
||||
|
||||
let mut source = Generic::from_raw_fd(fd);
|
||||
let mut source = Generic::from_fd_source(device);
|
||||
source.set_interest(Ready::readable());
|
||||
|
||||
match handle.insert_source(source, {
|
||||
let device = device.clone();
|
||||
move |_evt, _| {
|
||||
let mut device = device.borrow_mut();
|
||||
process_events(&mut *device, &mut handler);
|
||||
}
|
||||
}) {
|
||||
Ok(source) => Ok((source, device)),
|
||||
Err(e) => {
|
||||
let device = Rc::try_unwrap(device).unwrap_or_else(|_| unreachable!());
|
||||
Err((e.into(), device.into_inner()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn process_events<A, H>(device: &mut DrmDevice<A>, handler: &mut H)
|
||||
where
|
||||
A: ControlDevice + 'static,
|
||||
H: DrmHandler<A> + 'static,
|
||||
{
|
||||
match crtc::receive_events(&*device) {
|
||||
Ok(events) => for event in events {
|
||||
if let crtc::Event::PageFlip(event) = event {
|
||||
if device.active.load(Ordering::SeqCst) {
|
||||
let backends = device.backends.borrow().clone();
|
||||
if let Some(backend) = backends.get(&event.crtc).iter().flat_map(|x| x.upgrade()).next() {
|
||||
// we can now unlock the buffer
|
||||
backend.unlock_buffer();
|
||||
trace!(device.logger, "Handling event for backend {:?}", event.crtc);
|
||||
// and then call the user to render the next frame
|
||||
handler.ready(device, event.crtc, event.frame, event.duration);
|
||||
} else {
|
||||
device.backends.borrow_mut().remove(&event.crtc);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(err) => handler.error(device, err),
|
||||
}
|
||||
}
|
||||
|
||||
/// `SessionObserver` linked to the `DrmDevice` it was created from.
|
||||
pub struct DrmDeviceObserver<A: ControlDevice + 'static> {
|
||||
context: Weak<EGLContext<Gbm<framebuffer::Info>, GbmDevice<A>>>,
|
||||
device_id: dev_t,
|
||||
backends: Rc<RefCell<HashMap<crtc::Handle, Weak<DrmBackendInternal<A>>>>>,
|
||||
old_state: HashMap<crtc::Handle, (crtc::Info, Vec<connector::Handle>)>,
|
||||
active: Arc<AtomicBool>,
|
||||
privileged: bool,
|
||||
logger: ::slog::Logger,
|
||||
}
|
||||
|
||||
#[cfg(feature = "backend_session")]
|
||||
impl<A: ControlDevice + 'static> AsSessionObserver<DrmDeviceObserver<A>> for DrmDevice<A> {
|
||||
fn observer(&mut self) -> DrmDeviceObserver<A> {
|
||||
DrmDeviceObserver {
|
||||
context: Rc::downgrade(&self.context),
|
||||
device_id: self.device_id,
|
||||
backends: self.backends.clone(),
|
||||
old_state: self.old_state.clone(),
|
||||
active: self.active.clone(),
|
||||
privileged: self.privileged,
|
||||
logger: self.logger.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "backend_session")]
|
||||
impl<A: ControlDevice + 'static> SessionObserver for DrmDeviceObserver<A> {
|
||||
fn pause(&mut self, devnum: Option<(u32, u32)>) {
|
||||
if let Some((major, minor)) = devnum {
|
||||
if major as u64 != stat::major(self.device_id) || minor as u64 != stat::minor(self.device_id) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if let Some(device) = self.context.upgrade() {
|
||||
for (handle, &(ref info, ref connectors)) in &self.old_state {
|
||||
if let Err(err) = crtc::set(
|
||||
&*device,
|
||||
*handle,
|
||||
info.fb(),
|
||||
connectors,
|
||||
info.position(),
|
||||
info.mode(),
|
||||
) {
|
||||
error!(self.logger, "Failed to reset crtc ({:?}). Error: {}", handle, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.active.store(false, Ordering::SeqCst);
|
||||
if self.privileged {
|
||||
if let Some(device) = self.context.upgrade() {
|
||||
if let Err(err) = device.drop_master() {
|
||||
error!(self.logger, "Failed to drop drm master state. Error: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn activate(&mut self, devnum: Option<(u32, u32, Option<RawFd>)>) {
|
||||
if let Some((major, minor, fd)) = devnum {
|
||||
if major as u64 != stat::major(self.device_id) || minor as u64 != stat::minor(self.device_id) {
|
||||
return;
|
||||
} else if let Some(fd) = fd {
|
||||
info!(self.logger, "Replacing fd");
|
||||
if let Some(device) = self.context.upgrade() {
|
||||
nix::unistd::dup2(device.as_raw_fd(), fd)
|
||||
.expect("Failed to replace file descriptor of drm device");
|
||||
}
|
||||
}
|
||||
}
|
||||
self.active.store(true, Ordering::SeqCst);
|
||||
if self.privileged {
|
||||
if let Some(device) = self.context.upgrade() {
|
||||
if let Err(err) = device.set_master() {
|
||||
crit!(self.logger, "Failed to acquire drm master again. Error: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut crtcs = Vec::new();
|
||||
for (crtc, backend) in self.backends.borrow().iter() {
|
||||
if let Some(backend) = backend.upgrade() {
|
||||
backend.unlock_buffer();
|
||||
if let Err(err) = backend.page_flip(None) {
|
||||
error!(
|
||||
self.logger,
|
||||
"Failed to activate crtc ({:?}) again. Error: {}", crtc, err
|
||||
);
|
||||
}
|
||||
// reset cursor
|
||||
{
|
||||
let &(ref cursor, ref hotspot): &(BufferObject<()>, (u32, u32)) =
|
||||
unsafe { &*backend.cursor.as_ptr() };
|
||||
if crtc::set_cursor2(
|
||||
&*backend.context,
|
||||
*crtc,
|
||||
cursor,
|
||||
((*hotspot).0 as i32, (*hotspot).1 as i32),
|
||||
).is_err()
|
||||
{
|
||||
if let Err(err) = crtc::set_cursor(&*backend.context, *crtc, cursor) {
|
||||
error!(self.logger, "Failed to reset cursor. Error: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
crtcs.push(*crtc);
|
||||
}
|
||||
}
|
||||
for crtc in crtcs {
|
||||
self.backends.borrow_mut().remove(&crtc);
|
||||
}
|
||||
}
|
||||
handle.insert_source(source, |evt, _| {
|
||||
evt.source.borrow_mut().0.process_events();
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,52 +1,30 @@
|
|||
//! EGL context related structs
|
||||
|
||||
use super::{error::*, ffi, native, EGLSurface, PixelFormat};
|
||||
#[cfg(feature = "backend_drm")]
|
||||
use drm::control::Device as ControlDevice;
|
||||
#[cfg(feature = "backend_drm")]
|
||||
use drm::Device as BasicDevice;
|
||||
#[cfg(feature = "backend_drm")]
|
||||
use gbm::Device as GbmDevice;
|
||||
use super::{error::*, ffi, native, EGLSurface};
|
||||
use backend::graphics::PixelFormat;
|
||||
use nix::libc::{c_int, c_void};
|
||||
use slog;
|
||||
#[cfg(feature = "backend_drm")]
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::{
|
||||
cell::{Ref, RefCell, RefMut},
|
||||
ffi::{CStr, CString},
|
||||
marker::PhantomData,
|
||||
mem,
|
||||
ops::{Deref, DerefMut},
|
||||
ptr,
|
||||
mem, ptr,
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
/// EGL context for rendering
|
||||
pub struct EGLContext<B: native::Backend, N: native::NativeDisplay<B>> {
|
||||
native: N,
|
||||
native: RefCell<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>(
|
||||
|
@ -60,25 +38,17 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLContext<B, N> {
|
|||
{
|
||||
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()) }?;
|
||||
let (context, display, config_id, surface_attributes, pixel_format, wl_drm_support) =
|
||||
unsafe { EGLContext::<B, N>::new_internal(ptr, attributes, reqs, log.clone()) }?;
|
||||
|
||||
Ok(EGLContext {
|
||||
native,
|
||||
native: RefCell::new(native),
|
||||
context,
|
||||
display,
|
||||
config_id,
|
||||
surface_attributes,
|
||||
pixel_format,
|
||||
wl_drm_support,
|
||||
egl_to_texture_support,
|
||||
logger: log,
|
||||
_backend: PhantomData,
|
||||
})
|
||||
|
@ -96,7 +66,6 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLContext<B, N> {
|
|||
Vec<c_int>,
|
||||
PixelFormat,
|
||||
bool,
|
||||
bool,
|
||||
)> {
|
||||
// If no version is given, try OpenGLES 3.0, if available,
|
||||
// fallback to 2.0 otherwise
|
||||
|
@ -136,6 +105,7 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLContext<B, N> {
|
|||
{
|
||||
f
|
||||
};
|
||||
|
||||
ffi::egl::load_with(|sym| {
|
||||
let name = CString::new(sym).unwrap();
|
||||
let symbol = ffi::egl::LIB.get::<*mut c_void>(name.as_bytes());
|
||||
|
@ -153,7 +123,6 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLContext<B, N> {
|
|||
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
|
||||
|
@ -435,17 +404,6 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLContext<B, N> {
|
|||
// 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 _),
|
||||
|
@ -453,25 +411,21 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLContext<B, N> {
|
|||
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
|
||||
let surface = self
|
||||
.native
|
||||
.borrow_mut()
|
||||
.create_surface(args)
|
||||
.chain_err(|| ErrorKind::SurfaceCreationFailed)?,
|
||||
);
|
||||
if res.is_ok() {
|
||||
.chain_err(|| ErrorKind::SurfaceCreationFailed)?;
|
||||
EGLSurface::new(self, surface).map(|x| {
|
||||
debug!(self.logger, "EGL surface successfully created");
|
||||
}
|
||||
res
|
||||
x
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the address of an OpenGL function.
|
||||
|
@ -492,6 +446,25 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLContext<B, N> {
|
|||
pub fn get_pixel_format(&self) -> PixelFormat {
|
||||
self.pixel_format
|
||||
}
|
||||
|
||||
/// Borrow the underlying native display.
|
||||
///
|
||||
/// This follows the same semantics as `std::cell:RefCell`.
|
||||
/// Multiple read-only borrows are possible. Borrowing the
|
||||
/// backend while there is a mutable reference will panic.
|
||||
pub fn borrow(&self) -> Ref<N> {
|
||||
self.native.borrow()
|
||||
}
|
||||
|
||||
/// Borrow the underlying native display mutably.
|
||||
///
|
||||
/// This follows the same semantics as `std::cell:RefCell`.
|
||||
/// Holding any other borrow while trying to borrow the backend
|
||||
/// mutably will panic. Note that EGL will borrow the display
|
||||
/// mutably during surface creation.
|
||||
pub fn borrow_mut(&self) -> RefMut<N> {
|
||||
self.native.borrow_mut()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<B: native::Backend, N: native::NativeDisplay<B> + Send> Send for EGLContext<B, N> {}
|
||||
|
@ -508,18 +481,6 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> Drop for EGLContext<B, N>
|
|||
}
|
||||
}
|
||||
|
||||
#[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 {
|
|
@ -13,11 +13,6 @@ 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::*;
|
|
@ -1,3 +1,10 @@
|
|||
//! 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)
|
||||
//! Wayland specific EGL functionality - EGL based `WlBuffer`s.
|
||||
//!
|
||||
//! The types of this module can be used to initialize hardware acceleration rendering
|
||||
|
@ -10,13 +17,11 @@
|
|||
//! You may then use the resulting `EGLDisplay` to receive `EGLImages` of an EGL-based `WlBuffer`
|
||||
//! for rendering.
|
||||
|
||||
use backend::graphics::egl::{
|
||||
error::*,
|
||||
ffi::{self, egl::types::EGLImage},
|
||||
native, EGLContext, EglExtensionNotSupportedError,
|
||||
};
|
||||
#[cfg(feature = "renderer_gl")]
|
||||
use backend::graphics::gl::ffi as gl_ffi;
|
||||
use nix::libc::c_uint;
|
||||
use std::{
|
||||
ffi::CStr,
|
||||
fmt,
|
||||
rc::{Rc, Weak},
|
||||
};
|
||||
|
@ -24,8 +29,47 @@ use wayland_server::{
|
|||
protocol::wl_buffer::{self, WlBuffer},
|
||||
Display, Resource,
|
||||
};
|
||||
#[cfg(feature = "native_lib")]
|
||||
use wayland_sys::server::wl_display;
|
||||
|
||||
pub mod context;
|
||||
pub use self::context::EGLContext;
|
||||
pub mod error;
|
||||
use self::error::*;
|
||||
|
||||
#[allow(non_camel_case_types, dead_code, unused_mut, non_upper_case_globals)]
|
||||
pub mod ffi;
|
||||
use self::ffi::egl::types::EGLImage;
|
||||
|
||||
pub mod native;
|
||||
pub mod surface;
|
||||
pub use self::surface::EGLSurface;
|
||||
|
||||
/// 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
|
||||
}
|
||||
}
|
||||
|
||||
/// Error that can occur when accessing an EGL buffer
|
||||
pub enum BufferAccessError {
|
||||
/// The corresponding Context is not alive anymore
|
||||
|
@ -104,6 +148,8 @@ pub enum TextureCreationError {
|
|||
/// application on sleep and wakes it up later. However any OpenGL implementation
|
||||
/// can theoretically lose the context at any time.
|
||||
ContextLost,
|
||||
/// Required OpenGL Extension for texture creation is missing
|
||||
GLExtensionNotSupported(&'static str),
|
||||
/// Failed to bind the `EGLImage` to the given texture
|
||||
///
|
||||
/// The given argument is the GL error code
|
||||
|
@ -116,6 +162,9 @@ impl fmt::Display for TextureCreationError {
|
|||
match *self {
|
||||
TextureCreationError::ContextLost => write!(formatter, "{}", self.description()),
|
||||
TextureCreationError::PlaneIndexOutOfBounds => write!(formatter, "{}", self.description()),
|
||||
TextureCreationError::GLExtensionNotSupported(ext) => {
|
||||
write!(formatter, "{}: {:}", self.description(), ext)
|
||||
}
|
||||
TextureCreationError::TextureBindingFailed(code) => {
|
||||
write!(formatter, "{}. Gl error code: {:?}", self.description(), code)
|
||||
}
|
||||
|
@ -128,6 +177,9 @@ impl ::std::error::Error for TextureCreationError {
|
|||
match *self {
|
||||
TextureCreationError::ContextLost => "The context has been lost, it needs to be recreated",
|
||||
TextureCreationError::PlaneIndexOutOfBounds => "This buffer is not managed by EGL",
|
||||
TextureCreationError::GLExtensionNotSupported(_) => {
|
||||
"Required OpenGL Extension for texture creation is missing"
|
||||
}
|
||||
TextureCreationError::TextureBindingFailed(_) => "Failed to create EGLImages from the buffer",
|
||||
}
|
||||
}
|
||||
|
@ -180,6 +232,10 @@ pub struct EGLImages {
|
|||
pub format: Format,
|
||||
images: Vec<EGLImage>,
|
||||
buffer: Resource<WlBuffer>,
|
||||
#[cfg(feature = "renderer_gl")]
|
||||
gl: gl_ffi::Gles2,
|
||||
#[cfg(feature = "renderer_gl")]
|
||||
egl_to_texture_support: bool,
|
||||
}
|
||||
|
||||
impl EGLImages {
|
||||
|
@ -195,17 +251,22 @@ impl EGLImages {
|
|||
/// # Unsafety
|
||||
///
|
||||
/// The given `tex_id` needs to be a valid GL texture otherwise undefined behavior might occur.
|
||||
#[cfg(feature = "renderer_gl")]
|
||||
pub unsafe fn bind_to_texture(
|
||||
&self,
|
||||
plane: usize,
|
||||
tex_id: c_uint,
|
||||
) -> ::std::result::Result<(), TextureCreationError> {
|
||||
if self.display.upgrade().is_some() {
|
||||
if !self.egl_to_texture_support {
|
||||
return Err(TextureCreationError::GLExtensionNotSupported("GL_OES_EGL_image"));
|
||||
}
|
||||
|
||||
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.gl.GetIntegerv(gl_ffi::TEXTURE_BINDING_2D, &mut old_tex_id);
|
||||
self.gl.BindTexture(gl_ffi::TEXTURE_2D, tex_id);
|
||||
self.gl.EGLImageTargetTexture2DOES(
|
||||
gl_ffi::TEXTURE_2D,
|
||||
*self
|
||||
.images
|
||||
.get(plane)
|
||||
|
@ -215,7 +276,7 @@ impl EGLImages {
|
|||
ffi::egl::SUCCESS => Ok(()),
|
||||
err => Err(TextureCreationError::TextureBindingFailed(err)),
|
||||
};
|
||||
ffi::gl::BindTexture(ffi::gl::TEXTURE_2D, old_tex_id as u32);
|
||||
self.gl.BindTexture(gl_ffi::TEXTURE_2D, old_tex_id as u32);
|
||||
res
|
||||
} else {
|
||||
Err(TextureCreationError::ContextLost)
|
||||
|
@ -238,7 +299,8 @@ impl Drop for EGLImages {
|
|||
|
||||
/// 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 {
|
||||
#[cfg(feature = "native_lib")]
|
||||
pub trait EGLGraphicsBackend {
|
||||
/// Binds this EGL context to the given Wayland display.
|
||||
///
|
||||
/// This will allow clients to utilize EGL to create hardware-accelerated
|
||||
|
@ -257,15 +319,42 @@ pub trait EGLWaylandExtensions {
|
|||
|
||||
/// Type to receive `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);
|
||||
/// Can be created by using `EGLGraphicsBackend::bind_wl_display`.
|
||||
#[cfg(feature = "native_lib")]
|
||||
pub struct EGLDisplay {
|
||||
egl: Weak<ffi::egl::types::EGLDisplay>,
|
||||
wayland: *mut wl_display,
|
||||
#[cfg(feature = "renderer_gl")]
|
||||
gl: gl_ffi::Gles2,
|
||||
#[cfg(feature = "renderer_gl")]
|
||||
egl_to_texture_support: bool,
|
||||
}
|
||||
|
||||
#[cfg(feature = "native_lib")]
|
||||
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)
|
||||
#[cfg(feature = "renderer_gl")]
|
||||
let gl = gl_ffi::Gles2::load_with(|s| unsafe { context.get_proc_address(s) as *const _ });
|
||||
|
||||
EGLDisplay {
|
||||
egl: Rc::downgrade(&context.display),
|
||||
wayland: display,
|
||||
#[cfg(feature = "renderer_gl")]
|
||||
egl_to_texture_support: {
|
||||
// the list of gl extensions supported by the context
|
||||
let data = unsafe { CStr::from_ptr(gl.GetString(gl_ffi::EXTENSIONS) as *const _) }
|
||||
.to_bytes()
|
||||
.to_vec();
|
||||
let list = String::from_utf8(data).unwrap();
|
||||
list.split(' ')
|
||||
.any(|s| s == "GL_OES_EGL_image" || s == "GL_OES_EGL_image_base")
|
||||
},
|
||||
#[cfg(feature = "renderer_gl")]
|
||||
gl,
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to receive `EGLImages` from a given `WlBuffer`.
|
||||
|
@ -277,7 +366,7 @@ impl EGLDisplay {
|
|||
&self,
|
||||
buffer: Resource<WlBuffer>,
|
||||
) -> ::std::result::Result<EGLImages, BufferAccessError> {
|
||||
if let Some(display) = self.0.upgrade() {
|
||||
if let Some(display) = self.egl.upgrade() {
|
||||
let mut format: i32 = 0;
|
||||
if unsafe {
|
||||
ffi::egl::QueryWaylandBufferWL(
|
||||
|
@ -368,6 +457,10 @@ impl EGLDisplay {
|
|||
format,
|
||||
images,
|
||||
buffer,
|
||||
#[cfg(feature = "renderer_gl")]
|
||||
gl: self.gl.clone(),
|
||||
#[cfg(feature = "renderer_gl")]
|
||||
egl_to_texture_support: self.egl_to_texture_support,
|
||||
})
|
||||
} else {
|
||||
Err(BufferAccessError::ContextLost)
|
||||
|
@ -375,34 +468,34 @@ impl EGLDisplay {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "native_lib")]
|
||||
impl Drop for EGLDisplay {
|
||||
fn drop(&mut self) {
|
||||
if let Some(display) = self.0.upgrade() {
|
||||
if !self.1.is_null() {
|
||||
if let Some(display) = self.egl.upgrade() {
|
||||
if !self.wayland.is_null() {
|
||||
unsafe {
|
||||
ffi::egl::UnbindWaylandDisplayWL(*display, self.1 as *mut _);
|
||||
ffi::egl::UnbindWaylandDisplayWL(*display, self.wayland as *mut _);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EGLWaylandExtensions> EGLWaylandExtensions for Rc<E> {
|
||||
#[cfg(feature = "native_lib")]
|
||||
impl<E: EGLGraphicsBackend> EGLGraphicsBackend 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> {
|
||||
#[cfg(feature = "native_lib")]
|
||||
impl<B: native::Backend, N: native::NativeDisplay<B>> EGLGraphicsBackend 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.c_ptr() as *mut _) };
|
||||
if res == 0 {
|
||||
bail!(ErrorKind::OtherEGLDisplayAlreadyBound);
|
|
@ -1,16 +1,11 @@
|
|||
//! Type safe native types for safe context/surface creation
|
||||
|
||||
use super::{error::*, 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 backend::graphics::SwapBuffersError;
|
||||
|
||||
#[cfg(feature = "backend_winit")]
|
||||
use std::ptr;
|
||||
|
||||
#[cfg(feature = "backend_winit")]
|
||||
use wayland_client::egl as wegl;
|
||||
#[cfg(feature = "backend_winit")]
|
||||
|
@ -95,38 +90,6 @@ impl Backend for X11 {
|
|||
}
|
||||
}
|
||||
}
|
||||
#[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
|
||||
///
|
||||
|
@ -144,7 +107,7 @@ pub unsafe trait NativeDisplay<B: Backend> {
|
|||
/// 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>;
|
||||
fn create_surface(&mut self, args: Self::Arguments) -> ::std::result::Result<B::Surface, Self::Error>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "backend_winit")]
|
||||
|
@ -162,7 +125,7 @@ unsafe impl NativeDisplay<X11> for WinitWindow {
|
|||
.ok_or(ErrorKind::NonMatchingBackend("X11").into())
|
||||
}
|
||||
|
||||
fn create_surface(&self, _args: ()) -> Result<XlibWindow> {
|
||||
fn create_surface(&mut self, _args: ()) -> Result<XlibWindow> {
|
||||
self.get_xlib_window()
|
||||
.map(XlibWindow)
|
||||
.ok_or(ErrorKind::NonMatchingBackend("X11").into())
|
||||
|
@ -184,7 +147,7 @@ unsafe impl NativeDisplay<Wayland> for WinitWindow {
|
|||
.ok_or(ErrorKind::NonMatchingBackend("Wayland").into())
|
||||
}
|
||||
|
||||
fn create_surface(&self, _args: ()) -> Result<wegl::WlEglSurface> {
|
||||
fn create_surface(&mut self, _args: ()) -> Result<wegl::WlEglSurface> {
|
||||
if let Some(surface) = self.get_wayland_surface() {
|
||||
let size = self.get_inner_size().unwrap();
|
||||
Ok(unsafe {
|
||||
|
@ -196,41 +159,7 @@ unsafe impl NativeDisplay<Wayland> for WinitWindow {
|
|||
}
|
||||
}
|
||||
|
||||
#[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
|
||||
/// Trait for types returning valid surface pointers for initializing egl
|
||||
///
|
||||
/// ## Unsafety
|
||||
///
|
||||
|
@ -238,6 +167,32 @@ unsafe impl<A: AsRawFd + 'static, T: 'static> NativeDisplay<Gbm<T>> for GbmDevic
|
|||
pub unsafe trait NativeSurface {
|
||||
/// Return a raw pointer egl will accept for surface creation.
|
||||
fn ptr(&self) -> ffi::NativeWindowType;
|
||||
|
||||
/// Will be called to check if any internal resources will need
|
||||
/// to be recreated. Old resources must be used until `recreate`
|
||||
/// was called.
|
||||
///
|
||||
/// Only needs to be recreated, if this shall sometimes return true.
|
||||
/// The default implementation always returns false.
|
||||
fn needs_recreation(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Instructs the surface to recreate internal resources
|
||||
///
|
||||
/// Must only be implemented if `needs_recreation` can return `true`.
|
||||
/// Returns true on success.
|
||||
/// If this call was successful `ptr()` *should* return something different.
|
||||
fn recreate(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Adds additional semantics when calling EGLSurface::swap_buffers
|
||||
///
|
||||
/// Only implement if required by the backend.
|
||||
fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "backend_winit")]
|
||||
|
@ -253,10 +208,3 @@ unsafe impl NativeSurface for wegl::WlEglSurface {
|
|||
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 _
|
||||
}
|
||||
}
|
|
@ -1,7 +1,10 @@
|
|||
//! EGL surface related structs
|
||||
|
||||
use super::{error::*, ffi, native, EGLContext, SwapBuffersError};
|
||||
use super::{error::*, ffi, native, EGLContext};
|
||||
use backend::graphics::SwapBuffersError;
|
||||
use nix::libc::c_int;
|
||||
use std::{
|
||||
cell::Cell,
|
||||
ops::{Deref, DerefMut},
|
||||
rc::{Rc, Weak},
|
||||
};
|
||||
|
@ -11,7 +14,9 @@ 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,
|
||||
surface: Cell<ffi::egl::types::EGLSurface>,
|
||||
config_id: ffi::egl::types::EGLConfig,
|
||||
surface_attributes: Vec<c_int>,
|
||||
}
|
||||
|
||||
impl<N: native::NativeSurface> Deref for EGLSurface<N> {
|
||||
|
@ -49,27 +54,49 @@ impl<N: native::NativeSurface> EGLSurface<N> {
|
|||
context: Rc::downgrade(&context.context),
|
||||
display: Rc::downgrade(&context.display),
|
||||
native,
|
||||
surface,
|
||||
surface: Cell::new(surface),
|
||||
config_id: context.config_id,
|
||||
surface_attributes: context.surface_attributes.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Swaps buffers at the end of a frame.
|
||||
pub fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
|
||||
let surface = self.surface.get();
|
||||
|
||||
if !surface.is_null() {
|
||||
if let Some(display) = self.display.upgrade() {
|
||||
let ret = unsafe { ffi::egl::SwapBuffers((*display) as *const _, self.surface as *const _) };
|
||||
let ret = unsafe { ffi::egl::SwapBuffers((*display) as *const _, 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)),
|
||||
ffi::egl::CONTEXT_LOST => return Err(SwapBuffersError::ContextLost),
|
||||
err => return Err(SwapBuffersError::Unknown(err)),
|
||||
};
|
||||
} else {
|
||||
self.native.swap_buffers()?;
|
||||
}
|
||||
} else {
|
||||
return Err(SwapBuffersError::ContextLost);
|
||||
}
|
||||
};
|
||||
|
||||
if self.native.needs_recreation() || surface.is_null() {
|
||||
if let Some(display) = self.display.upgrade() {
|
||||
self.native.recreate();
|
||||
self.surface.set(unsafe {
|
||||
ffi::egl::CreateWindowSurface(
|
||||
*display,
|
||||
self.config_id,
|
||||
self.native.ptr(),
|
||||
self.surface_attributes.as_ptr(),
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
} else {
|
||||
Err(SwapBuffersError::ContextLost)
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes the OpenGL context the current context in the current thread.
|
||||
///
|
||||
|
@ -81,8 +108,8 @@ impl<N: native::NativeSurface> EGLSurface<N> {
|
|||
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 _,
|
||||
self.surface.get() as *const _,
|
||||
self.surface.get() as *const _,
|
||||
(*context) as *const _,
|
||||
);
|
||||
|
||||
|
@ -103,8 +130,8 @@ impl<N: native::NativeSurface> EGLSurface<N> {
|
|||
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 _
|
||||
ffi::egl::GetCurrentSurface(ffi::egl::DRAW as _) == self.surface.get() as *const _
|
||||
&& ffi::egl::GetCurrentSurface(ffi::egl::READ as _) == self.surface.get() as *const _
|
||||
}
|
||||
} else {
|
||||
false
|
||||
|
@ -116,7 +143,7 @@ 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 _);
|
||||
ffi::egl::DestroySurface((*display) as *const _, self.surface.get() as *const _);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/// Functions to render cursors on any graphics backend independently from it's rendering techique.
|
||||
pub trait CursorBackend<'a> {
|
||||
/// Format representing the image drawn for the cursor.
|
||||
type CursorFormat: 'a;
|
||||
|
||||
/// Error the underlying backend throws if operations fail
|
||||
type Error;
|
||||
|
||||
/// Sets the cursor position and therefore updates the drawn cursors position.
|
||||
/// Useful as well for e.g. pointer wrapping.
|
||||
///
|
||||
/// Not guaranteed to be supported on every backend. The result usually
|
||||
/// depends on the backend, the cursor might be "owned" by another more priviledged
|
||||
/// compositor (running nested).
|
||||
///
|
||||
/// In these cases setting the position is actually not required, as movement is done
|
||||
/// by the higher compositor and not by the backend. It is still good practice to update
|
||||
/// the position after every recieved event, but don't rely on pointer wrapping working.
|
||||
///
|
||||
fn set_cursor_position(&self, x: u32, y: u32) -> Result<(), Self::Error>;
|
||||
|
||||
/// Set the cursor drawn on the `CursorBackend`.
|
||||
///
|
||||
/// The format is entirely dictated by the concrete implementation and might range
|
||||
/// from raw image buffers over a fixed list of possible cursor types to simply the
|
||||
/// void type () to represent no possible customization of the cursor itself.
|
||||
fn set_cursor_representation<'b>(
|
||||
&'b self,
|
||||
cursor: Self::CursorFormat,
|
||||
hotspot: (u32, u32),
|
||||
) -> Result<(), Self::Error>
|
||||
where
|
||||
'a: 'b;
|
||||
}
|
|
@ -1,155 +0,0 @@
|
|||
//! 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 to 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,45 @@
|
|||
use std::error::Error;
|
||||
use std::fmt;
|
||||
|
||||
/// Error that can happen when swapping buffers.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum SwapBuffersError {
|
||||
/// The corresponding 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.
|
||||
/// will return uninitialized data instead.
|
||||
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 error
|
||||
Unknown(u32),
|
||||
}
|
||||
|
||||
impl fmt::Display for SwapBuffersError {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
use std::error::Error;
|
||||
write!(formatter, "{}", self.description())
|
||||
}
|
||||
}
|
||||
|
||||
impl 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 error occurred",
|
||||
}
|
||||
}
|
||||
|
||||
fn cause(&self) -> Option<&Error> {
|
||||
None
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/// Describes the pixel format of a 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,
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
//! Gl rendering types
|
||||
|
||||
use nix::libc::c_void;
|
||||
|
||||
use super::{PixelFormat, SwapBuffersError};
|
||||
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(clippy))]
|
||||
#[allow(missing_docs)]
|
||||
pub(crate) mod ffi {
|
||||
include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs"));
|
||||
}
|
||||
|
||||
pub use self::ffi::Gles2;
|
||||
|
||||
/// Trait that describes objects that have an OpenGL context
|
||||
/// and can be used to render upon
|
||||
pub trait GLGraphicsBackend {
|
||||
/// Swaps buffers at the end of a frame.
|
||||
fn swap_buffers(&self) -> 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.
|
||||
///
|
||||
/// These are the actual pixels of the underlying graphics backend.
|
||||
/// For nested compositors you will need to handle the scaling
|
||||
/// of the root compositor yourself, if you want to.
|
||||
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) -> Result<(), SwapBuffersError>;
|
||||
|
||||
/// Returns the pixel format of the main framebuffer of the context.
|
||||
fn get_pixel_format(&self) -> PixelFormat;
|
||||
}
|
||||
|
||||
/// Loads a Raw GLES Interface for a given `GLGraphicsBackend`
|
||||
///
|
||||
/// This remains valid as long as the underlying `GLGraphicsBackend` is alive
|
||||
/// and may only be used in combination with the backend. Using this with any
|
||||
/// other gl context *may* cause undefined behavior.
|
||||
pub fn load_raw_gl<B: GLGraphicsBackend>(backend: &B) -> Gles2 {
|
||||
Gles2::load_with(|s| unsafe { backend.get_proc_address(s) as *const _ })
|
||||
}
|
|
@ -1,10 +1,6 @@
|
|||
//! Glium compatibility module
|
||||
|
||||
use backend::graphics::egl::{
|
||||
error::Result as EGLResult,
|
||||
wayland::{EGLDisplay, EGLWaylandExtensions},
|
||||
EGLGraphicsBackend, SwapBuffersError,
|
||||
};
|
||||
use backend::graphics::{gl::GLGraphicsBackend, SwapBuffersError};
|
||||
use glium::{
|
||||
backend::{Backend, Context, Facade},
|
||||
debug::DebugCallbackBehavior,
|
||||
|
@ -15,7 +11,6 @@ use std::{
|
|||
os::raw::c_void,
|
||||
rc::Rc,
|
||||
};
|
||||
use wayland_server::Display;
|
||||
|
||||
impl From<SwapBuffersError> for GliumSwapBuffersError {
|
||||
fn from(error: SwapBuffersError) -> Self {
|
||||
|
@ -28,14 +23,14 @@ impl From<SwapBuffersError> for GliumSwapBuffersError {
|
|||
}
|
||||
|
||||
/// Wrapper to expose `Glium` compatibility
|
||||
pub struct GliumGraphicsBackend<T: EGLGraphicsBackend> {
|
||||
pub struct GliumGraphicsBackend<T: GLGraphicsBackend> {
|
||||
context: Rc<Context>,
|
||||
backend: Rc<InternalBackend<T>>,
|
||||
}
|
||||
|
||||
struct InternalBackend<T: EGLGraphicsBackend>(RefCell<T>);
|
||||
struct InternalBackend<T: GLGraphicsBackend>(RefCell<T>);
|
||||
|
||||
impl<T: EGLGraphicsBackend + 'static> GliumGraphicsBackend<T> {
|
||||
impl<T: GLGraphicsBackend + 'static> GliumGraphicsBackend<T> {
|
||||
fn new(backend: T) -> GliumGraphicsBackend<T> {
|
||||
let internal = Rc::new(InternalBackend(RefCell::new(backend)));
|
||||
|
||||
|
@ -79,27 +74,19 @@ impl<T: EGLGraphicsBackend + 'static> GliumGraphicsBackend<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: EGLGraphicsBackend> Facade for GliumGraphicsBackend<T> {
|
||||
impl<T: GLGraphicsBackend> Facade for GliumGraphicsBackend<T> {
|
||||
fn get_context(&self) -> &Rc<Context> {
|
||||
&self.context
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EGLGraphicsBackend + 'static> From<T> for GliumGraphicsBackend<T> {
|
||||
impl<T: GLGraphicsBackend + 'static> From<T> for GliumGraphicsBackend<T> {
|
||||
fn from(backend: T) -> Self {
|
||||
GliumGraphicsBackend::new(backend)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EGLGraphicsBackend + EGLWaylandExtensions + 'static> EGLWaylandExtensions
|
||||
for GliumGraphicsBackend<T>
|
||||
{
|
||||
fn bind_wl_display(&self, display: &Display) -> EGLResult<EGLDisplay> {
|
||||
(*self.backend).0.borrow().bind_wl_display(display)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: EGLGraphicsBackend> Backend for InternalBackend<T> {
|
||||
unsafe impl<T: GLGraphicsBackend> Backend for InternalBackend<T> {
|
||||
fn swap_buffers(&self) -> Result<(), GliumSwapBuffersError> {
|
||||
self.0.borrow().swap_buffers().map_err(Into::into)
|
||||
}
|
||||
|
|
|
@ -2,40 +2,18 @@
|
|||
//!
|
||||
//! Note: Not every API may be supported by every backend
|
||||
|
||||
/// General functions any graphics backend should support independently from it's rendering
|
||||
/// technique.
|
||||
pub trait GraphicsBackend {
|
||||
/// Format representing the image drawn for the cursor.
|
||||
type CursorFormat;
|
||||
mod errors;
|
||||
pub use self::errors::*;
|
||||
|
||||
/// Error the underlying backend throws if operations fail
|
||||
type Error;
|
||||
mod cursor;
|
||||
pub use self::cursor::*;
|
||||
|
||||
/// Sets the cursor position and therefor updates the drawn cursors position.
|
||||
/// Useful as well for e.g. pointer wrapping.
|
||||
///
|
||||
/// Not guaranteed to be supported on every backend. The result usually
|
||||
/// depends on the backend, the cursor might be "owned" by another more privileged
|
||||
/// compositor (running nested).
|
||||
///
|
||||
/// In these cases setting the position is actually not required, as movement is done
|
||||
/// by the higher compositor and not by the backend. It is still good practice to update
|
||||
/// the position after every received event, but don't rely on pointer wrapping working.
|
||||
fn set_cursor_position(&self, x: u32, y: u32) -> Result<(), Self::Error>;
|
||||
mod format;
|
||||
pub use self::format::*;
|
||||
|
||||
/// Set the cursor drawn on the `GraphicsBackend`.
|
||||
///
|
||||
/// The format is entirely dictated by the concrete implementation and might range
|
||||
/// from raw image buffers over a fixed list of possible cursor types to simply the
|
||||
/// void type `()` to represent no possible customization of the cursor itself.
|
||||
fn set_cursor_representation(
|
||||
&self,
|
||||
cursor: &Self::CursorFormat,
|
||||
hotspot: (u32, u32),
|
||||
) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
pub mod egl;
|
||||
#[cfg(feature = "renderer_gl")]
|
||||
pub mod gl;
|
||||
#[cfg(feature = "renderer_glium")]
|
||||
pub mod glium;
|
||||
#[cfg(feature = "renderer_software")]
|
||||
pub mod software;
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
//! Common traits and types used for software rendering on graphics backends
|
||||
|
||||
use super::GraphicsBackend;
|
||||
use std::error::Error;
|
||||
use wayland_server::protocol::wl_shm::Format;
|
||||
|
||||
/// Trait that describes objects providing a software rendering implementation
|
||||
pub trait CpuGraphicsBackend<E: Error>: GraphicsBackend {
|
||||
pub trait CpuGraphicsBackend<E: Error> {
|
||||
/// Render a given buffer of a given format at a specified place in the framebuffer
|
||||
///
|
||||
/// # Error
|
||||
|
|
|
@ -6,26 +6,25 @@ use backend::{input as backend, input::Axis};
|
|||
use input as libinput;
|
||||
use input::event;
|
||||
|
||||
#[cfg(feature = "backend_session")]
|
||||
use std::path::Path;
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::hash_map::{DefaultHasher, Entry, HashMap},
|
||||
hash::{Hash, Hasher},
|
||||
io::Error as IoError,
|
||||
os::unix::io::RawFd,
|
||||
path::Path,
|
||||
rc::Rc,
|
||||
os::unix::io::{AsRawFd, RawFd},
|
||||
};
|
||||
|
||||
use wayland_server::calloop::{
|
||||
generic::{EventedRawFd, Generic},
|
||||
generic::{EventedFd, Generic},
|
||||
mio::Ready,
|
||||
LoopHandle, Source,
|
||||
InsertError, LoopHandle, Source,
|
||||
};
|
||||
|
||||
// No idea if this is the same across unix platforms
|
||||
// Lets make this linux exclusive for now, once someone tries to build it for
|
||||
// any BSD-like system, they can verify if this is right and make a PR to change this.
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[cfg(all(any(target_os = "linux", target_os = "android"), feature = "backend_session"))]
|
||||
const INPUT_MAJOR: u32 = 13;
|
||||
|
||||
/// Libinput based `InputBackend`.
|
||||
|
@ -446,7 +445,8 @@ impl backend::InputBackend for LibinputInputBackend {
|
|||
libinput::Event::Keyboard(keyboard_event) => {
|
||||
use input::event::keyboard::*;
|
||||
match keyboard_event {
|
||||
KeyboardEvent::Key(key_event) => if let Some(ref mut handler) = self.handler {
|
||||
KeyboardEvent::Key(key_event) => {
|
||||
if let Some(ref mut handler) = self.handler {
|
||||
let device_seat = key_event.device().seat();
|
||||
if let Some(ref seat) = self.seats.get(&device_seat) {
|
||||
trace!(self.logger, "Calling on_keyboard_key with {:?}", key_event);
|
||||
|
@ -455,7 +455,8 @@ impl backend::InputBackend for LibinputInputBackend {
|
|||
warn!(self.logger, "Received key event of non existing Seat");
|
||||
continue;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
libinput::Event::Pointer(pointer_event) => {
|
||||
|
@ -590,6 +591,12 @@ impl<S: Session> libinput::LibinputInterface for LibinputSessionInterface<S> {
|
|||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for LibinputInputBackend {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.context.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
/// Binds a `LibinputInputBackend` to a given `EventLoop`.
|
||||
///
|
||||
/// Automatically feeds the backend with incoming events without any manual calls to
|
||||
|
@ -597,22 +604,19 @@ impl<S: Session> libinput::LibinputInterface for LibinputSessionInterface<S> {
|
|||
pub fn libinput_bind<Data: 'static>(
|
||||
backend: LibinputInputBackend,
|
||||
handle: LoopHandle<Data>,
|
||||
) -> ::std::result::Result<Source<Generic<EventedRawFd>>, (IoError, LibinputInputBackend)> {
|
||||
let mut source = Generic::from_raw_fd(unsafe { backend.context.fd() });
|
||||
) -> ::std::result::Result<
|
||||
Source<Generic<EventedFd<LibinputInputBackend>>>,
|
||||
InsertError<Generic<EventedFd<LibinputInputBackend>>>,
|
||||
> {
|
||||
let mut source = Generic::from_fd_source(backend);
|
||||
source.set_interest(Ready::readable());
|
||||
let backend = Rc::new(RefCell::new(backend));
|
||||
let fail_backend = backend.clone();
|
||||
handle
|
||||
.insert_source(source, move |_, _| {
|
||||
|
||||
handle.insert_source(source, move |evt, _| {
|
||||
use backend::input::InputBackend;
|
||||
if let Err(error) = backend.borrow_mut().dispatch_new_events() {
|
||||
warn!(backend.borrow().logger, "Libinput errored: {}", error);
|
||||
|
||||
let mut backend = evt.source.borrow_mut();
|
||||
if let Err(error) = backend.0.dispatch_new_events() {
|
||||
warn!(backend.0.logger, "Libinput errored: {}", error);
|
||||
}
|
||||
}).map_err(move |e| {
|
||||
// the backend in the closure should already have been dropped
|
||||
let backend = Rc::try_unwrap(fail_backend)
|
||||
.unwrap_or_else(|_| unreachable!())
|
||||
.into_inner();
|
||||
(e.into(), backend)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ pub mod input;
|
|||
|
||||
#[cfg(feature = "backend_drm")]
|
||||
pub mod drm;
|
||||
#[cfg(feature = "backend_egl")]
|
||||
pub mod egl;
|
||||
#[cfg(feature = "backend_libinput")]
|
||||
pub mod libinput;
|
||||
#[cfg(feature = "backend_session")]
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
use super::logind::{self, logind_session_bind, BoundLogindSession, LogindSession, LogindSessionNotifier};
|
||||
use super::{
|
||||
direct::{self, direct_session_bind, BoundDirectSession, DirectSession, DirectSessionNotifier},
|
||||
AsErrno, AsSessionObserver, Session, SessionNotifier, SessionObserver,
|
||||
AsErrno, Session, SessionNotifier, SessionObserver,
|
||||
};
|
||||
use nix::fcntl::OFlag;
|
||||
use std::{cell::RefCell, io::Error as IoError, os::unix::io::RawFd, path::Path, rc::Rc};
|
||||
|
@ -114,6 +114,7 @@ impl AutoSession {
|
|||
}
|
||||
}
|
||||
|
||||
/// Tries to create a new session via the best available interface.
|
||||
#[cfg(not(feature = "backend_session_logind"))]
|
||||
pub fn new<L>(logger: L) -> Option<(AutoSession, AutoSessionNotifier)>
|
||||
where
|
||||
|
@ -202,10 +203,7 @@ impl Session for AutoSession {
|
|||
impl SessionNotifier for AutoSessionNotifier {
|
||||
type Id = AutoId;
|
||||
|
||||
fn register<S: SessionObserver + 'static, A: AsSessionObserver<S>>(
|
||||
&mut self,
|
||||
signal: &mut A,
|
||||
) -> Self::Id {
|
||||
fn register<S: SessionObserver + 'static>(&mut self, signal: S) -> Self::Id {
|
||||
match *self {
|
||||
#[cfg(feature = "backend_session_logind")]
|
||||
AutoSessionNotifier::Logind(ref mut logind) => {
|
||||
|
@ -231,21 +229,6 @@ impl SessionNotifier for AutoSessionNotifier {
|
|||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_active(&self) -> bool {
|
||||
match *self {
|
||||
#[cfg(feature = "backend_session_logind")]
|
||||
AutoSessionNotifier::Logind(ref logind) => logind.is_active(),
|
||||
AutoSessionNotifier::Direct(ref direct) => direct.is_active(),
|
||||
}
|
||||
}
|
||||
fn seat(&self) -> &str {
|
||||
match *self {
|
||||
#[cfg(feature = "backend_session_logind")]
|
||||
AutoSessionNotifier::Logind(ref logind) => logind.seat(),
|
||||
AutoSessionNotifier::Direct(ref direct) => direct.seat(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BoundAutoSession {
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
//! automatically by the `UdevBackend`, if not done manually).
|
||||
//! ```
|
||||
|
||||
use backend::session::{AsErrno, AsSessionObserver, Session, SessionNotifier, SessionObserver};
|
||||
use backend::session::{AsErrno, Session, SessionNotifier, SessionObserver};
|
||||
use dbus::{
|
||||
BusName, BusType, Connection, ConnectionItem, ConnectionItems, Interface, Member, Message, MessageItem,
|
||||
OwnedFd, Path as DbusPath, Watch, WatchEvent,
|
||||
|
@ -101,7 +101,8 @@ impl LogindSession {
|
|||
"org.freedesktop.login1.Manager",
|
||||
"GetSession",
|
||||
Some(vec![session_id.clone().into()]),
|
||||
)?.get1::<DbusPath<'static>>()
|
||||
)?
|
||||
.get1::<DbusPath<'static>>()
|
||||
.chain_err(|| ErrorKind::UnexpectedMethodReturn)?;
|
||||
|
||||
// Match all signals that we want to receive and handle
|
||||
|
@ -342,7 +343,8 @@ impl Session for LogindSession {
|
|||
(major(stat.st_rdev) as u32).into(),
|
||||
(minor(stat.st_rdev) as u32).into(),
|
||||
]),
|
||||
)?.get2::<OwnedFd, bool>();
|
||||
)?
|
||||
.get2::<OwnedFd, bool>();
|
||||
let fd = fd.chain_err(|| ErrorKind::UnexpectedMethodReturn)?.into_fd();
|
||||
Ok(fd)
|
||||
} else {
|
||||
|
@ -363,7 +365,8 @@ impl Session for LogindSession {
|
|||
(major(stat.st_rdev) as u32).into(),
|
||||
(minor(stat.st_rdev) as u32).into(),
|
||||
]),
|
||||
).map(|_| ())
|
||||
)
|
||||
.map(|_| ())
|
||||
} else {
|
||||
bail!(ErrorKind::SessionLost)
|
||||
}
|
||||
|
@ -390,7 +393,8 @@ impl Session for LogindSession {
|
|||
"org.freedesktop.login1.Seat",
|
||||
"SwitchTo",
|
||||
Some(vec![(vt_num as u32).into()]),
|
||||
).map(|_| ())
|
||||
)
|
||||
.map(|_| ())
|
||||
} else {
|
||||
bail!(ErrorKind::SessionLost)
|
||||
}
|
||||
|
@ -404,27 +408,13 @@ pub struct Id(usize);
|
|||
impl SessionNotifier for LogindSessionNotifier {
|
||||
type Id = Id;
|
||||
|
||||
fn register<S: SessionObserver + 'static, A: AsSessionObserver<S>>(
|
||||
&mut self,
|
||||
signal: &mut A,
|
||||
) -> Self::Id {
|
||||
self.internal
|
||||
.signals
|
||||
.borrow_mut()
|
||||
.push(Some(Box::new(signal.observer())));
|
||||
fn register<S: SessionObserver + 'static>(&mut self, signal: S) -> Self::Id {
|
||||
self.internal.signals.borrow_mut().push(Some(Box::new(signal)));
|
||||
Id(self.internal.signals.borrow().len() - 1)
|
||||
}
|
||||
fn unregister(&mut self, signal: Id) {
|
||||
self.internal.signals.borrow_mut()[signal.0] = None;
|
||||
}
|
||||
|
||||
fn is_active(&self) -> bool {
|
||||
self.internal.active.load(Ordering::SeqCst)
|
||||
}
|
||||
|
||||
fn seat(&self) -> &str {
|
||||
&self.internal.seat
|
||||
}
|
||||
}
|
||||
|
||||
/// Bound logind session that is driven by the `wayland_server::EventLoop`.
|
||||
|
@ -455,7 +445,10 @@ pub fn logind_session_bind<Data: 'static>(
|
|||
.into_iter()
|
||||
.map(|watch| {
|
||||
let mut source = Generic::from_raw_fd(watch.fd());
|
||||
source.set_interest(Ready::readable() | Ready::writable());
|
||||
source.set_interest(
|
||||
if watch.readable() { Ready::readable() } else { Ready::empty() }
|
||||
| if watch.writable() { Ready::writable() } else { Ready::empty() }
|
||||
);
|
||||
handle.insert_source(source, {
|
||||
let mut notifier = notifier.clone();
|
||||
move |evt, _| notifier.event(evt)
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
//! for notifications are the `Libinput` context, the `UdevBackend` or a `DrmDevice` (handled
|
||||
//! automatically by the `UdevBackend`, if not done manually).
|
||||
|
||||
use super::{AsErrno, AsSessionObserver, Session, SessionNotifier, SessionObserver};
|
||||
use super::{AsErrno, Session, SessionNotifier, SessionObserver};
|
||||
use nix::{
|
||||
fcntl::{self, open, OFlag},
|
||||
libc::c_int,
|
||||
|
@ -67,7 +67,7 @@ use std::{
|
|||
Arc,
|
||||
},
|
||||
};
|
||||
#[cfg(feature = "backend_session_udev")]
|
||||
#[cfg(feature = "backend_udev")]
|
||||
use udev::Context;
|
||||
use wayland_server::calloop::{signals::Signals, LoopHandle, Source};
|
||||
|
||||
|
@ -120,12 +120,12 @@ const TTY_MAJOR: u64 = 4;
|
|||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
const TTY_MAJOR: u64 = 0;
|
||||
|
||||
#[cfg(not(feature = "backend_session_udev"))]
|
||||
#[cfg(not(feature = "backend_udev"))]
|
||||
fn is_tty_device(dev: dev_t, _path: Option<&Path>) -> bool {
|
||||
major(dev) == TTY_MAJOR
|
||||
}
|
||||
|
||||
#[cfg(feature = "backend_session_udev")]
|
||||
#[cfg(feature = "backend_udev")]
|
||||
fn is_tty_device(dev: dev_t, path: Option<&Path>) -> bool {
|
||||
match path {
|
||||
Some(path) => {
|
||||
|
@ -185,8 +185,10 @@ impl DirectSession {
|
|||
path,
|
||||
fcntl::OFlag::O_RDWR | fcntl::OFlag::O_CLOEXEC,
|
||||
Mode::empty(),
|
||||
).chain_err(|| ErrorKind::FailedToOpenTTY(String::from(path.to_string_lossy())))
|
||||
}).unwrap_or_else(|| {
|
||||
)
|
||||
.chain_err(|| ErrorKind::FailedToOpenTTY(String::from(path.to_string_lossy())))
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
dup(0 /*stdin*/).chain_err(|| ErrorKind::FailedToOpenTTY(String::from("<stdin>")))
|
||||
})?;
|
||||
|
||||
|
@ -350,28 +352,18 @@ pub struct Id(usize);
|
|||
impl SessionNotifier for DirectSessionNotifier {
|
||||
type Id = Id;
|
||||
|
||||
fn register<S: SessionObserver + 'static, A: AsSessionObserver<S>>(
|
||||
&mut self,
|
||||
signal: &mut A,
|
||||
) -> Self::Id {
|
||||
self.signals.push(Some(Box::new(signal.observer())));
|
||||
fn register<S: SessionObserver + 'static>(&mut self, signal: S) -> Self::Id {
|
||||
self.signals.push(Some(Box::new(signal)));
|
||||
Id(self.signals.len() - 1)
|
||||
}
|
||||
fn unregister(&mut self, signal: Id) {
|
||||
self.signals[signal.0] = None;
|
||||
}
|
||||
|
||||
fn is_active(&self) -> bool {
|
||||
self.active.load(Ordering::SeqCst)
|
||||
}
|
||||
fn seat(&self) -> &str {
|
||||
"seat0"
|
||||
}
|
||||
}
|
||||
|
||||
impl DirectSessionNotifier {
|
||||
fn signal_received(&mut self) {
|
||||
if self.is_active() {
|
||||
if self.active.load(Ordering::SeqCst) {
|
||||
info!(self.logger, "Session shall become inactive.");
|
||||
for signal in &mut self.signals {
|
||||
if let Some(ref mut signal) = *signal {
|
||||
|
@ -439,7 +431,8 @@ pub fn direct_session_bind<Data: 'static>(
|
|||
.insert_source(source, {
|
||||
let notifier = notifier.clone();
|
||||
move |_, _| notifier.borrow_mut().signal_received()
|
||||
}).map_err(move |e| {
|
||||
})
|
||||
.map_err(move |e| {
|
||||
// the backend in the closure should already have been dropped
|
||||
let notifier = Rc::try_unwrap(fail_notifier)
|
||||
.unwrap_or_else(|_| unreachable!())
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
//! The following mechanisms are currently provided:
|
||||
//! - direct - legacy tty / virtual terminal kernel API
|
||||
//!
|
||||
use nix::fcntl::OFlag;
|
||||
pub use nix::fcntl::OFlag;
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
os::unix::io::RawFd,
|
||||
|
@ -54,15 +54,9 @@ pub trait SessionNotifier {
|
|||
/// Registers a given `SessionObserver`.
|
||||
///
|
||||
/// Returns an id of the inserted observer, can be used to remove it again.
|
||||
fn register<S: SessionObserver + 'static, A: AsSessionObserver<S>>(&mut self, signal: &mut A)
|
||||
-> Self::Id;
|
||||
fn register<S: SessionObserver + 'static>(&mut self, signal: S) -> Self::Id;
|
||||
/// Removes an observer by its given id from `SessionNotifier::register`.
|
||||
fn unregister(&mut self, signal: Self::Id);
|
||||
|
||||
/// Check if this session is currently active
|
||||
fn is_active(&self) -> bool;
|
||||
/// Which seat this session is on
|
||||
fn seat(&self) -> &str;
|
||||
}
|
||||
|
||||
/// Trait describing the ability to return a `SessionObserver` related to Self.
|
||||
|
@ -186,3 +180,5 @@ pub mod auto;
|
|||
mod dbus;
|
||||
pub mod direct;
|
||||
pub use self::dbus::*;
|
||||
mod multi;
|
||||
pub use self::multi::*;
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
use std::{
|
||||
collections::HashMap,
|
||||
os::unix::io::RawFd,
|
||||
sync::{
|
||||
atomic::{AtomicUsize, Ordering},
|
||||
Arc, Mutex,
|
||||
},
|
||||
};
|
||||
|
||||
use super::{SessionNotifier, SessionObserver};
|
||||
|
||||
static ID_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
/// Ids of registered `SessionObserver`s of the `DirectSessionNotifier`
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
||||
pub struct Id(usize);
|
||||
|
||||
struct MultiObserver {
|
||||
observer: Arc<Mutex<HashMap<Id, Box<SessionObserver>>>>,
|
||||
}
|
||||
|
||||
impl SessionObserver for MultiObserver {
|
||||
fn pause(&mut self, device: Option<(u32, u32)>) {
|
||||
let mut lock = self.observer.lock().unwrap();
|
||||
for mut observer in lock.values_mut() {
|
||||
observer.pause(device)
|
||||
}
|
||||
}
|
||||
fn activate(&mut self, device: Option<(u32, u32, Option<RawFd>)>) {
|
||||
let mut lock = self.observer.lock().unwrap();
|
||||
for mut observer in lock.values_mut() {
|
||||
observer.activate(device)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MultiNotifier {
|
||||
observer: Arc<Mutex<HashMap<Id, Box<SessionObserver>>>>,
|
||||
}
|
||||
|
||||
impl SessionNotifier for MultiNotifier {
|
||||
type Id = Id;
|
||||
|
||||
fn register<S: SessionObserver + 'static>(&mut self, signal: S) -> Self::Id {
|
||||
let id = Id(ID_COUNTER.fetch_add(1, Ordering::SeqCst));
|
||||
self.observer.lock().unwrap().insert(id, Box::new(signal));
|
||||
id
|
||||
}
|
||||
|
||||
fn unregister(&mut self, signal: Self::Id) {
|
||||
self.observer.lock().unwrap().remove(&signal);
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a pair of a linked [`SessionObserver`](../trait.SessionObserver.html) and a
|
||||
/// [`SessionNotifier`](../trait.SessionNotifier.html).
|
||||
///
|
||||
/// Observers added to the returned notifier are notified,
|
||||
/// when the returned observer is notified.
|
||||
pub fn notify_multiplexer() -> (impl SessionObserver, impl SessionNotifier<Id = Id>) {
|
||||
let observer = Arc::new(Mutex::new(HashMap::new()));
|
||||
|
||||
(
|
||||
MultiObserver {
|
||||
observer: observer.clone(),
|
||||
},
|
||||
MultiNotifier { observer },
|
||||
)
|
||||
}
|
|
@ -9,258 +9,90 @@
|
|||
//! See also `examples/udev.rs` for pure hardware backed example of a compositor utilizing this
|
||||
//! backend.
|
||||
|
||||
use backend::{
|
||||
drm::{drm_device_bind, DrmDevice, DrmHandler},
|
||||
session::{AsSessionObserver, Session, SessionObserver},
|
||||
};
|
||||
use drm::{control::Device as ControlDevice, Device as BasicDevice};
|
||||
use nix::{fcntl, sys::stat::dev_t};
|
||||
use nix::sys::stat::{dev_t, stat};
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::HashMap,
|
||||
collections::HashSet,
|
||||
ffi::OsString,
|
||||
io::Error as IoError,
|
||||
mem::drop,
|
||||
os::unix::io::{AsRawFd, RawFd},
|
||||
path::{Path, PathBuf},
|
||||
rc::{Rc, Weak},
|
||||
};
|
||||
use udev::{Context, Enumerator, Event, EventType, MonitorBuilder, MonitorSocket, Result as UdevResult};
|
||||
use udev::{Context, Enumerator, EventType, MonitorBuilder, MonitorSocket, Result as UdevResult};
|
||||
|
||||
use wayland_server::calloop::{
|
||||
generic::{EventedRawFd, Generic},
|
||||
generic::{EventedFd, Generic},
|
||||
mio::Ready,
|
||||
LoopHandle, Source,
|
||||
InsertError, LoopHandle, Source,
|
||||
};
|
||||
|
||||
/// 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.
|
||||
/// Backend to monitor available drm devices.
|
||||
///
|
||||
/// 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
|
||||
/// Provides a way to automatically scan for available gpus and notifies the
|
||||
/// given handler of any changes. Can be used to provide hot-plug functionality for gpus and
|
||||
/// attached monitors.
|
||||
pub struct UdevBackend<
|
||||
H: DrmHandler<SessionFdDrmDevice> + 'static,
|
||||
S: Session + 'static,
|
||||
T: UdevHandler<H> + 'static,
|
||||
Data: 'static,
|
||||
> {
|
||||
_handler: ::std::marker::PhantomData<H>,
|
||||
devices: Rc<
|
||||
RefCell<
|
||||
HashMap<
|
||||
dev_t,
|
||||
(
|
||||
Source<Generic<EventedRawFd>>,
|
||||
Rc<RefCell<DrmDevice<SessionFdDrmDevice>>>,
|
||||
),
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
pub struct UdevBackend<T: UdevHandler + 'static> {
|
||||
devices: HashSet<dev_t>,
|
||||
monitor: MonitorSocket,
|
||||
session: S,
|
||||
handler: T,
|
||||
logger: ::slog::Logger,
|
||||
handle: LoopHandle<Data>,
|
||||
}
|
||||
|
||||
impl<
|
||||
H: DrmHandler<SessionFdDrmDevice> + 'static,
|
||||
S: Session + 'static,
|
||||
T: UdevHandler<H> + 'static,
|
||||
Data: 'static,
|
||||
> UdevBackend<H, S, T, Data>
|
||||
{
|
||||
impl<T: UdevHandler + 'static> AsRawFd for UdevBackend<T> {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.monitor.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UdevHandler + 'static> UdevBackend<T> {
|
||||
/// Creates a new `UdevBackend` and adds it to the given `EventLoop`'s state.
|
||||
///
|
||||
/// ## Arguments
|
||||
/// `evlh` - An event loop to use for binding `DrmDevices`
|
||||
/// `context` - An initialized udev context
|
||||
/// `session` - A session used to open and close devices as they become available
|
||||
/// `handler` - User-provided handler to respond to any detected changes
|
||||
/// `seat` -
|
||||
/// `logger` - slog Logger to be used by the backend and its `DrmDevices`.
|
||||
pub fn new<L>(
|
||||
handle: LoopHandle<Data>,
|
||||
pub fn new<L, S: AsRef<str>>(
|
||||
context: &Context,
|
||||
mut session: S,
|
||||
mut handler: T,
|
||||
seat: S,
|
||||
logger: L,
|
||||
) -> Result<UdevBackend<H, S, T, Data>>
|
||||
) -> UdevResult<UdevBackend<T>>
|
||||
where
|
||||
L: Into<Option<::slog::Logger>>,
|
||||
{
|
||||
let logger = ::slog_or_stdlog(logger).new(o!("smithay_module" => "backend_udev"));
|
||||
let seat = session.seat();
|
||||
let devices = all_gpus(context, seat)
|
||||
.chain_err(|| ErrorKind::FailedToScan)?
|
||||
let log = ::slog_or_stdlog(logger).new(o!("smithay_module" => "backend_udev"));
|
||||
|
||||
let devices = all_gpus(context, seat)?
|
||||
.into_iter()
|
||||
// Create devices
|
||||
.flat_map(|path| {
|
||||
match DrmDevice::new(
|
||||
{
|
||||
match session.open(
|
||||
&path,
|
||||
fcntl::OFlag::O_RDWR
|
||||
| fcntl::OFlag::O_CLOEXEC
|
||||
| fcntl::OFlag::O_NOCTTY
|
||||
| fcntl::OFlag::O_NONBLOCK,
|
||||
) {
|
||||
Ok(fd) => SessionFdDrmDevice(fd),
|
||||
Err(err) => {
|
||||
warn!(
|
||||
logger,
|
||||
"Unable to open drm device {:?}, Error: {:?}. Skipping", path, err
|
||||
);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
},
|
||||
logger.clone(),
|
||||
) {
|
||||
// Call the handler, which might add it to the runloop
|
||||
Ok(mut device) => {
|
||||
let devnum = device.device_id();
|
||||
let fd = device.as_raw_fd();
|
||||
match handler.device_added(&mut device) {
|
||||
Some(drm_handler) => match drm_device_bind(&handle, device, drm_handler) {
|
||||
Ok((event_source, device)) => Some((devnum, (event_source, device))),
|
||||
Err((err, mut device)) => {
|
||||
warn!(logger, "Failed to bind device. Error: {:?}.", err);
|
||||
handler.device_removed(&mut device);
|
||||
drop(device);
|
||||
if let Err(err) = session.close(fd) {
|
||||
warn!(
|
||||
logger,
|
||||
"Failed to close dropped device. Error: {:?}. Ignoring", err
|
||||
);
|
||||
};
|
||||
None
|
||||
}
|
||||
},
|
||||
None => {
|
||||
drop(device); //drops master
|
||||
if let Err(err) = session.close(fd) {
|
||||
warn!(logger, "Failed to close device. Error: {:?}. Ignoring", err);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
.flat_map(|path| match stat(&path) {
|
||||
Ok(stat) => {
|
||||
handler.device_added(stat.st_rdev, path);
|
||||
Some(stat.st_rdev)
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(
|
||||
logger,
|
||||
"Failed to initialize device {:?}. Error: {:?}. Skipping", path, err
|
||||
);
|
||||
warn!(log, "Unable to get id of {:?}, Error: {:?}. Skipping", path, err);
|
||||
None
|
||||
}
|
||||
}
|
||||
}).collect::<HashMap<dev_t, _>>();
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut builder = MonitorBuilder::new(context).chain_err(|| ErrorKind::FailedToInitMonitor)?;
|
||||
builder
|
||||
.match_subsystem("drm")
|
||||
.chain_err(|| ErrorKind::FailedToInitMonitor)?;
|
||||
let monitor = builder.listen().chain_err(|| ErrorKind::FailedToInitMonitor)?;
|
||||
let mut builder = MonitorBuilder::new(context)?;
|
||||
builder.match_subsystem("drm")?;
|
||||
let monitor = builder.listen()?;
|
||||
|
||||
Ok(UdevBackend {
|
||||
_handler: ::std::marker::PhantomData,
|
||||
devices: Rc::new(RefCell::new(devices)),
|
||||
devices,
|
||||
monitor,
|
||||
session,
|
||||
handler,
|
||||
logger,
|
||||
handle,
|
||||
logger: log,
|
||||
})
|
||||
}
|
||||
|
||||
/// Closes the udev backend and frees all remaining open devices.
|
||||
pub fn close(&mut self) {
|
||||
let mut devices = self.devices.borrow_mut();
|
||||
for (_, (event_source, device)) in devices.drain() {
|
||||
event_source.remove();
|
||||
let mut device = Rc::try_unwrap(device)
|
||||
.unwrap_or_else(|_| unreachable!())
|
||||
.into_inner();
|
||||
self.handler.device_removed(&mut device);
|
||||
let fd = device.as_raw_fd();
|
||||
drop(device);
|
||||
if let Err(err) = self.session.close(fd) {
|
||||
warn!(self.logger, "Failed to close device. Error: {:?}. Ignoring", err);
|
||||
};
|
||||
}
|
||||
info!(self.logger, "All devices closed");
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
H: DrmHandler<SessionFdDrmDevice> + 'static,
|
||||
S: Session + 'static,
|
||||
T: UdevHandler<H> + 'static,
|
||||
Data: 'static,
|
||||
> Drop for UdevBackend<H, S, T, Data>
|
||||
{
|
||||
impl<T: UdevHandler + 'static> Drop for UdevBackend<T> {
|
||||
fn drop(&mut self) {
|
||||
self.close();
|
||||
}
|
||||
}
|
||||
|
||||
/// `SessionObserver` linked to the `UdevBackend` it was created from.
|
||||
pub struct UdevBackendObserver {
|
||||
devices: Weak<
|
||||
RefCell<
|
||||
HashMap<
|
||||
dev_t,
|
||||
(
|
||||
Source<Generic<EventedRawFd>>,
|
||||
Rc<RefCell<DrmDevice<SessionFdDrmDevice>>>,
|
||||
),
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
logger: ::slog::Logger,
|
||||
}
|
||||
|
||||
impl<
|
||||
H: DrmHandler<SessionFdDrmDevice> + 'static,
|
||||
S: Session + 'static,
|
||||
T: UdevHandler<H> + 'static,
|
||||
Data: 'static,
|
||||
> AsSessionObserver<UdevBackendObserver> for UdevBackend<H, S, T, Data>
|
||||
{
|
||||
fn observer(&mut self) -> UdevBackendObserver {
|
||||
UdevBackendObserver {
|
||||
devices: Rc::downgrade(&self.devices),
|
||||
logger: self.logger.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SessionObserver for UdevBackendObserver {
|
||||
fn pause(&mut self, devnum: Option<(u32, u32)>) {
|
||||
if let Some(devices) = self.devices.upgrade() {
|
||||
for &mut (_, ref device) in devices.borrow_mut().values_mut() {
|
||||
info!(self.logger, "changed successful");
|
||||
device.borrow_mut().observer().pause(devnum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn activate(&mut self, devnum: Option<(u32, u32, Option<RawFd>)>) {
|
||||
if let Some(devices) = self.devices.upgrade() {
|
||||
for &mut (_, ref device) in devices.borrow_mut().values_mut() {
|
||||
info!(self.logger, "changed successful");
|
||||
device.borrow_mut().observer().activate(devnum);
|
||||
}
|
||||
for device in &self.devices {
|
||||
self.handler.device_removed(*device);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -269,122 +101,38 @@ impl SessionObserver for UdevBackendObserver {
|
|||
///
|
||||
/// Allows the backend to receive kernel events and thus to drive the `UdevHandler`.
|
||||
/// No runtime functionality can be provided without using this function.
|
||||
pub fn udev_backend_bind<H, S, T, Data>(
|
||||
mut udev: UdevBackend<H, S, T, Data>,
|
||||
) -> ::std::result::Result<Source<Generic<EventedRawFd>>, IoError>
|
||||
where
|
||||
H: DrmHandler<SessionFdDrmDevice> + 'static,
|
||||
T: UdevHandler<H> + 'static,
|
||||
S: Session + 'static,
|
||||
{
|
||||
let fd = udev.monitor.as_raw_fd();
|
||||
let handle = udev.handle.clone();
|
||||
let mut source = Generic::from_raw_fd(fd);
|
||||
pub fn udev_backend_bind<T: UdevHandler + 'static, Data: 'static>(
|
||||
udev: UdevBackend<T>,
|
||||
handle: &LoopHandle<Data>,
|
||||
) -> Result<Source<Generic<EventedFd<UdevBackend<T>>>>, InsertError<Generic<EventedFd<UdevBackend<T>>>>> {
|
||||
let mut source = Generic::from_fd_source(udev);
|
||||
source.set_interest(Ready::readable());
|
||||
handle
|
||||
.insert_source(source, move |_, _| {
|
||||
udev.process_events();
|
||||
}).map_err(Into::into)
|
||||
|
||||
handle.insert_source(source, |evt, _| {
|
||||
evt.source.borrow_mut().0.process_events();
|
||||
})
|
||||
}
|
||||
|
||||
impl<H, S, T, Data> UdevBackend<H, S, T, Data>
|
||||
where
|
||||
H: DrmHandler<SessionFdDrmDevice> + 'static,
|
||||
T: UdevHandler<H> + 'static,
|
||||
S: Session + 'static,
|
||||
Data: 'static,
|
||||
{
|
||||
impl<T: UdevHandler + 'static> UdevBackend<T> {
|
||||
fn process_events(&mut self) {
|
||||
let events = self.monitor.clone().collect::<Vec<Event>>();
|
||||
for event in events {
|
||||
let monitor = self.monitor.clone();
|
||||
for event in monitor {
|
||||
match event.event_type() {
|
||||
// New device
|
||||
EventType::Add => {
|
||||
info!(self.logger, "Device Added");
|
||||
if let (Some(path), Some(devnum)) = (event.devnode(), event.devnum()) {
|
||||
let mut device = {
|
||||
match DrmDevice::new(
|
||||
{
|
||||
let logger = self.logger.clone();
|
||||
match self.session.open(
|
||||
path,
|
||||
fcntl::OFlag::O_RDWR
|
||||
| fcntl::OFlag::O_CLOEXEC
|
||||
| fcntl::OFlag::O_NOCTTY
|
||||
| fcntl::OFlag::O_NONBLOCK,
|
||||
) {
|
||||
Ok(fd) => SessionFdDrmDevice(fd),
|
||||
Err(err) => {
|
||||
warn!(
|
||||
logger,
|
||||
"Unable to open drm device {:?}, Error: {:?}. Skipping",
|
||||
path,
|
||||
err
|
||||
);
|
||||
continue;
|
||||
if self.devices.insert(devnum) {
|
||||
self.handler.device_added(devnum, path.to_path_buf());
|
||||
}
|
||||
}
|
||||
},
|
||||
self.logger.clone(),
|
||||
) {
|
||||
Ok(dev) => dev,
|
||||
Err(err) => {
|
||||
warn!(
|
||||
self.logger,
|
||||
"Failed to initialize device {:?}. Error: {}. Skipping", path, err
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
};
|
||||
let fd = device.as_raw_fd();
|
||||
match self.handler.device_added(&mut device) {
|
||||
Some(drm_handler) => match drm_device_bind(&self.handle, device, drm_handler) {
|
||||
Ok(fd_event_source) => {
|
||||
self.devices.borrow_mut().insert(devnum, fd_event_source);
|
||||
}
|
||||
Err((err, mut device)) => {
|
||||
warn!(self.logger, "Failed to bind device. Error: {:?}.", err);
|
||||
self.handler.device_removed(&mut device);
|
||||
drop(device);
|
||||
if let Err(err) = self.session.close(fd) {
|
||||
warn!(
|
||||
self.logger,
|
||||
"Failed to close dropped device. Error: {:?}. Ignoring", err
|
||||
);
|
||||
};
|
||||
}
|
||||
},
|
||||
None => {
|
||||
self.handler.device_removed(&mut device);
|
||||
drop(device);
|
||||
if let Err(err) = self.session.close(fd) {
|
||||
warn!(self.logger, "Failed to close unused device. Error: {:?}", err);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
// Device removed
|
||||
EventType::Remove => {
|
||||
info!(self.logger, "Device Remove");
|
||||
if let Some(devnum) = event.devnum() {
|
||||
if let Some((fd_event_source, device)) = self.devices.borrow_mut().remove(&devnum) {
|
||||
fd_event_source.remove();
|
||||
let mut device = Rc::try_unwrap(device)
|
||||
.unwrap_or_else(|_| unreachable!())
|
||||
.into_inner();
|
||||
self.handler.device_removed(&mut device);
|
||||
let fd = device.as_raw_fd();
|
||||
drop(device);
|
||||
if let Err(err) = self.session.close(fd) {
|
||||
warn!(
|
||||
self.logger,
|
||||
"Failed to close device {:?}. Error: {:?}. Ignoring",
|
||||
event.sysname(),
|
||||
err
|
||||
);
|
||||
};
|
||||
if self.devices.remove(&devnum) {
|
||||
self.handler.device_removed(devnum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -393,9 +141,8 @@ where
|
|||
info!(self.logger, "Device Changed");
|
||||
if let Some(devnum) = event.devnum() {
|
||||
info!(self.logger, "Devnum: {:b}", devnum);
|
||||
if let Some(&(_, ref device)) = self.devices.borrow_mut().get(&devnum) {
|
||||
let handler = &mut self.handler;
|
||||
handler.device_changed(&mut device.borrow_mut());
|
||||
if self.devices.contains(&devnum) {
|
||||
self.handler.device_changed(devnum);
|
||||
} else {
|
||||
info!(self.logger, "changed, but device not tracked by backend");
|
||||
};
|
||||
|
@ -409,36 +156,21 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Handler for the `UdevBackend`, allows to open, close and update DRM devices as they change during runtime.
|
||||
pub trait UdevHandler<H: DrmHandler<SessionFdDrmDevice> + 'static> {
|
||||
/// 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.
|
||||
///
|
||||
/// ## Panics
|
||||
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
|
||||
fn device_added(&mut self, device: &mut DrmDevice<SessionFdDrmDevice>) -> Option<H>;
|
||||
/// Handler for the `UdevBackend`, allows to open, close and update drm devices as they change during runtime.
|
||||
pub trait UdevHandler {
|
||||
/// Called when a new device is detected.
|
||||
fn device_added(&mut self, device: dev_t, path: PathBuf);
|
||||
/// Called when an open device is changed.
|
||||
///
|
||||
/// This usually indicates that some connectors did become available or were unplugged. The handler
|
||||
/// should scan again for connected monitors and mode switch accordingly.
|
||||
///
|
||||
/// ## Panics
|
||||
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
|
||||
fn device_changed(&mut self, device: &mut DrmDevice<SessionFdDrmDevice>);
|
||||
fn device_changed(&mut self, device: dev_t);
|
||||
/// Called when a device was removed.
|
||||
///
|
||||
/// The device will not accept any operations anymore and its file descriptor will be closed once
|
||||
/// this function returns, any open references/tokens to this device need to be released.
|
||||
///
|
||||
/// ## Panics
|
||||
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
|
||||
fn device_removed(&mut self, device: &mut DrmDevice<SessionFdDrmDevice>);
|
||||
/// Called when the udev context has encountered and error.
|
||||
///
|
||||
/// ## Panics
|
||||
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
|
||||
fn error(&mut self, error: IoError);
|
||||
/// The corresponding `UdevRawFd` will never return a valid `RawFd` anymore
|
||||
/// and its file descriptor will be closed once this function returns,
|
||||
/// any open references/tokens to this device need to be released.
|
||||
fn device_removed(&mut self, device: dev_t);
|
||||
}
|
||||
|
||||
/// Returns the path of the primary GPU device if any
|
||||
|
@ -486,25 +218,7 @@ pub fn all_gpus<S: AsRef<str>>(context: &Context, seat: S) -> UdevResult<Vec<Pat
|
|||
.map(|x| x.to_os_string())
|
||||
.unwrap_or(OsString::from("seat0"))
|
||||
== *seat.as_ref()
|
||||
}).flat_map(|device| device.devnode().map(PathBuf::from))
|
||||
})
|
||||
.flat_map(|device| device.devnode().map(PathBuf::from))
|
||||
.collect())
|
||||
}
|
||||
|
||||
error_chain! {
|
||||
errors {
|
||||
#[doc = "Failed to scan for devices"]
|
||||
FailedToScan {
|
||||
description("Failed to scan for devices"),
|
||||
}
|
||||
|
||||
#[doc = "Failed to initialize udev monitor"]
|
||||
FailedToInitMonitor {
|
||||
description("Failed to initialize udev monitor"),
|
||||
}
|
||||
|
||||
#[doc = "Failed to identify devices"]
|
||||
FailedToIdentifyDevices {
|
||||
description("Failed to identify devices"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,11 @@
|
|||
//! Implementation of backend traits for types provided by `winit`
|
||||
|
||||
use backend::{
|
||||
graphics::{
|
||||
egl::{
|
||||
context::GlAttributes,
|
||||
error as egl_error,
|
||||
error::Result as EGLResult,
|
||||
native,
|
||||
wayland::{EGLDisplay, EGLWaylandExtensions},
|
||||
EGLContext, EGLGraphicsBackend, EGLSurface, PixelFormat, SwapBuffersError,
|
||||
},
|
||||
GraphicsBackend,
|
||||
context::GlAttributes, error as egl_error, error::Result as EGLResult, native, EGLContext,
|
||||
EGLDisplay, EGLGraphicsBackend, EGLSurface,
|
||||
},
|
||||
graphics::{gl::GLGraphicsBackend, CursorBackend, PixelFormat, SwapBuffersError},
|
||||
input::{
|
||||
Axis, AxisSource, Event as BackendEvent, InputBackend, InputHandler, KeyState, KeyboardKeyEvent,
|
||||
MouseButton, MouseButtonState, PointerAxisEvent, PointerButtonEvent, PointerMotionAbsoluteEvent,
|
||||
|
@ -20,7 +14,12 @@ use backend::{
|
|||
},
|
||||
};
|
||||
use nix::libc::c_void;
|
||||
use std::{cell::RefCell, cmp, error, fmt, rc::Rc, time::Instant};
|
||||
use std::{
|
||||
cell::{Ref, RefCell},
|
||||
cmp, error, fmt,
|
||||
rc::Rc,
|
||||
time::Instant,
|
||||
};
|
||||
use wayland_client::egl as wegl;
|
||||
use wayland_server::Display;
|
||||
use winit::{
|
||||
|
@ -59,10 +58,10 @@ enum Window {
|
|||
}
|
||||
|
||||
impl Window {
|
||||
fn window(&self) -> &WinitWindow {
|
||||
fn window(&self) -> Ref<WinitWindow> {
|
||||
match *self {
|
||||
Window::Wayland { ref context, .. } => &**context,
|
||||
Window::X11 { ref context, .. } => &**context,
|
||||
Window::Wayland { ref context, .. } => context.borrow(),
|
||||
Window::X11 { ref context, .. } => context.borrow(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -156,12 +155,12 @@ where
|
|||
let reqs = Default::default();
|
||||
let window = Rc::new(
|
||||
if native::NativeDisplay::<native::Wayland>::is_backend(&winit_window) {
|
||||
let context =
|
||||
let mut context =
|
||||
EGLContext::<native::Wayland, WinitWindow>::new(winit_window, attributes, reqs, log.clone())?;
|
||||
let surface = context.create_surface(())?;
|
||||
Window::Wayland { context, surface }
|
||||
} else if native::NativeDisplay::<native::X11>::is_backend(&winit_window) {
|
||||
let context =
|
||||
let mut context =
|
||||
EGLContext::<native::X11, WinitWindow>::new(winit_window, attributes, reqs, log.clone())?;
|
||||
let surface = context.create_surface(())?;
|
||||
Window::X11 { context, surface }
|
||||
|
@ -221,13 +220,13 @@ pub trait WinitEventsHandler {
|
|||
|
||||
impl WinitGraphicsBackend {
|
||||
/// Get a reference to the internally used `winit::Window`
|
||||
pub fn winit_window(&self) -> &WinitWindow {
|
||||
pub fn winit_window(&self) -> Ref<WinitWindow> {
|
||||
self.window.window()
|
||||
}
|
||||
}
|
||||
|
||||
impl GraphicsBackend for WinitGraphicsBackend {
|
||||
type CursorFormat = MouseCursor;
|
||||
impl<'a> CursorBackend<'a> for WinitGraphicsBackend {
|
||||
type CursorFormat = &'a MouseCursor;
|
||||
type Error = ();
|
||||
|
||||
fn set_cursor_position(&self, x: u32, y: u32) -> ::std::result::Result<(), ()> {
|
||||
|
@ -240,11 +239,14 @@ impl GraphicsBackend for WinitGraphicsBackend {
|
|||
})
|
||||
}
|
||||
|
||||
fn set_cursor_representation(
|
||||
&self,
|
||||
cursor: &Self::CursorFormat,
|
||||
fn set_cursor_representation<'b>(
|
||||
&'b self,
|
||||
cursor: Self::CursorFormat,
|
||||
_hotspot: (u32, u32),
|
||||
) -> ::std::result::Result<(), ()> {
|
||||
) -> ::std::result::Result<(), ()>
|
||||
where
|
||||
'a: 'b,
|
||||
{
|
||||
// Cannot log this one, as `CursorFormat` is not `Debug` and should not be
|
||||
debug!(self.logger, "Changing cursor representation");
|
||||
self.window.window().set_cursor(*cursor);
|
||||
|
@ -252,7 +254,7 @@ impl GraphicsBackend for WinitGraphicsBackend {
|
|||
}
|
||||
}
|
||||
|
||||
impl EGLGraphicsBackend for WinitGraphicsBackend {
|
||||
impl GLGraphicsBackend for WinitGraphicsBackend {
|
||||
fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
|
||||
trace!(self.logger, "Swapping buffers");
|
||||
match *self.window {
|
||||
|
@ -303,7 +305,7 @@ impl EGLGraphicsBackend for WinitGraphicsBackend {
|
|||
}
|
||||
}
|
||||
|
||||
impl EGLWaylandExtensions for WinitGraphicsBackend {
|
||||
impl EGLGraphicsBackend for WinitGraphicsBackend {
|
||||
fn bind_wl_display(&self, display: &Display) -> EGLResult<EGLDisplay> {
|
||||
match *self.window {
|
||||
Window::Wayland { ref context, .. } => context.bind_wl_display(display),
|
||||
|
|
|
@ -14,6 +14,7 @@ extern crate tempfile;
|
|||
pub extern crate wayland_commons;
|
||||
pub extern crate wayland_protocols;
|
||||
pub extern crate wayland_server;
|
||||
#[cfg(feature = "native_lib")]
|
||||
extern crate wayland_sys;
|
||||
extern crate xkbcommon;
|
||||
|
||||
|
@ -21,13 +22,13 @@ extern crate xkbcommon;
|
|||
pub extern crate dbus;
|
||||
#[cfg(feature = "backend_drm")]
|
||||
pub extern crate drm;
|
||||
#[cfg(feature = "backend_drm")]
|
||||
#[cfg(feature = "backend_drm_gbm")]
|
||||
pub extern crate gbm;
|
||||
#[cfg(feature = "backend_libinput")]
|
||||
pub extern crate input;
|
||||
#[cfg(feature = "backend_session_logind")]
|
||||
pub extern crate systemd;
|
||||
#[cfg(feature = "udev")]
|
||||
#[cfg(feature = "backend_udev")]
|
||||
pub extern crate udev;
|
||||
#[cfg(feature = "backend_winit")]
|
||||
extern crate wayland_client;
|
||||
|
|
|
@ -108,7 +108,8 @@ impl PointerGrab for DnDGrab {
|
|||
offer_data.clone(),
|
||||
action_choice,
|
||||
)
|
||||
}).unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
// advertize the offer to the client
|
||||
device.send(wl_data_device::Event::DataOffer { id: offer.clone() });
|
||||
with_source_metadata(source, |meta| {
|
||||
|
@ -118,7 +119,8 @@ impl PointerGrab for DnDGrab {
|
|||
offer.send(wl_data_offer::Event::SourceActions {
|
||||
source_actions: meta.dnd_action.to_raw(),
|
||||
});
|
||||
}).unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
device.send(wl_data_device::Event::Enter {
|
||||
serial,
|
||||
x: x - sx,
|
||||
|
|
|
@ -150,7 +150,8 @@ impl SeatData {
|
|||
// check if the source and associated mime type is still valid
|
||||
let valid = with_source_metadata(&source, |meta| {
|
||||
meta.mime_types.contains(&mime_type)
|
||||
}).unwrap_or(false)
|
||||
})
|
||||
.unwrap_or(false)
|
||||
&& source.is_alive();
|
||||
if !valid {
|
||||
// deny the receive
|
||||
|
@ -171,7 +172,8 @@ impl SeatData {
|
|||
for mime_type in meta.mime_types.iter().cloned() {
|
||||
offer.send(wl_data_offer::Event::Offer { mime_type })
|
||||
}
|
||||
}).unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
dd.send(wl_data_device::Event::Selection { id: Some(offer) });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -131,7 +131,8 @@ where
|
|||
self.callback.clone(),
|
||||
action_choice,
|
||||
)
|
||||
}).unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
// advertize the offer to the client
|
||||
device.send(wl_data_device::Event::DataOffer { id: offer.clone() });
|
||||
for mime_type in self.metadata.mime_types.iter().cloned() {
|
||||
|
|
|
@ -141,7 +141,8 @@ impl KbdInternal {
|
|||
&xkb_config.variant,
|
||||
xkb_config.options,
|
||||
xkb::KEYMAP_COMPILE_NO_FLAGS,
|
||||
).ok_or(())?;
|
||||
)
|
||||
.ok_or(())?;
|
||||
let state = xkb::State::new(&keymap);
|
||||
Ok(KbdInternal {
|
||||
known_kbds: Vec::new(),
|
||||
|
|
|
@ -119,7 +119,8 @@ where
|
|||
} else {
|
||||
false
|
||||
}
|
||||
}).expect("wl_shell_surface exists but surface has not the right role?");
|
||||
})
|
||||
.expect("wl_shell_surface exists but surface has not the right role?");
|
||||
if valid {
|
||||
(&mut *user_impl)(ShellRequest::Pong {
|
||||
surface: make_handle(&shell_surface, ctoken),
|
||||
|
|
|
@ -620,7 +620,8 @@ where
|
|||
.with_role_data::<XdgSurfaceRole, _, _>(&self.wl_surface, |data| match data.pending_state {
|
||||
XdgSurfacePendingState::Toplevel(ref state) => Some(state.clone()),
|
||||
_ => None,
|
||||
}).ok()
|
||||
})
|
||||
.ok()
|
||||
.and_then(|x| x)
|
||||
}
|
||||
}
|
||||
|
@ -782,7 +783,8 @@ where
|
|||
.with_role_data::<XdgSurfaceRole, _, _>(&self.wl_surface, |data| match data.pending_state {
|
||||
XdgSurfacePendingState::Popup(ref state) => Some(state.clone()),
|
||||
_ => None,
|
||||
}).ok()
|
||||
})
|
||||
.ok()
|
||||
.and_then(|x| x)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -217,7 +217,8 @@ where
|
|||
"xdg_surface was destroyed before its role object".into(),
|
||||
);
|
||||
}
|
||||
}).expect("xdg_surface exists but surface has not shell_surface role?!");
|
||||
})
|
||||
.expect("xdg_surface exists but surface has not shell_surface role?!");
|
||||
}
|
||||
|
||||
fn xdg_surface_implementation<U, R, SD>(
|
||||
|
@ -244,7 +245,8 @@ fn xdg_surface_implementation<U, R, SD>(
|
|||
min_size: (0, 0),
|
||||
max_size: (0, 0),
|
||||
});
|
||||
}).expect("xdg_surface exists but surface has not shell_surface role?!");
|
||||
})
|
||||
.expect("xdg_surface exists but surface has not shell_surface role?!");
|
||||
let toplevel = id.implement_nonsend(
|
||||
toplevel_implementation::<U, R, SD>,
|
||||
Some(destroy_toplevel::<U, R, SD>),
|
||||
|
@ -286,7 +288,8 @@ fn xdg_surface_implementation<U, R, SD>(
|
|||
parent: parent_surface,
|
||||
positioner: positioner_data.borrow().clone(),
|
||||
});
|
||||
}).expect("xdg_surface exists but surface has not shell_surface role?!");
|
||||
})
|
||||
.expect("xdg_surface exists but surface has not shell_surface role?!");
|
||||
let popup = id.implement_nonsend(
|
||||
xg_popup_implementation::<U, R, SD>,
|
||||
Some(destroy_popup::<U, R, SD>),
|
||||
|
@ -315,7 +318,8 @@ fn xdg_surface_implementation<U, R, SD>(
|
|||
.compositor_token
|
||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |data| {
|
||||
data.window_geometry = Some(Rectangle { x, y, width, height });
|
||||
}).expect("xdg_surface exists but surface has not shell_surface role?!");
|
||||
})
|
||||
.expect("xdg_surface exists but surface has not shell_surface role?!");
|
||||
}
|
||||
xdg_surface::Request::AckConfigure { serial } => {
|
||||
data.shell_data
|
||||
|
@ -336,7 +340,8 @@ fn xdg_surface_implementation<U, R, SD>(
|
|||
);
|
||||
}
|
||||
role_data.configured = true;
|
||||
}).expect("xdg_surface exists but surface has not shell_surface role?!");
|
||||
})
|
||||
.expect("xdg_surface exists but surface has not shell_surface role?!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -369,7 +374,8 @@ fn with_surface_toplevel_data<U, R, SD, F>(
|
|||
.with_role_data::<XdgSurfaceRole, _, _>(&toplevel_data.wl_surface, |data| match data.pending_state {
|
||||
XdgSurfacePendingState::Toplevel(ref mut toplevel_data) => f(toplevel_data),
|
||||
_ => unreachable!(),
|
||||
}).expect("xdg_toplevel exists but surface has not shell_surface role?!");
|
||||
})
|
||||
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
|
||||
}
|
||||
|
||||
pub fn send_toplevel_configure<U, R, SD>(
|
||||
|
@ -539,7 +545,8 @@ where
|
|||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |data| {
|
||||
data.pending_state = XdgSurfacePendingState::None;
|
||||
data.configured = false;
|
||||
}).expect("xdg_toplevel exists but surface has not shell_surface role?!");
|
||||
})
|
||||
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
|
||||
}
|
||||
// remove this surface from the known ones (as well as any leftover dead surface)
|
||||
data.shell_data
|
||||
|
@ -627,7 +634,8 @@ where
|
|||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |data| {
|
||||
data.pending_state = XdgSurfacePendingState::None;
|
||||
data.configured = false;
|
||||
}).expect("xdg_popup exists but surface has not shell_surface role?!");
|
||||
})
|
||||
.expect("xdg_popup exists but surface has not shell_surface role?!");
|
||||
}
|
||||
// remove this surface from the known ones (as well as any leftover dead surface)
|
||||
data.shell_data
|
||||
|
|
|
@ -236,7 +236,8 @@ where
|
|||
"xdg_surface was destroyed before its role object".into(),
|
||||
);
|
||||
}
|
||||
}).expect("xdg_surface exists but surface has not shell_surface role?!");
|
||||
})
|
||||
.expect("xdg_surface exists but surface has not shell_surface role?!");
|
||||
}
|
||||
|
||||
fn xdg_surface_implementation<U, R, SD>(
|
||||
|
@ -263,7 +264,8 @@ fn xdg_surface_implementation<U, R, SD>(
|
|||
min_size: (0, 0),
|
||||
max_size: (0, 0),
|
||||
});
|
||||
}).expect("xdg_surface exists but surface has not shell_surface role?!");
|
||||
})
|
||||
.expect("xdg_surface exists but surface has not shell_surface role?!");
|
||||
let toplevel = id.implement_nonsend(
|
||||
toplevel_implementation::<U, R, SD>,
|
||||
Some(destroy_toplevel::<U, R, SD>),
|
||||
|
@ -302,7 +304,8 @@ fn xdg_surface_implementation<U, R, SD>(
|
|||
parent: Some(parent_data.wl_surface.clone()),
|
||||
positioner: positioner_data.borrow().clone(),
|
||||
});
|
||||
}).expect("xdg_surface exists but surface has not shell_surface role?!");
|
||||
})
|
||||
.expect("xdg_surface exists but surface has not shell_surface role?!");
|
||||
let popup = id.implement_nonsend(
|
||||
popup_implementation::<U, R, SD>,
|
||||
Some(destroy_popup::<U, R, SD>),
|
||||
|
@ -331,7 +334,8 @@ fn xdg_surface_implementation<U, R, SD>(
|
|||
.compositor_token
|
||||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |data| {
|
||||
data.window_geometry = Some(Rectangle { x, y, width, height });
|
||||
}).expect("xdg_surface exists but surface has not shell_surface role?!");
|
||||
})
|
||||
.expect("xdg_surface exists but surface has not shell_surface role?!");
|
||||
}
|
||||
zxdg_surface_v6::Request::AckConfigure { serial } => {
|
||||
data.shell_data
|
||||
|
@ -352,7 +356,8 @@ fn xdg_surface_implementation<U, R, SD>(
|
|||
);
|
||||
}
|
||||
role_data.configured = true;
|
||||
}).expect("xdg_surface exists but surface has not shell_surface role?!");
|
||||
})
|
||||
.expect("xdg_surface exists but surface has not shell_surface role?!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -382,7 +387,8 @@ where
|
|||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |data| match data.pending_state {
|
||||
XdgSurfacePendingState::Toplevel(ref mut toplevel_data) => f(toplevel_data),
|
||||
_ => unreachable!(),
|
||||
}).expect("xdg_toplevel exists but surface has not shell_surface role?!");
|
||||
})
|
||||
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
|
||||
}
|
||||
|
||||
pub fn send_toplevel_configure<U, R, SD>(
|
||||
|
@ -553,7 +559,8 @@ where
|
|||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |data| {
|
||||
data.pending_state = XdgSurfacePendingState::None;
|
||||
data.configured = false;
|
||||
}).expect("xdg_toplevel exists but surface has not shell_surface role?!");
|
||||
})
|
||||
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
|
||||
}
|
||||
// remove this surface from the known ones (as well as any leftover dead surface)
|
||||
data.shell_data
|
||||
|
@ -644,7 +651,8 @@ where
|
|||
.with_role_data::<XdgSurfaceRole, _, _>(&data.wl_surface, |data| {
|
||||
data.pending_state = XdgSurfacePendingState::None;
|
||||
data.configured = false;
|
||||
}).expect("xdg_popup exists but surface has not shell_surface role?!");
|
||||
})
|
||||
.expect("xdg_popup exists but surface has not shell_surface role?!");
|
||||
}
|
||||
// remove this surface from the known ones (as well as any leftover dead surface)
|
||||
data.shell_data
|
||||
|
|
|
@ -97,7 +97,8 @@ impl<WM: XWindowManager + 'static> XWayland<WM> {
|
|||
debug_assert!(evt.signal() == Signal::SIGUSR1);
|
||||
xwayland_ready(&inner);
|
||||
},
|
||||
).map_err(|_| ())
|
||||
)
|
||||
.map_err(|_| ())
|
||||
}),
|
||||
wayland_display: display,
|
||||
instance: None,
|
||||
|
|
Loading…
Reference in New Issue