From 0758ec98bab8e18ab69c7f97021c6a8e7d49fe11 Mon Sep 17 00:00:00 2001 From: Drakulix Date: Wed, 20 Sep 2017 17:09:37 +0200 Subject: [PATCH] Fix the drm backend for wayland-rs 0.10 --- examples/drm.rs | 230 +++++-------------------------------- src/backend/drm/backend.rs | 4 +- src/backend/drm/mod.rs | 122 +++++++++----------- 3 files changed, 89 insertions(+), 267 deletions(-) diff --git a/examples/drm.rs b/examples/drm.rs index 1da4faa..30541b0 100644 --- a/examples/drm.rs +++ b/examples/drm.rs @@ -4,7 +4,6 @@ extern crate glium; extern crate rand; #[macro_use(define_roles)] extern crate smithay; -extern crate wayland_protocols; extern crate wayland_server; #[macro_use] @@ -18,153 +17,19 @@ use drm::control::{Device as ControlDevice, ResourceInfo}; use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState}; use drm::control::encoder::Info as EncoderInfo; use glium::Surface; -use helpers::GliumDrawer; -use slog::*; -use smithay::backend::drm::{DrmBackend, DrmDevice, DrmHandler, Id}; +use helpers::{shell_implementation, surface_implementation, GliumDrawer, Roles, SurfaceData}; +use slog::{Drain, Logger}; +use smithay::backend::drm::{drm_device_bind, DrmBackend, DrmDevice, DrmHandler, Id}; use smithay::backend::graphics::egl::EGLGraphicsBackend; use smithay::backend::graphics::glium::{GliumGraphicsBackend, IntoGlium}; -use smithay::compositor::{self, CompositorHandler, CompositorToken, SubsurfaceRole, TraversalAction}; +use smithay::compositor::{compositor_init, CompositorToken, SubsurfaceRole, TraversalAction}; use smithay::compositor::roles::Role; -use smithay::shell::{self, PopupConfigure, PopupSurface, ShellClient, ShellHandler, ShellSurfaceRole, - ToplevelConfigure, ToplevelSurface}; -use smithay::shm::{ShmGlobal, ShmToken}; +use smithay::shell::{shell_init, ShellState}; +use smithay::shm::init_shm_global; use std::fs::OpenOptions; use std::io::Error as IoError; -use std::os::unix::io::AsRawFd; use std::time::Duration; -use wayland_protocols::unstable::xdg_shell::server::{zxdg_shell_v6, zxdg_toplevel_v6}; -use wayland_server::{Client, EventLoopHandle}; -use wayland_server::protocol::{wl_callback, wl_compositor, wl_output, wl_seat, wl_shell, wl_shm, - wl_subcompositor, wl_surface}; -use wayland_server::sources::READ; - -define_roles!(Roles => [ ShellSurface, ShellSurfaceRole ] ); - -struct SurfaceHandler { - shm_token: ShmToken, -} - -#[derive(Default)] -struct SurfaceData { - buffer: Option<(Vec, (u32, u32))>, - location: Option<(i32, i32)>, -} - -impl compositor::Handler for SurfaceHandler { - fn commit(&mut self, _evlh: &mut EventLoopHandle, _client: &Client, surface: &wl_surface::WlSurface, - token: CompositorToken) { - // we retrieve the contents of the associated buffer and copy it - token.with_surface_data(surface, |attributes| { - match attributes.buffer.take() { - Some(Some((buffer, (_x, _y)))) => { - // we ignore hotspot coordinates in this simple example - self.shm_token - .with_buffer_contents(&buffer, |slice, data| { - let offset = data.offset as usize; - let stride = data.stride as usize; - let width = data.width as usize; - let height = data.height as usize; - let mut new_vec = Vec::with_capacity(width * height * 4); - for i in 0..height { - new_vec.extend( - &slice[(offset + i * stride)..(offset + i * stride + width * 4)], - ); - } - attributes.user_data.buffer = - Some((new_vec, (data.width as u32, data.height as u32))); - }) - .unwrap(); - buffer.release(); - } - Some(None) => { - // erase the contents - attributes.user_data.buffer = None; - } - None => {} - } - }); - } - - fn frame(&mut self, _evlh: &mut EventLoopHandle, _client: &Client, _surface: &wl_surface::WlSurface, - callback: wl_callback::WlCallback, - _token: CompositorToken) { - callback.done(0); - } -} - -struct ShellSurfaceHandler { - token: CompositorToken, -} - -impl ShellSurfaceHandler { - fn new(token: CompositorToken) -> ShellSurfaceHandler { - ShellSurfaceHandler { token } - } -} - -impl shell::Handler for ShellSurfaceHandler { - fn new_client(&mut self, _evlh: &mut EventLoopHandle, _client: ShellClient<()>) {} - fn client_pong(&mut self, _evlh: &mut EventLoopHandle, _client: ShellClient<()>) {} - fn new_toplevel(&mut self, _evlh: &mut EventLoopHandle, - surface: ToplevelSurface) - -> ToplevelConfigure { - let wl_surface = surface.get_surface().unwrap(); - self.token.with_surface_data(wl_surface, |data| { - // place the window at a random location in the [0;300]x[0;300] square - use rand::distributions::{IndependentSample, Range}; - let range = Range::new(0, 300); - let mut rng = rand::thread_rng(); - let x = range.ind_sample(&mut rng); - let y = range.ind_sample(&mut rng); - data.user_data.location = Some((x, y)) - }); - ToplevelConfigure { - size: None, - states: vec![], - serial: 42, - } - } - fn new_popup(&mut self, _evlh: &mut EventLoopHandle, - _surface: PopupSurface) - -> PopupConfigure { - PopupConfigure { - size: (10, 10), - position: (10, 10), - serial: 42, - } - } - fn move_(&mut self, _evlh: &mut EventLoopHandle, - _surface: ToplevelSurface, _seat: &wl_seat::WlSeat, - _serial: u32) { - } - fn resize(&mut self, _evlh: &mut EventLoopHandle, - _surface: ToplevelSurface, _seat: &wl_seat::WlSeat, - _serial: u32, _edges: zxdg_toplevel_v6::ResizeEdge) { - } - fn grab(&mut self, _evlh: &mut EventLoopHandle, - _surface: PopupSurface, _seat: &wl_seat::WlSeat, - _serial: u32) { - } - fn change_display_state(&mut self, _evlh: &mut EventLoopHandle, - _surface: ToplevelSurface, - _maximized: Option, _minimized: Option, _fullscreen: Option, - _output: Option<&wl_output::WlOutput>) - -> ToplevelConfigure { - ToplevelConfigure { - size: None, - states: vec![], - serial: 42, - } - } - fn show_window_menu(&mut self, _evlh: &mut EventLoopHandle, - _surface: ToplevelSurface, - _seat: &wl_seat::WlSeat, _serial: u32, _x: i32, _y: i32) { - } -} - - -type MyCompositorHandler = CompositorHandler; -type MyShellHandler = ShellHandler; +use wayland_server::{EventLoopHandle, StateToken}; fn main() { // A logger facility, here we use the terminal for this example @@ -220,52 +85,21 @@ fn main() { .unwrap(); /* - * Initialize wl_shm global + * Initialize the globals */ - // Insert the ShmGlobal as a handler to your event loop - // Here, we specify tha the standard Argb8888 and Xrgb8888 is the only supported. - let shm_handler_id = event_loop.add_handler_with_init(ShmGlobal::new(vec![], log.clone())); - // Register this handler to advertise a wl_shm global of version 1 - event_loop.register_global::(shm_handler_id, 1); - // retreive the token - let shm_token = { - let state = event_loop.state(); - state.get_handler::(shm_handler_id).get_token() - }; + init_shm_global(&mut event_loop, vec![], log.clone()); - /* - * Initialize the compositor global - */ - let compositor_handler_id = event_loop.add_handler_with_init(MyCompositorHandler::new( - SurfaceHandler { - shm_token: shm_token.clone(), - }, - log.clone(), - )); - // register it to handle wl_compositor and wl_subcompositor - event_loop.register_global::(compositor_handler_id, 4); - event_loop - .register_global::(compositor_handler_id, 1); - // retrieve the tokens - let compositor_token = { - let state = event_loop.state(); - state - .get_handler::(compositor_handler_id) - .get_token() - }; + let (compositor_token, _, _) = + compositor_init(&mut event_loop, surface_implementation(), (), log.clone()); - /* - * Initialize the shell global - */ - let shell_handler_id = event_loop.add_handler_with_init(MyShellHandler::new( - ShellSurfaceHandler::new(compositor_token), + let (shell_state_token, _, _) = shell_init( + &mut event_loop, + compositor_token, + shell_implementation(), compositor_token, log.clone(), - )); - event_loop.register_global::(shell_handler_id, 1); - event_loop.register_global::(shell_handler_id, 1); - + ); /* * Initialize glium @@ -281,29 +115,27 @@ fn main() { let name = display.add_socket_auto().unwrap().into_string().unwrap(); println!("Listening on socket: {}", name); - // Set the DrmHandler - device.set_handler(DrmHandlerImpl { - drawer: drawer, - shell_handler_id, - compositor_token, - logger: log, - }); - /* * Register the DrmDevice on the EventLoop */ - let fd = device.as_raw_fd(); - let drm_device_id = event_loop.add_handler(device); - let _drm_event_source = - event_loop.add_fd_event_source::>(fd, drm_device_id, READ); + let _source = drm_device_bind( + &mut event_loop, + device, + DrmHandlerImpl { + drawer, + shell_state_token, + compositor_token, + logger: log, + }, + ).unwrap(); event_loop.run().unwrap(); } pub struct DrmHandlerImpl { drawer: GliumDrawer>, - shell_handler_id: usize, - compositor_token: CompositorToken, + shell_state_token: StateToken>, + compositor_token: CompositorToken, logger: ::slog::Logger, } @@ -314,10 +146,8 @@ impl DrmHandler for DrmHandlerImpl { // redraw the frame, in a simple but inneficient way { let screen_dimensions = self.drawer.get_framebuffer_dimensions(); - for toplevel_surface in unsafe { - evlh.get_handler_unchecked::(self.shell_handler_id) - .toplevel_surfaces() - } { + let state = evlh.state(); + for toplevel_surface in state.get(&self.shell_state_token).toplevel_surfaces() { if let Some(wl_surface) = toplevel_surface.get_surface() { // this surface is a root of a subsurface tree that needs to be drawn let initial_place = self.compositor_token diff --git a/src/backend/drm/backend.rs b/src/backend/drm/backend.rs index 7703d84..4d2ba49 100644 --- a/src/backend/drm/backend.rs +++ b/src/backend/drm/backend.rs @@ -476,7 +476,7 @@ impl GraphicsBackend for DrmBackend { -> Result<()> { let (w, h) = buffer.dimensions(); debug!(self.0.borrow().logger, "Importing cursor"); - /// import the cursor into a buffer we can render + // import the cursor into a buffer we can render self.0 .borrow_mut() .graphics @@ -548,7 +548,7 @@ impl EGLGraphicsBackend for DrmBackend { return Err(SwapBuffersError::AlreadySwapped); } - /// flip normally + // flip normally graphics.gbm.surface.rent(|egl| egl.surface.swap_buffers())?; graphics.gbm.surface.rent_all(|surface| { diff --git a/src/backend/drm/mod.rs b/src/backend/drm/mod.rs index 9281b70..1dc2510 100644 --- a/src/backend/drm/mod.rs +++ b/src/backend/drm/mod.rs @@ -189,20 +189,16 @@ use backend::graphics::egl::{EGLContext, GlAttributes, PixelFormatRequirements}; use drm::Device as BasicDevice; use drm::control::{connector, crtc, encoder, Mode, ResourceInfo}; use drm::control::Device as ControlDevice; - use gbm::Device as GbmDevice; - use nix; - use std::cell::RefCell; use std::fs::File; -use std::io::Error as IoError; +use std::io::{Error as IoError, Result as IoResult}; use std::os::unix::io::{AsRawFd, RawFd}; use std::rc::{Rc, Weak}; use std::time::Duration; - use wayland_server::EventLoopHandle; -use wayland_server::sources::{FdEventSourceHandler, FdInterest}; +use wayland_server::sources::{FdEventSource, FdEventSourceImpl, READ}; mod backend; pub mod error; @@ -259,14 +255,13 @@ rental! { use self::devices::{Context, Devices}; /// Representation of an open drm device node to create rendering backends -pub struct DrmDevice { +pub struct DrmDevice { context: Rc, backends: Vec>>, - handler: Option, logger: ::slog::Logger, } -impl DrmDevice { +impl DrmDevice { /// Create a new `DrmDevice` from a raw file descriptor /// /// Returns an error of opening the device failed or context creation was not @@ -363,7 +358,7 @@ impl DrmDevice { context: Rc::new(Context::try_new( Box::new(Devices::try_new(Box::new(drm), |drm| { debug!(log, "Creating gbm device"); - GbmDevice::new_from_drm::>(drm).chain_err(|| ErrorKind::GbmInitFailed) + GbmDevice::new_from_drm::(drm).chain_err(|| ErrorKind::GbmInitFailed) })?), |devices| { debug!(log, "Creating egl context from gbm device"); @@ -381,7 +376,6 @@ impl DrmDevice { }, )?), backends: Vec::new(), - handler: None, logger: log, }) } @@ -433,8 +427,9 @@ impl DrmDevice { // configuration is valid, the kernel will figure out the rest let own_id = self.backends.len(); - let logger = self.logger - .new(o!("id" => format!("{}", own_id), "crtc" => format!("{:?}", crtc))); + let logger = self.logger.new( + o!("id" => format!("{}", own_id), "crtc" => format!("{:?}", crtc)), + ); let backend = Rc::new(RefCell::new(DrmBackendInternal::new( self.context.clone(), @@ -449,28 +444,16 @@ impl DrmDevice { Ok(DrmBackend::new(backend)) } - - /// Set a handler for handling finished rendering - pub fn set_handler(&mut self, handler: H) -> Option { - let res = self.handler.take(); - self.handler = Some(handler); - res - } - - /// Clear the currently set handler - pub fn clear_handler(&mut self) -> Option { - self.handler.take() - } } // for users convinience and FdEventSource registering -impl AsRawFd for DrmDevice { +impl AsRawFd for DrmDevice { fn as_raw_fd(&self) -> RawFd { self.context.head().head().as_raw_fd() } } -impl BasicDevice for DrmDevice {} -impl ControlDevice for DrmDevice {} +impl BasicDevice for DrmDevice {} +impl ControlDevice for DrmDevice {} /// Handler for drm node events /// @@ -489,50 +472,59 @@ pub trait DrmHandler { fn error(&mut self, evlh: &mut EventLoopHandle, error: IoError); } -impl FdEventSourceHandler for DrmDevice { - fn ready(&mut self, evlh: &mut EventLoopHandle, fd: RawFd, _mask: FdInterest) { - use std::any::Any; +/// Bind a `DrmDevice` to an EventLoop, +/// +/// This will cause it to recieve events and feed them into an `DrmHandler` +pub fn drm_device_bind(evlh: &mut EventLoopHandle, device: DrmDevice, handler: H) + -> IoResult> +where + H: DrmHandler + 'static, +{ + evlh.add_fd_event_source( + device.as_raw_fd(), + fd_event_source_implementation(), + (device, handler), + READ, + ) +} - struct DrmDeviceRef(RawFd); - impl AsRawFd for DrmDeviceRef { - fn as_raw_fd(&self) -> RawFd { - self.0 - } - } - impl BasicDevice for DrmDeviceRef {} - impl ControlDevice for DrmDeviceRef {} +fn fd_event_source_implementation() -> FdEventSourceImpl<(DrmDevice, H)> +where + H: DrmHandler + 'static, +{ + FdEventSourceImpl { + ready: |evlh, id, _, _| { + use std::any::Any; - struct PageFlipHandler<'a, 'b, H: DrmHandler + 'static>(&'a mut DrmDevice, &'b mut EventLoopHandle); + let &mut (ref dev, ref mut handler) = id; - impl<'a, 'b, H: DrmHandler + 'static> crtc::PageFlipHandler for PageFlipHandler<'a, 'b, H> { - fn handle_event(&mut self, _device: &DrmDeviceRef, frame: u32, duration: Duration, - userdata: Box) { - let id: Id = *userdata.downcast().unwrap(); - if let Some(backend) = self.0.backends[id.raw()].upgrade() { - // we can now unlock the buffer - trace!(self.0.logger, "Handling event for backend {:?}", id.raw()); - backend.borrow().unlock_buffer(); - if let Some(handler) = self.0.handler.as_mut() { + struct PageFlipHandler<'a, 'b, H: DrmHandler + 'static>(&'a mut H, &'b mut EventLoopHandle); + + impl<'a, 'b, H: DrmHandler + 'static> crtc::PageFlipHandler for PageFlipHandler<'a, 'b, H> { + fn handle_event(&mut self, device: &DrmDevice, frame: u32, duration: Duration, + userdata: Box) { + let id: Id = *userdata.downcast().unwrap(); + if let Some(backend) = device.backends[id.raw()].upgrade() { + // we can now unlock the buffer + backend.borrow().unlock_buffer(); + trace!(device.logger, "Handling event for backend {:?}", id.raw()); // and then call the user to render the next frame - handler.ready(self.1, id, frame, duration); + self.0.ready(self.1, id, frame, duration); } } } - } - crtc::handle_event( - &DrmDeviceRef(fd), - 2, - None::<&mut ()>, - Some(&mut PageFlipHandler(self, evlh)), - None::<&mut ()>, - ).unwrap(); - } - - fn error(&mut self, evlh: &mut EventLoopHandle, _fd: RawFd, error: IoError) { - if let Some(handler) = self.handler.as_mut() { - warn!(self.logger, "DrmDevice errored: {}", error); - handler.error(evlh, error) - } + crtc::handle_event( + dev, + 2, + None::<&mut ()>, + Some(&mut PageFlipHandler(handler, evlh)), + None::<&mut ()>, + ).unwrap(); + }, + error: |evlh, id, _, error| { + warn!(id.0.logger, "DrmDevice errored: {}", error); + id.1.error(evlh, error); + }, } }