From c7682e77defebc0ec046c87c9f90563e222ce64d Mon Sep 17 00:00:00 2001 From: Drakulix Date: Sun, 1 Oct 2017 19:21:12 +0200 Subject: [PATCH 01/29] Udev backend --- Cargo.toml | 15 +- examples/drm.rs | 40 ++-- examples/udev.rs | 261 +++++++++++++++++++++++ src/backend/drm/backend.rs | 14 +- src/backend/drm/error.rs | 13 +- src/backend/drm/mod.rs | 167 +++++++++++---- src/backend/graphics/egl.rs | 13 +- src/backend/graphics/glium.rs | 1 + src/backend/input.rs | 13 +- src/backend/libinput.rs | 1 - src/backend/mod.rs | 15 +- src/backend/session/direct.rs | 385 ++++++++++++++++++++++++++++++++++ src/backend/session/logind.rs | 0 src/backend/session/mod.rs | 44 ++++ src/backend/udev.rs | 348 ++++++++++++++++++++++++++++++ src/backend/winit.rs | 3 +- src/lib.rs | 3 + 17 files changed, 1247 insertions(+), 89 deletions(-) create mode 100644 examples/udev.rs create mode 100644 src/backend/session/direct.rs create mode 100644 src/backend/session/logind.rs create mode 100644 src/backend/session/mod.rs create mode 100644 src/backend/udev.rs diff --git a/Cargo.toml b/Cargo.toml index 51027dd..3efcc8e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,13 +17,15 @@ libloading = "0.4.0" wayland-client = { version = "0.9.9", optional = true } winit = { version = "0.8.2", optional = true } drm = { version = "^0.3.0", optional = true } -gbm = { version = "^0.2.2", optional = true } +gbm = { version = "^0.3.0", optional = true } glium = { version = "0.17.1", optional = true, default-features = false } -input = { version = "0.2.0", optional = true } +input = { version = "0.3.0", optional = true } +libudev = { git = "https://github.com/Drakulix/libudev-rs.git", branch = "feature/raw_ffi_access", optional = true } rental = "0.4.11" wayland-protocols = { version = "0.12.0", features = ["unstable_protocols", "server"] } -image = "0.16.0" +image = "0.17.0" error-chain = "0.11.0" +dbus = { version = "0.5.4", optional = true } [build-dependencies] gl_generator = "0.5" @@ -32,10 +34,15 @@ gl_generator = "0.5" slog-term = "2.0" slog-async = "2.0" rand = "0.3" +ctrlc = { version = "3.0", features = ["termination"] } [features] -default = ["backend_winit", "backend_drm", "backend_libinput", "renderer_glium"] +default = ["backend_winit", "backend_drm", "backend_libinput", "backend_udev", "renderer_glium"] backend_winit = ["winit", "wayland-server/dlopen", "wayland-client/dlopen"] backend_drm = ["drm", "gbm"] backend_libinput = ["input"] +backend_session = [] +backend_session_udev = ["libudev", "backend_session"] +backend_session_logind = ["backend_session", "dbus"] +backend_udev = ["libudev", "backend_drm", "backend_session_udev"] renderer_glium = ["glium"] diff --git a/examples/drm.rs b/examples/drm.rs index 5f88726..319b8aa 100644 --- a/examples/drm.rs +++ b/examples/drm.rs @@ -15,6 +15,7 @@ mod helpers; use drm::control::{Device as ControlDevice, ResourceInfo}; use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState}; +use drm::control::crtc; use drm::control::encoder::Info as EncoderInfo; use drm::result::Error as DrmError; use glium::Surface; @@ -80,10 +81,19 @@ fn main() { // Assuming we found a good connector and loaded the info into `connector_info` let mode = connector_info.modes()[0]; // Use first mode (usually highest resoltion, but in reality you should filter and sort and check and match with other connectors, if you use more then one.) - // Initialize the hardware backend - let renderer_token = device - .create_backend(&mut event_loop, crtc, mode, vec![connector_info.handle()]) - .unwrap(); + { + // Initialize the hardware backend + let renderer = device + .create_backend(crtc, mode, vec![connector_info.handle()]) + .unwrap(); + + /* + * Initialize glium + */ + let mut frame = renderer.draw(); + frame.clear_color(0.8, 0.8, 0.9, 1.0); + frame.finish().unwrap(); + } /* * Initialize the globals @@ -93,16 +103,6 @@ fn main() { let (compositor_token, shell_state_token, window_map) = init_shell(&mut event_loop, log.clone()); - /* - * Initialize glium - */ - { - let drawer = event_loop.state().get(&renderer_token); - let mut frame = drawer.draw(); - frame.clear_color(0.8, 0.8, 0.9, 1.0); - frame.finish().unwrap(); - } - /* * Add a listening socket: */ @@ -112,9 +112,10 @@ fn main() { /* * Register the DrmDevice on the EventLoop */ + let device_token = event_loop.state().insert(device); let _source = drm_device_bind( &mut event_loop, - device, + device_token, DrmHandlerImpl { shell_state_token, compositor_token, @@ -139,10 +140,11 @@ pub struct DrmHandlerImpl { } impl DrmHandler> for DrmHandlerImpl { - fn ready(&mut self, evlh: &mut EventLoopHandle, _device: &mut DrmDevice>, - backend: &StateToken>, _frame: u32, _duration: Duration) { + fn ready(&mut self, evlh: &mut EventLoopHandle, device: &StateToken>>, + crtc: &crtc::Handle, _frame: u32, _duration: Duration) { let state = evlh.state(); - let drawer = state.get(backend); + let dev = state.get(device); + let drawer = dev.backend_for_crtc(crtc).unwrap(); let mut frame = drawer.draw(); frame.clear_color(0.8, 0.8, 0.9, 1.0); // redraw the frame, in a simple but inneficient way @@ -185,7 +187,7 @@ impl DrmHandler> for DrmHandlerImpl { frame.finish().unwrap(); } - fn error(&mut self, _evlh: &mut EventLoopHandle, _device: &mut DrmDevice>, + fn error(&mut self, _evlh: &mut EventLoopHandle, _device: &StateToken>>, error: DrmError) { panic!("{:?}", error); } diff --git a/examples/udev.rs b/examples/udev.rs new file mode 100644 index 0000000..8d48c95 --- /dev/null +++ b/examples/udev.rs @@ -0,0 +1,261 @@ +extern crate drm; +#[macro_use] +extern crate glium; +extern crate rand; +extern crate libudev; +#[macro_use(define_roles)] +extern crate smithay; +extern crate wayland_server; + +#[macro_use] +extern crate slog; +extern crate slog_async; +extern crate slog_term; + +extern crate ctrlc; + +mod helpers; + +use drm::control::{Device as ControlDevice, ResourceInfo}; +use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState}; +use drm::control::encoder::Info as EncoderInfo; +use drm::control::crtc; +use drm::result::Error as DrmError; +use glium::Surface; +use helpers::{init_shell, GliumDrawer, MyWindowMap, Roles, SurfaceData}; +use slog::{Drain, Logger}; +use smithay::backend::drm::{DrmBackend, DrmDevice, DrmHandler}; +use smithay::backend::graphics::egl::EGLGraphicsBackend; +use smithay::backend::udev::{UdevBackend, UdevHandler, udev_backend_bind}; +use smithay::backend::session::SessionNotifier; +use smithay::backend::session::direct::{direct_session_bind, DirectSession}; +use smithay::wayland::compositor::{CompositorToken, SubsurfaceRole, TraversalAction}; +use smithay::wayland::compositor::roles::Role; +use smithay::wayland::shell::ShellState; +use smithay::wayland::shm::init_shm_global; +use std::cell::RefCell; +use std::collections::HashSet; +use std::io::Error as IoError; +use std::rc::Rc; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; +use std::time::Duration; +use wayland_server::{StateToken, StateProxy}; + +fn main() { + // A logger facility, here we use the terminal for this example + let log = Logger::root( + slog_term::FullFormat::new(slog_term::PlainSyncDecorator::new(std::io::stdout())).build().fuse(), + o!(), + ); + + // Initialize the wayland server + let (mut display, mut event_loop) = wayland_server::create_display(); + + /* + * Initialize the compositor + */ + init_shm_global(&mut event_loop, vec![], log.clone()); + + let (compositor_token, shell_state_token, window_map) = init_shell(&mut event_loop, log.clone()); + + /* + * Initialize session on the current tty + */ + let (session, mut notifier) = DirectSession::new(None, log.clone()).unwrap(); + let session_token = event_loop.state().insert(session); + + let running = Arc::new(AtomicBool::new(true)); + let r = running.clone(); + ctrlc::set_handler(move || { + r.store(false, Ordering::SeqCst); + }).expect("Error setting Ctrl-C handler"); + + /* + * Initialize the udev backend + */ + let context = libudev::Context::new().unwrap(); + let udev + = UdevBackend::new(&mut event_loop, &context, &session_token, UdevHandlerImpl { + shell_state_token, + compositor_token, + window_map: window_map.clone(), + logger: log.clone(), + }, log.clone()).unwrap(); + + let udev_token = event_loop.state().insert(udev); + let udev_session_id = notifier.register(udev_token.clone()); + let session_event_source = direct_session_bind(notifier, &mut event_loop, log.clone()).unwrap(); + let udev_event_source = udev_backend_bind(&mut event_loop, udev_token).unwrap(); + /* + * Add a listening socket: + */ + let name = display.add_socket_auto().unwrap().into_string().unwrap(); + println!("Listening on socket: {}", name); + + while running.load(Ordering::SeqCst) { + event_loop.dispatch(Some(16)).unwrap(); + display.flush_clients(); + window_map.borrow_mut().refresh(); + } + + let mut notifier = session_event_source.remove(); + notifier.unregister(udev_session_id); + + let udev_token = udev_event_source.remove(); + let udev = event_loop.state().remove(udev_token); + udev.close(event_loop.state()); + + event_loop.state().remove(session_token); +} + +struct UdevHandlerImpl { + shell_state_token: StateToken>, + compositor_token: CompositorToken, + window_map: Rc>, + logger: ::slog::Logger, +} + +impl UdevHandlerImpl { + pub fn scan_connectors<'a, S: Into>>(&self, state: S, device: &mut DrmDevice>) { + // 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 = res_handles + .connectors() + .iter() + .map(|conn| { + ConnectorInfo::load_from_device(device, *conn).unwrap() + }) + .filter(|conn| conn.connection_state() == ConnectorState::Connected) + .inspect(|conn| info!(self.logger, "Connected: {:?}", conn.connector_type())) + .collect(); + + let mut used_crtcs: HashSet = HashSet::new(); + + let mut state = state.into(); + + // 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| EncoderInfo::load_from_device(device, *encoder_handle)).collect::>(); + for encoder_info in encoder_infos { + for crtc in res_handles.filter_crtcs(encoder_info.possible_crtcs()) { + if !used_crtcs.contains(&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_token = device.create_backend(&mut state, crtc, mode, vec![connector_info.handle()]).unwrap(); + + // render first frame + { + let renderer = state.get_mut(renderer_token); + let mut frame = renderer.draw(); + frame.clear_color(0.8, 0.8, 0.9, 1.0); + frame.finish().unwrap(); + } + + used_crtcs.insert(crtc); + break; + } + } + } + } + } +} + +impl UdevHandler, DrmHandlerImpl> for UdevHandlerImpl { + fn device_added<'a, S: Into>>(&mut self, state: S, device: &mut DrmDevice>) -> Option + { + self.scan_connectors(state, device); + + Some(DrmHandlerImpl { + shell_state_token: self.shell_state_token.clone(), + compositor_token: self.compositor_token.clone(), + window_map: self.window_map.clone(), + logger: self.logger.clone(), + }) + } + + fn device_changed<'a, S: Into>>(&mut self, state: S, device: &StateToken>>) { + //quick and dirty + let mut state = state.into(); + self.device_removed(&mut state, device); + state.with_value(device, |state, device| self.scan_connectors(state, device)); + } + + fn device_removed<'a, S: Into>>(&mut self, state: S, device: &StateToken>>) { + state.into().with_value(device, |state, device| { + let crtcs = device.current_backends().into_iter().map(|backend| state.get(backend).crtc()).collect::>(); + let mut state: StateProxy = state.into(); + for crtc in crtcs { + device.destroy_backend(&mut state, &crtc); + } + }); + } + + fn error<'a, S: Into>>(&mut self, _state: S, error: IoError) { + error!(self.logger, "{:?}", error); + } +} + +pub struct DrmHandlerImpl { + shell_state_token: StateToken>, + compositor_token: CompositorToken, + window_map: Rc>, + logger: ::slog::Logger, +} + +impl DrmHandler> for DrmHandlerImpl { + fn ready<'a, S: Into>>(&mut self, state: S, _device: &mut DrmDevice>, + backend: &StateToken>, _crtc: crtc::Handle, _frame: u32, _duration: Duration) { + let state = state.into(); + let drawer = state.get(backend); + let mut frame = drawer.draw(); + frame.clear_color(0.8, 0.8, 0.9, 1.0); + // redraw the frame, in a simple but inneficient way + { + let screen_dimensions = drawer.get_framebuffer_dimensions(); + self.window_map + .borrow() + .with_windows_from_bottom_to_top(|toplevel_surface, initial_place| { + if let Some(wl_surface) = toplevel_surface.get_surface() { + // this surface is a root of a subsurface tree that needs to be drawn + self.compositor_token + .with_surface_tree_upward( + wl_surface, + initial_place, + |_surface, attributes, role, &(mut x, mut y)| { + if let Some((ref contents, (w, h))) = attributes.user_data.buffer { + // there is actually something to draw ! + if let Ok(subdata) = Role::::data(role) { + x += subdata.x; + y += subdata.y; + } + drawer.render( + &mut frame, + contents, + (w, h), + (x, y), + screen_dimensions, + ); + TraversalAction::DoChildren((x, y)) + } else { + // we are not display, so our children are neither + TraversalAction::SkipChildren + } + }, + ) + .unwrap(); + } + }); + } + if let Err(err) = frame.finish() { + error!(self.logger, "Error during rendering: {:?}", err); + } + } + + fn error<'a, S: Into>>(&mut self, _state: S, _device: &mut DrmDevice>, + error: DrmError) { + error!(self.logger, "{:?}", error); + } +} diff --git a/src/backend/drm/backend.rs b/src/backend/drm/backend.rs index d2ccd95..d2197bc 100644 --- a/src/backend/drm/backend.rs +++ b/src/backend/drm/backend.rs @@ -92,7 +92,7 @@ impl DrmBackend { 1, 1, GbmFormat::ARGB8888, - &[BufferObjectFlags::Cursor, BufferObjectFlags::Write], + BufferObjectFlags::CURSOR | BufferObjectFlags::WRITE, ) .chain_err(|| ErrorKind::GbmInitFailed)?) }, @@ -107,7 +107,7 @@ impl DrmBackend { w as u32, h as u32, GbmFormat::XRGB8888, - &[BufferObjectFlags::Scanout, BufferObjectFlags::Rendering], + BufferObjectFlags::SCANOUT | BufferObjectFlags::RENDERING, ) .chain_err(|| ErrorKind::GbmInitFailed)?) }, @@ -311,7 +311,7 @@ impl DrmBackend { w as u32, h as u32, GbmFormat::XRGB8888, - &[BufferObjectFlags::Scanout, BufferObjectFlags::Rendering], + BufferObjectFlags::SCANOUT | BufferObjectFlags::RENDERING, ) .chain_err(|| ErrorKind::GbmInitFailed)?) }, @@ -374,6 +374,10 @@ impl DrmBackend { self.mode = mode; Ok(()) } + + pub fn crtc(&self) -> crtc::Handle { + self.crtc + } } impl Drop for DrmBackend { @@ -437,7 +441,7 @@ impl GraphicsBackend for DrmBackend { w, h, GbmFormat::ARGB8888, - &[BufferObjectFlags::Cursor, BufferObjectFlags::Write], + BufferObjectFlags::CURSOR | BufferObjectFlags::WRITE, ) .chain_err(|| ErrorKind::GbmInitFailed)?; cursor @@ -523,7 +527,7 @@ impl EGLGraphicsBackend for DrmBackend { } fn is_current(&self) -> bool { - self.graphics.head().rent(|context| context.is_current()) + self.graphics.rent_all(|graphics| graphics.context.egl.is_current() && graphics.gbm.surface.rent(|egl| egl.surface.is_current())) } unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> { diff --git a/src/backend/drm/error.rs b/src/backend/drm/error.rs index 00cd73c..35517ee 100644 --- a/src/backend/drm/error.rs +++ b/src/backend/drm/error.rs @@ -8,6 +8,11 @@ use rental::TryNewError; error_chain! { errors { + #[doc = "Unable to acquire drm master"] + DrmMasterFailed { + description("Failed to acquire drm master") + } + #[doc = "The `DrmDevice` encountered an access error"] DrmDev(dev: String) { description("The drm device encountered an access error"), @@ -26,7 +31,13 @@ error_chain! { display("Swapping front buffers failed"), } - #[doc = "mode is not compatible with all given connectors"] + #[doc = "Device is currently paused"] + DeviceInactive { + description("Device is currently paused, operation rejected"), + display("Device is currently paused, operation rejected"), + } + + #[doc = "Mode is not compatible with all given connectors"] ModeNotSuitable(mode: Mode) { description("Mode is not compatible with all given connectors"), display("Mode ({:?}) is not compatible with all given connectors", mode), diff --git a/src/backend/drm/mod.rs b/src/backend/drm/mod.rs index 9278b7f..1b8bacd 100644 --- a/src/backend/drm/mod.rs +++ b/src/backend/drm/mod.rs @@ -183,20 +183,27 @@ //! ``` use backend::graphics::egl::{EGLContext, GlAttributes, PixelFormatRequirements}; +#[cfg(feature = "backend_session")] +use backend::session::SessionObserver; +use backend::graphics::egl::EGLGraphicsBackend; use drm::Device as BasicDevice; use drm::control::{connector, crtc, encoder, Mode, ResourceInfo}; use drm::control::Device as ControlDevice; use drm::result::Error as DrmError; use gbm::Device as GbmDevice; use nix; +use nix::Result as NixResult; +use nix::unistd::close; use std::borrow::Borrow; use std::collections::HashMap; use std::fs::File; +use std::hash::{Hash, Hasher}; use std::io::Result as IoResult; -use std::os::unix::io::{AsRawFd, RawFd}; +use std::os::unix::io::{IntoRawFd, AsRawFd, RawFd}; use std::rc::Rc; +use std::mem; use std::time::Duration; -use wayland_server::{EventLoopHandle, StateToken}; +use wayland_server::{EventLoopHandle, StateToken, StateProxy}; use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest}; mod backend; @@ -207,11 +214,11 @@ use self::error::*; /// Internal struct as required by the drm crate #[derive(Debug)] -pub(crate) struct DrmDev(File); +pub(crate) struct DrmDev(RawFd); impl AsRawFd for DrmDev { fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() + self.0 } } impl BasicDevice for DrmDev {} @@ -219,12 +226,11 @@ impl ControlDevice for DrmDev {} impl DrmDev { unsafe fn new_from_fd(fd: RawFd) -> Self { - use std::os::unix::io::FromRawFd; - DrmDev(File::from_raw_fd(fd)) + DrmDev(fd) } fn new_from_file(file: File) -> Self { - DrmDev(file) + DrmDev(file.into_raw_fd()) } } @@ -256,6 +262,7 @@ use self::devices::{Context, Devices}; pub struct DrmDevice + 'static> { context: Rc, backends: HashMap>, + active: bool, logger: ::slog::Logger, } @@ -351,6 +358,9 @@ impl + Borrow + 'static> DrmDevice { info!(log, "DrmDevice initializing"); + // we want to mode-set, so we better be the master + drm.set_master().chain_err(|| ErrorKind::DrmMasterFailed)?; + // Open the gbm device from the drm device and create a context based on that Ok(DrmDevice { context: Rc::new(Context::try_new( @@ -374,6 +384,7 @@ impl + Borrow + 'static> DrmDevice { }, )?), backends: HashMap::new(), + active: true, logger: log, }) } @@ -383,16 +394,20 @@ impl + Borrow + 'static> DrmDevice { /// /// Errors if initialization fails or the mode is not available on all given /// connectors. - pub fn create_backend(&mut self, evlh: &mut EventLoopHandle, crtc: crtc::Handle, mode: Mode, - connectors: I) - -> Result> + pub fn create_backend<'a, I, S>(&mut self, state: S, crtc: crtc::Handle, mode: Mode, connectors: I) + -> Result<&StateToken> where I: Into>, + S: Into>, { if self.backends.contains_key(&crtc) { bail!(ErrorKind::CrtcAlreadyInUse(crtc)); } + if !self.active { + bail!(ErrorKind::DeviceInactive); + } + // check if the given connectors and crtc match let connectors = connectors.into(); @@ -437,10 +452,32 @@ impl + Borrow + 'static> DrmDevice { let logger = self.logger.new(o!("crtc" => format!("{:?}", crtc))); let backend = DrmBackend::new(self.context.clone(), crtc, mode, connectors, logger)?; - let token = evlh.state().insert(backend.into()); - self.backends.insert(crtc, token.clone()); + self.backends.insert(crtc, state.into().insert(backend.into())); - Ok(token) + Ok(self.backends.get(&crtc).unwrap()) + } + + pub fn backend_for_crtc(&self, crtc: &crtc::Handle) -> Option<&StateToken> { + self.backends.get(crtc) + } + + pub fn current_backends(&self) -> Vec<&StateToken> { + self.backends.values().collect() + } + + pub fn destroy_backend<'a, S>(&mut self, state: S, crtc: &crtc::Handle) + where + S: Into> + { + if let Some(token) = self.backends.remove(crtc) { + state.into().remove(token); + } + } + + pub fn close(self) -> NixResult<()> { + let fd = self.as_raw_fd(); + mem::drop(self); + close(fd) } } @@ -450,9 +487,27 @@ impl + 'static> AsRawFd for DrmDevice { self.context.head().head().as_raw_fd() } } + impl + 'static> BasicDevice for DrmDevice {} impl + 'static> ControlDevice for DrmDevice {} +impl + 'static> Drop for DrmDevice { + fn drop(&mut self) { + if Rc::strong_count(&self.context) > 1 { + panic!("Pending DrmBackends. Please free all backends before the DrmDevice gets destroyed"); + } + if let Err(err) = self.drop_master() { + error!(self.logger, "Failed to drop drm master state. Error: {}", err); + } + } +} + +impl + 'static> Hash for DrmDevice { + fn hash(&self, state: &mut H) { + self.as_raw_fd().hash(state) + } +} + /// Handler for drm node events /// /// See module-level documentation for its use @@ -462,61 +517,99 @@ pub trait DrmHandler + 'static> { /// /// The `id` argument is the `Id` of the `DrmBackend` that finished rendering, /// check using `DrmBackend::is`. - fn ready(&mut self, evlh: &mut EventLoopHandle, device: &mut DrmDevice, backend: &StateToken, - frame: u32, duration: Duration); + fn ready<'a, S: Into>>(&mut self, state: S, device: &mut DrmDevice, backend: &StateToken, + 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, evlh: &mut EventLoopHandle, device: &mut DrmDevice, error: DrmError); + fn error<'a, S: Into>>(&mut self, state: S, device: &mut DrmDevice, 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(evlh: &mut EventLoopHandle, device: DrmDevice, handler: H) - -> IoResult, H)>> +pub fn drm_device_bind(evlh: &mut EventLoopHandle, device: StateToken>, handler: H) + -> IoResult>, H)>> where - B: Borrow + 'static, + B: From + Borrow + 'static, H: DrmHandler + 'static, { + let fd = evlh.state().get(&device).as_raw_fd(); evlh.add_fd_event_source( - device.as_raw_fd(), + fd, fd_event_source_implementation(), (device, handler), FdInterest::READ, ) } -fn fd_event_source_implementation() -> FdEventSourceImpl<(DrmDevice, H)> +fn fd_event_source_implementation() -> FdEventSourceImpl<(StateToken>, H)> where - B: Borrow + 'static, + B: From + Borrow + 'static, H: DrmHandler + 'static, { FdEventSourceImpl { - ready: |evlh, id, _, _| { - let &mut (ref mut dev, ref mut handler) = id; + ready: |evlh, &mut (ref mut dev_token, ref mut handler), _, _| { + let (events, logger) = { + let dev = evlh.state().get(dev_token); + let events = crtc::receive_events(dev); + let logger = dev.logger.clone(); + (events, logger) + }; - let events = crtc::receive_events(dev); match events { Ok(events) => for event in events { if let crtc::Event::PageFlip(event) = event { - let token = dev.backends.get(&event.crtc).cloned(); - if let Some(token) = token { - // we can now unlock the buffer - evlh.state().get(&token).borrow().unlock_buffer(); - trace!(dev.logger, "Handling event for backend {:?}", event.crtc); - // and then call the user to render the next frame - handler.ready(evlh, dev, &token, event.frame, event.duration); - } + evlh.state().with_value(dev_token, |state, mut dev| { + if dev.active { + if let Some(backend_token) = dev.backend_for_crtc(&event.crtc).cloned() { + // we can now unlock the buffer + state.get(&backend_token).borrow().unlock_buffer(); + trace!(logger, "Handling event for backend {:?}", event.crtc); + // and then call the user to render the next frame + handler.ready(state, &mut dev, &backend_token, event.crtc, event.frame, event.duration); + } + } + }); } }, - Err(err) => handler.error(evlh, dev, err), + Err(err) => evlh.state().with_value(dev_token, |state, mut dev| handler.error(state, &mut dev, err)), }; }, - error: |evlh, id, _, error| { - warn!(id.0.logger, "DrmDevice errored: {}", error); - id.1.error(evlh, &mut id.0, error.into()); + error: |evlh, &mut (ref mut dev_token, ref mut handler), _, error| { + evlh.state().with_value(dev_token, |state, mut dev| { + warn!(dev.logger, "DrmDevice errored: {}", error); + handler.error(state, &mut dev, error.into()); + }) }, } } + +#[cfg(feature = "backend_session")] +impl + 'static> SessionObserver for StateToken> { + fn pause<'a>(&mut self, state: &mut StateProxy<'a>) { + let device: &mut DrmDevice = state.get_mut(self); + device.active = false; + if let Err(err) = device.drop_master() { + error!(device.logger, "Failed to drop drm master state. Error: {}", err); + } + } + + fn activate<'a>(&mut self, state: &mut StateProxy<'a>) { + state.with_value(self, |state, device| { + device.active = true; + if let Err(err) = device.set_master() { + crit!(device.logger, "Failed to acquire drm master again. Error: {}", err); + } + for token in device.backends.values() { + let backend = state.get(token); + if let Err(err) = backend.borrow().swap_buffers() { + // TODO handle this better? + error!(device.logger, "Failed to activate crtc ({:?}) again. Error: {}", backend.borrow().crtc(), err); + } + } + }) + + } +} diff --git a/src/backend/graphics/egl.rs b/src/backend/graphics/egl.rs index ad809d8..f057777 100644 --- a/src/backend/graphics/egl.rs +++ b/src/backend/graphics/egl.rs @@ -817,7 +817,7 @@ impl<'context, 'surface, T: NativeSurface> EGLSurface<'context, 'surface, T> { if ret == 0 { match unsafe { self.context.egl.GetError() } as u32 { ffi::egl::CONTEXT_LOST => Err(SwapBuffersError::ContextLost), - err => panic!("eglSwapBuffers failed (eglGetError returned 0x{:x})", err), + err => Err(SwapBuffersError::Unknown(err)), } } else { Ok(()) @@ -847,6 +847,12 @@ impl<'context, 'surface, T: NativeSurface> EGLSurface<'context, 'surface, T> { Ok(()) } } + + /// Returns true if the OpenGL surface is the current one in the thread. + pub fn is_current(&self) -> bool { + unsafe { self.context.egl.GetCurrentSurface(ffi::egl::DRAW as _) == self.surface as *const _ && + self.context.egl.GetCurrentSurface(ffi::egl::READ as _) == self.surface as *const _ } + } } unsafe impl<'a, 'b, T: NativeSurface> Send for EGLSurface<'a, 'b, T> {} @@ -882,6 +888,8 @@ pub enum SwapBuffersError { /// 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 { @@ -897,6 +905,9 @@ impl error::Error for SwapBuffersError { 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" } } } diff --git a/src/backend/graphics/glium.rs b/src/backend/graphics/glium.rs index 42ffedf..484b798 100644 --- a/src/backend/graphics/glium.rs +++ b/src/backend/graphics/glium.rs @@ -14,6 +14,7 @@ impl From for GliumSwapBuffersError { match error { SwapBuffersError::ContextLost => GliumSwapBuffersError::ContextLost, SwapBuffersError::AlreadySwapped => GliumSwapBuffersError::AlreadySwapped, + SwapBuffersError::Unknown(_) => GliumSwapBuffersError::ContextLost, // TODO } } } diff --git a/src/backend/input.rs b/src/backend/input.rs index 6baf26b..e08b439 100644 --- a/src/backend/input.rs +++ b/src/backend/input.rs @@ -1,6 +1,5 @@ //! Common traits for input backends to receive input from. -use backend::{SeatInternal, TouchSlotInternal}; use std::error::Error; /// A seat describes a group of input devices and at least one @@ -20,20 +19,18 @@ pub struct Seat { capabilities: SeatCapabilities, } -impl SeatInternal for Seat { - fn new(id: u64, capabilities: SeatCapabilities) -> Seat { +impl Seat { + pub(crate) fn new(id: u64, capabilities: SeatCapabilities) -> Seat { Seat { id: id, capabilities: capabilities, } } - fn capabilities_mut(&mut self) -> &mut SeatCapabilities { + pub(crate) fn capabilities_mut(&mut self) -> &mut SeatCapabilities { &mut self.capabilities } -} -impl Seat { /// Get the currently capabilities of this `Seat` pub fn capabilities(&self) -> &SeatCapabilities { &self.capabilities @@ -318,8 +315,8 @@ pub struct TouchSlot { id: u64, } -impl TouchSlotInternal for TouchSlot { - fn new(id: u64) -> Self { +impl TouchSlot { + pub(crate) fn new(id: u64) -> Self { TouchSlot { id: id } } } diff --git a/src/backend/libinput.rs b/src/backend/libinput.rs index c55ddf8..4f7cd06 100644 --- a/src/backend/libinput.rs +++ b/src/backend/libinput.rs @@ -1,6 +1,5 @@ //! Implementation of input backend trait for types provided by `libinput` -use backend::{SeatInternal, TouchSlotInternal}; use backend::input as backend; use input as libinput; use input::event; diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 09bb18f..f73914a 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -23,14 +23,7 @@ pub mod winit; pub mod drm; #[cfg(feature = "backend_libinput")] pub mod libinput; - -// Internal functions that need to be accessible by the different backend implementations - -trait SeatInternal { - fn new(id: u64, capabilities: input::SeatCapabilities) -> Self; - fn capabilities_mut(&mut self) -> &mut input::SeatCapabilities; -} - -trait TouchSlotInternal { - fn new(id: u64) -> Self; -} +#[cfg(feature = "backend_session")] +pub mod session; +#[cfg(feature = "backend_udev")] +pub mod udev; diff --git a/src/backend/session/direct.rs b/src/backend/session/direct.rs new file mode 100644 index 0000000..a74def2 --- /dev/null +++ b/src/backend/session/direct.rs @@ -0,0 +1,385 @@ +use std::io::Result as IoResult; +use std::path::Path; +use std::os::unix::io::RawFd; +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; +use nix::{Error as NixError, Result as NixResult}; +use nix::fcntl::{self, open}; +use nix::libc::c_int; +use nix::sys::signal::{self, Signal}; +use nix::sys::stat::{dev_t, major, minor, Mode, fstat}; +use nix::unistd::{dup, close}; +use wayland_server::EventLoopHandle; +use wayland_server::sources::SignalEventSource; + +#[cfg(feature = "backend_session_udev")] +use libudev::Context; + +use super::{Session, SessionNotifier, SessionObserver}; + +mod tty { + ioctl!(bad read kd_get_mode with 0x4B3B; i16); + ioctl!(bad write_int kd_set_mode with 0x4B3A); + pub const KD_TEXT: i16 = 0x00; + pub const KD_GRAPHICS: i16 = 0x00; + + ioctl!(bad read kd_get_kb_mode with 0x4B44; i32); + ioctl!(bad write_int kd_set_kb_mode with 0x4B45); + pub const K_RAW: i32 = 0x00; + pub const K_XLATE: i32 = 0x01; + pub const K_MEDIUMRAW: i32 = 0x02; + pub const K_UNICODE: i32 = 0x03; + pub const K_OFF: i32 = 0x04; + + ioctl!(bad write_int vt_activate with 0x5606); + ioctl!(bad write_int vt_wait_active with 0x5607); + ioctl!(bad write_ptr vt_set_mode with 0x5602; VtMode); + #[repr(C)] + #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] + pub struct VtMode { + /// vt mode + pub mode: i8, + /// if set, hang on writes if not active + pub waitv: i8, + /// signal to raise on release req + pub relsig: i16, + /// signal to raise on acquisition + pub acqsig: i16, + /// unused (set to 0) + pub frsig: i16, + } + pub const VT_AUTO: i8 = 0x00; + pub const VT_PROCESS: i8 = 0x01; + pub const VT_ACKACQ: i8 = 0x02; + + extern { + pub fn __libc_current_sigrtmin() -> i8; + pub fn __libc_current_sigrtmax() -> i8; + } +} + + +// on freebsd and dragonfly +#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] +const DRM_MAJOR: u64 = 145; + +// on netbsd +#[cfg(target_os = "netbsd")] +const DRM_MAJOR: u64 = 34; + +// on openbsd (32 & 64 bit) +#[cfg(all(target_os = "openbsd", target_pointer_width = "32"))] +const DRM_MAJOR: u64 = 88; +#[cfg(all(target_os = "openbsd", target_pointer_width = "64"))] +const DRM_MAJOR: u64 = 87; + +// on linux/android +#[cfg(any(target_os = "linux", target_os = "android"))] +const DRM_MAJOR: u64 = 226; + +#[cfg(any(target_os = "linux", target_os = "android"))] +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"))] +fn is_drm_device(dev: dev_t, _path: &Path) -> bool { + major(dev) == DRM_MAJOR +} + +#[cfg(not(feature = "backend_session_udev"))] +fn is_tty_device(dev: dev_t, _path: Option<&Path>) -> bool { + major(dev) == TTY_MAJOR +} + +#[cfg(feature = "backend_session_udev")] +fn is_drm_device(dev: dev_t, path: &Path) -> bool { + let udev = match Context::new() { + Ok(context) => context, + Err(_) => return major(dev) == DRM_MAJOR, + }; + + let device = match udev.device_from_syspath(path) { + Ok(device) => device, + Err(_) => return major(dev) == DRM_MAJOR, + }; + + if let Some(subsystem) = device.subsystem() { + subsystem == "drm" + } else { + major(dev) == DRM_MAJOR + } +} + +#[cfg(feature = "backend_session_udev")] +fn is_tty_device(dev: dev_t, path: Option<&Path>) -> bool { + match path { + Some(path) => { + let udev = match Context::new() { + Ok(context) => context, + Err(_) => return major(dev) == TTY_MAJOR || minor(dev) != 0, + }; + + let device = match udev.device_from_syspath(path) { + Ok(device) => device, + Err(_) => return major(dev) == TTY_MAJOR || minor(dev) != 0, + }; + + let res = if let Some(subsystem) = device.subsystem() { + subsystem == "tty" + } else { + major(dev) == TTY_MAJOR + }; + res || minor(dev) != 0 + }, + None => major(dev) == TTY_MAJOR || minor(dev) != 0 + } +} + +pub struct DirectSession { + tty: RawFd, + active: Arc, + vt: i32, + old_keyboard_mode: i32, + logger: ::slog::Logger, +} + +pub struct DirectSessionNotifier { + active: Arc, + signals: Vec>>, + signal: Signal, + logger: ::slog::Logger, +} + +impl DirectSession { + pub fn new(tty: Option<&Path>, logger: L) -> Result<(DirectSession, DirectSessionNotifier)> + where + L: Into> + { + let logger = ::slog_or_stdlog(logger).new(o!("smithay_module" => "backend_session", "session_type" => "direct/vt")); + + let fd = tty + .map(|path| open(path, fcntl::O_RDWR | fcntl::O_CLOEXEC, Mode::empty()) + .chain_err(|| ErrorKind::FailedToOpenTTY(String::from(path.to_string_lossy())))) + .unwrap_or(dup(0 /*stdin*/).chain_err(|| ErrorKind::FailedToOpenTTY(String::from(""))))?; + + let active = Arc::new(AtomicBool::new(true)); + + match DirectSession::setup_tty(tty, fd, logger.clone()) { + Ok((vt, old_keyboard_mode, signal)) => { + Ok((DirectSession { + tty: fd, + active: active.clone(), + vt, + old_keyboard_mode, + logger: logger.new(o!("vt" => format!("{}", vt), "component" => "session")), + }, DirectSessionNotifier { + active, + signals: Vec::new(), + signal, + logger: logger.new(o!("vt" => format!("{}", vt), "component" => "session_notifier")) + })) + }, + Err(err) => { + let _ = close(fd); + Err(err) + } + } + } + + fn setup_tty(path: Option<&Path>, tty: RawFd, logger: ::slog::Logger) -> Result<(i32, i32, Signal)> { + let stat = fstat(tty).chain_err(|| ErrorKind::NotRunningFromTTY)?; + if !is_tty_device(stat.st_dev, path) { + bail!(ErrorKind::NotRunningFromTTY); + } + + let vt_num = minor(stat.st_dev) as i32 - 1; + info!(logger, "Running from tty: {}", vt_num); + + let mut mode = 0; + unsafe { + tty::kd_get_mode(tty, &mut mode).chain_err(|| ErrorKind::NotRunningFromTTY)?; + } + if mode != tty::KD_TEXT { + bail!(ErrorKind::TTYAlreadyInGraphicsMode); + } + + unsafe { + tty::vt_activate(tty, vt_num as c_int).chain_err(|| ErrorKind::FailedToActivateTTY(vt_num))?; + tty::vt_wait_active(tty, vt_num as c_int).chain_err(|| ErrorKind::FailedToWaitForTTY(vt_num))?; + } + + let mut old_keyboard_mode = 0; + unsafe { + tty::kd_get_kb_mode(tty, &mut old_keyboard_mode).chain_err(|| ErrorKind::FailedToSaveTTYState(vt_num))?; + tty::kd_set_kb_mode(tty, tty::K_OFF).chain_err(|| ErrorKind::FailedToSetTTYKbMode(vt_num))?; + tty::kd_set_mode(tty, tty::KD_GRAPHICS as i32).chain_err(|| ErrorKind::FailedToSetTTYMode(vt_num))?; + } + + // TODO: Support realtime signals + // https://github.com/nix-rust/nix/issues/495 + /* + let signal = if tty::__libc_current_sigrtmin() > tty::__libc_current_sigrtmax() { + warn!(logger, "Not enough real-time signals available, falling back to USR1"); + nix::sys::signal::SIGUSR1 as i32 + } else { + tty::__libc_current_sigrtmin() + };*/ + let signal = signal::SIGUSR1; + + let mode = tty::VtMode { + mode: tty::VT_PROCESS, + relsig: signal as i16, + acqsig: signal as i16, + ..Default::default() + }; + + unsafe { + tty::vt_set_mode(tty, &mode).chain_err(|| ErrorKind::FailedToTakeControlOfTTY(vt_num))?; + } + + Ok((vt_num, old_keyboard_mode, signal)) + } +} + +impl Session for DirectSession { + type Error = NixError; + + fn open(&mut self, path: &Path) -> NixResult { + open(path, fcntl::O_RDWR | fcntl::O_CLOEXEC | fcntl::O_NOCTTY | fcntl::O_NONBLOCK, Mode::empty()) + } + + fn close(&mut self, fd: RawFd) -> NixResult<()> { + close(fd) + } + + fn is_active(&self) -> bool { + self.active.load(Ordering::SeqCst) + } + + fn seat(&self) -> &str { + // The VT api can only be used on seat0 + return "seat0" + } + + fn change_vt(&mut self, vt_num: i32) -> NixResult<()> { + unsafe { tty::vt_activate(self.tty, vt_num).map(|_| ()) } + } +} + +impl Drop for DirectSession { + fn drop(&mut self) { + if let Err(err) = unsafe { tty::kd_set_kb_mode(self.tty, self.old_keyboard_mode) } { + warn!(self.logger, "Unable to restore vt keyboard mode. Error: {}", err); + } + if let Err(err) = unsafe { tty::kd_set_mode(self.tty, tty::KD_TEXT as i32) } { + warn!(self.logger, "Unable to restore vt text mode. Error: {}", err); + } + if let Err(err) = unsafe { tty::vt_set_mode(self.tty, &tty::VtMode { + mode: tty::VT_AUTO, + ..Default::default() + }) } { + error!(self.logger, "Failed to reset vt handling. Error: {}", err); + } + if let Err(err) = close(self.tty) { + error!(self.logger, "Failed to close tty file descriptor. Error: {}", err); + } + } +} + +impl SessionNotifier for DirectSessionNotifier { + fn register(&mut self, signal: S) -> usize { + self.signals.push(Some(Box::new(signal))); + self.signals.len() - 1 + } + fn unregister(&mut self, signal: usize) { + self.signals[signal] = None; + } + + fn is_active(&self) -> bool { + self.active.load(Ordering::SeqCst) + } + fn seat(&self) -> &str { + "seat0" + } +} + +pub fn direct_session_bind(notifier: DirectSessionNotifier, evlh: &mut EventLoopHandle, _logger: L) + -> IoResult> +where + L: Into>, +{ + let signal = notifier.signal; + + evlh.add_signal_event_source(|evlh, notifier, _| { + if notifier.is_active() { + for signal in &mut notifier.signals { + if let &mut Some(ref mut signal) = signal {signal.pause(&mut evlh.state().as_proxy()); } + } + notifier.active.store(false, Ordering::SeqCst); + } else { + for signal in &mut notifier.signals { + if let &mut Some(ref mut signal) = signal { signal.activate(&mut evlh.state().as_proxy()); } + } + notifier.active.store(true, Ordering::SeqCst); + } + }, notifier, signal) +} + +error_chain! { + errors { + #[doc = "Failed to open tty"] + FailedToOpenTTY(path: String) { + description("Failed to open tty"), + display("Failed to open tty ({:?})", path), + } + + #[doc = "Not running from a tty"] + NotRunningFromTTY { + description("Not running from a tty"), + } + + #[doc = "tty is already in KB_GRAPHICS mode"] + TTYAlreadyInGraphicsMode { + description("The tty is already in KB_GRAPHICS mode"), + display("The tty is already in graphics mode, is already a compositor running?"), + } + + #[doc = "Failed to activate open tty"] + FailedToActivateTTY(num: i32) { + description("Failed to activate open tty"), + display("Failed to activate open tty ({:?})", num), + } + + #[doc = "Failed to wait for tty to become active"] + FailedToWaitForTTY(num: i32) { + description("Failed to wait for tty to become active"), + display("Failed to wait for tty ({:?}) to become active", num), + } + + #[doc = "Failed to save old tty state"] + FailedToSaveTTYState(num: i32) { + description("Failed to save old tty state"), + display("Failed to save old tty ({:?}) state", num), + } + + #[doc = "Failed to set tty kb mode"] + FailedToSetTTYKbMode(num: i32) { + description("Failed to set tty kb mode to K_OFF"), + display("Failed to set tty ({:?}) kb mode to K_OFF", num), + } + + #[doc = "Failed to set tty mode"] + FailedToSetTTYMode(num: i32) { + description("Failed to set tty mode to KD_GRAPHICS"), + display("Failed to set tty ({:?}) mode into graphics mode", num), + } + + #[doc = "Failed to set tty in process mode"] + FailedToTakeControlOfTTY(num: i32) { + description("Failed to set tty mode to VT_PROCESS"), + display("Failed to take control of tty ({:?})", num), + } + } +} diff --git a/src/backend/session/logind.rs b/src/backend/session/logind.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/backend/session/mod.rs b/src/backend/session/mod.rs new file mode 100644 index 0000000..235fc66 --- /dev/null +++ b/src/backend/session/mod.rs @@ -0,0 +1,44 @@ +use std::path::Path; +use std::os::unix::io::RawFd; +use wayland_server::StateProxy; + +pub trait Session { + type Error: ::std::fmt::Debug; + + fn open(&mut self, path: &Path) -> Result; + fn close(&mut self, fd: RawFd) -> Result<(), Self::Error>; + + fn change_vt(&mut self, vt: i32) -> Result<(), Self::Error>; + + fn is_active(&self) -> bool; + fn seat(&self) -> &str; +} + +pub trait SessionNotifier { + fn register(&mut self, signal: S) -> usize; + fn unregister(&mut self, signal: usize); + + fn is_active(&self) -> bool; + fn seat(&self) -> &str; +} + +pub trait SessionObserver { + fn pause<'a>(&mut self, state: &mut StateProxy<'a>); + fn activate<'a>(&mut self, state: &mut StateProxy<'a>); +} + +impl Session for () { + type Error = (); + + fn open(&mut self, _path: &Path) -> Result { Err(()) } + fn close(&mut self, _fd: RawFd) -> Result<(), Self::Error> { Err(()) } + + fn change_vt(&mut self, _vt: i32) -> Result<(), Self::Error> { Err(()) } + + fn is_active(&self) -> bool { false } + fn seat(&self) -> &str { "seat0" } +} + +pub mod direct; +#[cfg(feature = "backend_session_logind")] +pub mod logind; diff --git a/src/backend/udev.rs b/src/backend/udev.rs new file mode 100644 index 0000000..2ddef60 --- /dev/null +++ b/src/backend/udev.rs @@ -0,0 +1,348 @@ +use libudev::{Context, MonitorBuilder, MonitorSocket, Event, EventType, Enumerator, Result as UdevResult}; +use nix::sys::stat::{dev_t, fstat}; +use std::borrow::Borrow; +use std::collections::HashMap; +use std::io::{Error as IoError, Result as IoResult}; +use std::ffi::OsString; +use std::mem::drop; +use std::path::{PathBuf, Path}; +use std::os::unix::io::AsRawFd; +use wayland_server::{EventLoopHandle, StateToken, StateProxy}; +use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest}; + +use ::backend::drm::{DrmDevice, DrmBackend, DrmHandler, drm_device_bind}; +use ::backend::session::{Session, SessionObserver}; + +pub struct UdevBackend + 'static, H: DrmHandler + 'static, S: Session + 'static, T: UdevHandler + 'static> { + devices: HashMap>, FdEventSource<(StateToken>, H)>)>, + monitor: MonitorSocket, + session: StateToken, + handler: T, + logger: ::slog::Logger, +} + +impl + Borrow + 'static, H: DrmHandler + 'static, S: Session + 'static, T: UdevHandler + 'static> UdevBackend { + pub fn new<'a, L>(mut evlh: &mut EventLoopHandle, + context: &Context, + session_token: &StateToken, + mut handler: T, + logger: L) + -> Result> + where + L: Into> + { + let logger = ::slog_or_stdlog(logger).new(o!("smithay_module" => "backend_udev")); + let seat = String::from(evlh.state().get(&session_token).seat()); + let devices = all_gpus(context, seat) + .chain_err(|| ErrorKind::FailedToScan)? + .into_iter() + // Create devices + .flat_map(|path| { + match unsafe { DrmDevice::new_from_fd( + { + let session = evlh.state().get_mut(session_token); + match session.open(&path) { + Ok(fd) => 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) => match handler.device_added(&mut evlh.state().as_proxy(), &mut device) { + // fstat them + Some(drm_handler) => match fstat(device.as_raw_fd()) { + Ok(stat) => { + let token = evlh.state().insert(device); + if let Ok(event_source) = drm_device_bind(&mut evlh, token.clone(), drm_handler) { + Some((stat.st_rdev, (token, event_source))) + } else { + handler.device_removed(evlh.state(), &token); + let device = evlh.state().remove(token); + let fd = device.as_raw_fd(); + drop(device); + let session = evlh.state().get_mut(session_token); + if let Err(err) = session.close(fd) { + warn!(logger, "Failed to close dropped device. Error: {:?}. Ignoring", err); + }; + None + } + }, + Err(err) => { + // almost impossible to hit, but lets do it as good as possible + error!(logger, "Failed to get devnum of newly initialized device, dropping. Error: {:?}", err); + let token = evlh.state().insert(device); + handler.device_removed(evlh.state(), &token); + let device = evlh.state().remove(token); + let fd = device.as_raw_fd(); + drop(device); + let session = evlh.state().get_mut(session_token); + if let Err(err) = session.close(fd) { + warn!(logger, "Failed to close dropped device. Error: {:?}. Ignoring", err); + }; + None + } + }, + None => { + let fd = device.as_raw_fd(); + drop(device); //drops master + let session = evlh.state().get_mut(session_token); + if let Err(err) = session.close(fd) { + warn!(logger, "Failed to close device. Error: {:?}. Ignoring", err); + } + None + } + }, + Err(err) => { + warn!(logger, "Failed to initialize device {:?}. Error: {:?}. Skipping", path, err); + return None; + } + } + }) + .collect::>, FdEventSource<(StateToken>, H)>)>>(); + + 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)?; + + Ok(UdevBackend { + devices, + monitor, + session: session_token.clone(), + handler, + logger, + }) + } + + pub fn close<'a, ST: Into>>(mut self, state: ST) { + let mut state = state.into(); + for (_, (mut device, event_source)) in self.devices.drain() { + event_source.remove(); + self.handler.device_removed(&mut state, &device); + let device = state.remove(device); + let fd = device.as_raw_fd(); + drop(device); + let session = state.get_mut(&self.session); + if let Err(err) = session.close(fd) { + warn!(self.logger, "Failed to close device. Error: {:?}. Ignoring", err); + }; + } + } +} + +impl + 'static, H: DrmHandler + 'static, S: Session + 'static, T: UdevHandler + 'static> SessionObserver for StateToken> { + fn pause<'a>(&mut self, state: &mut StateProxy<'a>) { + state.with_value(self, |state, udev| { + for &mut (ref mut device, _) in udev.devices.values_mut() { + device.pause(state); + } + }); + } + + fn activate<'a>(&mut self, state: &mut StateProxy<'a>) { + state.with_value(self, |state, udev| { + for &mut (ref mut device, _) in udev.devices.values_mut() { + device.activate(state); + } + }); + } +} + +pub fn udev_backend_bind(evlh: &mut EventLoopHandle, udev: StateToken>) + -> IoResult>>> +where + B: From + Borrow + 'static, + H: DrmHandler + 'static, + T: UdevHandler + 'static, + S: Session + 'static, +{ + let fd = evlh.state().get(&udev).monitor.as_raw_fd(); + evlh.add_fd_event_source( + fd, + fd_event_source_implementation(), + udev, + FdInterest::READ, + ) +} + +fn fd_event_source_implementation() + -> FdEventSourceImpl>> +where + B: From + Borrow + 'static, + H: DrmHandler + 'static, + T: UdevHandler + 'static, + S: Session + 'static, +{ + FdEventSourceImpl { + ready: |mut evlh, token, _, _| { + let events = evlh.state().get(token).monitor.clone().collect::>(); + for event in events { + match event.event_type() { + // New device + EventType::Add => { + info!(evlh.state().get(token).logger, "Device Added"); + if let (Some(path), Some(devnum)) = (event.devnode(), event.devnum()) { + let mut device = { + match unsafe { DrmDevice::new_from_fd( + { + let session_token = evlh.state().get(token).session.clone(); + let logger = evlh.state().get(token).logger.clone(); + let session = evlh.state().get_mut(&session_token); + match session.open(path) { + Ok(fd) => fd, + Err(err) => { + warn!(logger, "Unable to open drm device {:?}, Error: {:?}. Skipping", path, err); + continue; + } + } + }, evlh.state().get(token).logger.clone() + ) } { + Ok(dev) => dev, + Err(err) => { + warn!(evlh.state().get(token).logger, "Failed to initialize device {:?}. Error: {}. Skipping", path, err); + continue; + } + } + }; + match evlh.state().with_value(token, |state, udev| udev.handler.device_added(state, &mut device)) { + Some(drm_handler) => { + let dev_token = evlh.state().insert(device); + if let Ok(fd_event_source) = drm_device_bind(&mut evlh, dev_token.clone(), drm_handler) { + evlh.state().get_mut(token).devices.insert(devnum, (dev_token, fd_event_source)); + } else { + evlh.state().with_value(token, |state, udev| { + let session_token = udev.session.clone(); + state.with_value(&session_token, |state, session| { + let mut state: StateProxy = state.into(); + udev.handler.device_removed(&mut state, &dev_token); + let device = state.remove(dev_token); + let fd = device.as_raw_fd(); + drop(device); + if let Err(err) = session.close(fd) { + warn!(udev.logger, "Failed to close dropped device. Error: {:?}. Ignoring", err); + }; + }) + }) + } + }, + None => { + let fd = device.as_raw_fd(); + drop(device); + evlh.state().with_value(token, |state, udev| { + state.with_value(&udev.session, |_, session| { + if let Err(err) = session.close(fd) { + warn!(udev.logger, "Failed to close unused device. Error: {:?}", err); + } + }) + }) + }, + }; + } + }, + // Device removed + EventType::Remove => { + evlh.state().with_value(token, |state, udev| { + let session_token = udev.session.clone(); + state.with_value(&session_token, |state, session| { + info!(udev.logger, "Device Remove"); + if let Some(devnum) = event.devnum() { + if let Some((device, fd_event_source)) = udev.devices.remove(&devnum) { + fd_event_source.remove(); + let mut state: StateProxy = state.into(); + udev.handler.device_removed(&mut state, &device); + let device = state.remove(device); + let fd = device.as_raw_fd(); + drop(device); + if let Err(err) = session.close(fd) { + warn!(udev.logger, "Failed to close device {:?}. Error: {:?}. Ignoring", event.sysname(), err); + }; + } + } + }) + }) + }, + // New connector + EventType::Change => evlh.state().with_value(token, |state, udev| { + info!(udev.logger, "Device Changed"); + if let Some(devnum) = event.devnum() { + info!(udev.logger, "Devnum: {:b}", devnum); + if let Some(&(ref device, _)) = udev.devices.get(&devnum) { + info!(udev.logger, "changed successful"); + udev.handler.device_changed(state, device); + } else { + info!(udev.logger, "changed, but device not tracked by backend"); + } + } else { + info!(udev.logger, "changed, but no devnum"); + } + }), + _ => {}, + } + } + }, + error: |evlh, token, _, err| { + evlh.state().with_value(token, |state, udev| udev.handler.error(state, err)) + }, + } +} + +pub trait UdevHandler + 'static, H: DrmHandler + 'static> { + fn device_added<'a, S: Into>>(&mut self, state: S, device: &mut DrmDevice) -> Option; + fn device_changed<'a, S: Into>>(&mut self, state: S, device: &StateToken>); + fn device_removed<'a, S: Into>>(&mut self, state: S, device: &StateToken>); + fn error<'a, S: Into>>(&mut self, state: S, error: IoError); +} + +pub fn primary_gpu>(context: &Context, seat: S) -> UdevResult> { + let mut enumerator = Enumerator::new(context)?; + enumerator.match_subsystem("drm")?; + enumerator.match_sysname("card[0-9]*")?; + + let mut result = None; + for device in enumerator.scan_devices()? { + if device.property_value("ID_SEAT").map(|x| x.to_os_string()).unwrap_or(OsString::from("seat0")) == *seat.as_ref() { + if let Some(pci) = device.parent_with_subsystem(Path::new("pci"))? { + if let Some(id) = pci.attribute_value("boot_vga") { + if id == "1" { + result = Some(device); + } + } + } else if result.is_none() { + result = Some(device); + } + } + } + Ok(result.and_then(|device| device.devnode().map(PathBuf::from))) +} + +pub fn all_gpus>(context: &Context, seat: S) -> UdevResult> { + let mut enumerator = Enumerator::new(context)?; + enumerator.match_subsystem("drm")?; + enumerator.match_sysname("card[0-9]*")?; + Ok(enumerator.scan_devices()? + .filter(|device| device.property_value("ID_SEAT").map(|x| x.to_os_string()).unwrap_or(OsString::from("seat0")) == *seat.as_ref()) + .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"), + } + } +} diff --git a/src/backend/winit.rs b/src/backend/winit.rs index f81b1d4..50c8e8b 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -1,6 +1,5 @@ //! Implementation of backend traits for types provided by `winit` -use backend::{SeatInternal, TouchSlotInternal}; use backend::graphics::GraphicsBackend; use backend::graphics::egl::{self, EGLContext, EGLGraphicsBackend, GlAttributes, PixelFormat, PixelFormatRequirements, SwapBuffersError}; @@ -229,7 +228,7 @@ impl EGLGraphicsBackend for WinitGraphicsBackend { } fn is_current(&self) -> bool { - self.window.rent(|egl| egl.head().is_current()) + self.window.rent(|egl| egl.rent_all(|egl| egl.context.is_current() && egl.surface.is_current())) } unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> { diff --git a/src/lib.rs b/src/lib.rs index 28c4cbe..95042e3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ #![recursion_limit = "1024"] extern crate image; +#[macro_use] extern crate nix; #[macro_use] extern crate rental; @@ -22,6 +23,8 @@ extern crate drm; extern crate gbm; #[cfg(feature = "backend_libinput")] extern crate input; +#[cfg(feature = "backend_udev")] +extern crate libudev; #[cfg(feature = "backend_winit")] extern crate wayland_client; #[cfg(feature = "backend_winit")] From 96bb3570bad015446e4978535496bdd27b1933a1 Mon Sep 17 00:00:00 2001 From: Drakulix Date: Tue, 7 Nov 2017 01:18:52 +0100 Subject: [PATCH 02/29] Get input working --- Cargo.toml | 2 +- examples/resources/cursor.rgba | Bin 0 -> 2816 bytes examples/resources/cursor2.rgba | Bin 0 -> 16384 bytes examples/udev.rs | 227 ++++++++++++++++++++++++++++++-- src/backend/drm/backend.rs | 4 +- src/backend/graphics/mod.rs | 2 +- src/backend/libinput.rs | 66 +++++++++- src/backend/session/direct.rs | 37 ++++-- src/backend/session/mod.rs | 46 ++++++- src/backend/udev.rs | 81 +++++------- src/backend/winit.rs | 4 +- 11 files changed, 393 insertions(+), 76 deletions(-) create mode 100644 examples/resources/cursor.rgba create mode 100644 examples/resources/cursor2.rgba diff --git a/Cargo.toml b/Cargo.toml index 3efcc8e..06ba3f0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ winit = { version = "0.8.2", optional = true } drm = { version = "^0.3.0", optional = true } gbm = { version = "^0.3.0", optional = true } glium = { version = "0.17.1", optional = true, default-features = false } -input = { version = "0.3.0", optional = true } +input = { version = "0.4.0", git = "https://github.com/Smithay/input.rs.git", branch = "feature/udev", optional = true } libudev = { git = "https://github.com/Drakulix/libudev-rs.git", branch = "feature/raw_ffi_access", optional = true } rental = "0.4.11" wayland-protocols = { version = "0.12.0", features = ["unstable_protocols", "server"] } diff --git a/examples/resources/cursor.rgba b/examples/resources/cursor.rgba new file mode 100644 index 0000000000000000000000000000000000000000..1bd35092c060a6ac4ff37db714bf26e264ace56e GIT binary patch literal 2816 zcmb`JOGso#6o&1-{qog1Dr%x8!Y~sVa1n)I5EQ`?fgqQHmX?;@7hCiBeBX*laAs!41GF8w54JWt zJL@_w;6{~{l-!kVL7a*k2m}ze1o58l!PfHg^Y6k|T<`+T&CP;%$(Fgf zxhAk&r%fiA! zJABsE)YO}@4Q^dso#K+6n3zcR=^48fK4)@r@=e$wxnw6LCCTLEB+)0x#ZLHaWo2dI zvM=S5jdc^R*Gsl6E-rS%7X^jGzLHCJLPCN}OiZXbLEJyX7nhx#eI5H+F4+!;L&nC& zRNP?qz-Q0R&Ao~p7ME<3$t0trql&$}y!zu`e~0 z7wtw|bXK`IG&H2xD=RC%!50-DAAd3KTXD%B92`_@xxT*s2gZ>E-}$)1;$n@Ysi{dC z8yl4x8XBa%yrHbEm$(UiIfsZEdZ=*3YA(qoaS(?^7rh;=EFcP#omIShvQ}M6|;st z<3?`(ot~cl1=?gl>aMq1troM{{1MK_0YXr3#GICvCRjg?Zv^e5L!9|qPfyPi)IA30 z1C)IC`EkW`b#;{ogTejr@o~j1EGQJj!B?p@#8{r9j}JEbuI5b Dtabf1 literal 0 HcmV?d00001 diff --git a/examples/resources/cursor2.rgba b/examples/resources/cursor2.rgba new file mode 100644 index 0000000000000000000000000000000000000000..729c1cc466b85bb2b1e9923d4a0bb7db559df4be GIT binary patch literal 16384 zcmeI1O-NKx6vxx~)X~vYbjq=Ws3dTaLKuP~m=FrJuvI9bL!v{}AX>ICKS>b^7eP@g zaT_j75OiUpE)+i&I*Qt0f{G}SV4*Xkr~kS51{afAtKOS?;mn;m^9KC=-nk#|y0NiQ zw6wHz{|s7TVPP2eq14=N_X8sGPfkv{z->Z=l!K|MDaYaAp%>vFA0K}N{>XxYf{<~5 z`!4vsUN6dk{P6J<4s_Ys*&*S8`N8e2udgTEe|A9A)6;cu5T(=UH19y+uc)Y?q@*O8 znVDe)e)P}az*JmZ9FAVIP2qPsodig-SS&O#F~JUKc6PQIIfKn+)2su9zpAQA@Uw&X z_;_}3c;uiFIcs8Kq9z>#<7Wp62?;bdHpU7Lutv5bXDliz(wu|e@v{TWe>5^O!VYL| zZmu13GZPyFn_Y&O&2;Gn1jIOssml$n{S z2?wX)X9rARH_#EWAc87NL3vr{!k{R;!iz`}>6hJTrA7XT?5IhhDYqZ}gwvKwzgJ8 zO-&6oH#gJn?yjhVm6eqb;J2owrRmYDss-ccxz1!V1@?n_ygsC2$uE_al>rCJbJ70({!cJoN7xWN?m6NSF$gniM^$s-aemxa!M?q{ zy}7fq^KEZ$kD{Za0}e_{O9|&hG(SJj<2?$&tQ>q!pakBkrz!mW+>h~0o12?ov$C=r zot>R6ETg@>J;2Y;17Ihg&-cBlsVNtfM)i;e|4W&l&;OWpZEfuf*0MXe@-iZIYirBB zzP?T=DJcO5d3kw!DSLR2b)V~A)n`OU_n%f*SKS#I8TY_*5n)C|mzS5{W!Bc#R>9Bf z0`fcua{7nE;kb#S7%3o?{NUifh#Tf0L8eE&eVNXJI#Z-Q$Lu z_ux#}h5ca;zDq?Y>mc50uNjR-u7S9|zP=aT-QBP7J{xffYa#Dqc>6-dj literal 0 HcmV?d00001 diff --git a/examples/udev.rs b/examples/udev.rs index 8d48c95..60c9e4e 100644 --- a/examples/udev.rs +++ b/examples/udev.rs @@ -2,6 +2,8 @@ extern crate drm; #[macro_use] extern crate glium; extern crate rand; +extern crate input as libinput; +extern crate image; extern crate libudev; #[macro_use(define_roles)] extern crate smithay; @@ -22,15 +24,24 @@ use drm::control::encoder::Info as EncoderInfo; use drm::control::crtc; use drm::result::Error as DrmError; use glium::Surface; +use image::{ImageBuffer, Rgba}; +use libinput::{Libinput, Device as LibinputDevice, event}; +use libinput::event::keyboard::KeyboardEventTrait; use helpers::{init_shell, GliumDrawer, MyWindowMap, Roles, SurfaceData}; use slog::{Drain, Logger}; use smithay::backend::drm::{DrmBackend, DrmDevice, DrmHandler}; +use smithay::backend::graphics::GraphicsBackend; use smithay::backend::graphics::egl::EGLGraphicsBackend; +use smithay::backend::input::{self, Event, InputBackend, InputHandler, KeyboardKeyEvent, KeyState, PointerButtonEvent, + PointerAxisEvent}; +use smithay::backend::libinput::{LibinputInputBackend, libinput_bind, PointerAxisEvent as LibinputPointerAxisEvent, LibinputSessionInterface}; use smithay::backend::udev::{UdevBackend, UdevHandler, udev_backend_bind}; -use smithay::backend::session::SessionNotifier; +use smithay::backend::session::{Session, SessionNotifier}; use smithay::backend::session::direct::{direct_session_bind, DirectSession}; use smithay::wayland::compositor::{CompositorToken, SubsurfaceRole, TraversalAction}; use smithay::wayland::compositor::roles::Role; +use smithay::wayland::output::{Mode, Output, PhysicalProperties}; +use smithay::wayland::seat::{KeyboardHandle, PointerHandle, Seat}; use smithay::wayland::shell::ShellState; use smithay::wayland::shm::init_shm_global; use std::cell::RefCell; @@ -38,9 +49,121 @@ use std::collections::HashSet; use std::io::Error as IoError; use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use std::time::Duration; use wayland_server::{StateToken, StateProxy}; +use wayland_server::protocol::{wl_output, wl_pointer}; + +struct LibinputInputHandler { + log: Logger, + pointer: PointerHandle, + keyboard: KeyboardHandle, + window_map: Rc>, + pointer_location: Arc>, + screen_size: (u32, u32), + serial: u32, + running: Arc, +} + +impl LibinputInputHandler { + fn next_serial(&mut self) -> u32 { + self.serial += 1; + self.serial + } +} + +impl InputHandler for LibinputInputHandler { + fn on_seat_created(&mut self, _: &input::Seat) { + /* we just create a single static one */ + } + fn on_seat_destroyed(&mut self, _: &input::Seat) { + /* we just create a single static one */ + } + fn on_seat_changed(&mut self, _: &input::Seat) { + /* we just create a single static one */ + } + fn on_keyboard_key(&mut self, _: &input::Seat, evt: event::keyboard::KeyboardKeyEvent) { + let keycode = evt.key(); + let state = evt.state(); + debug!(self.log, "key"; "keycode" => keycode, "state" => format!("{:?}", state)); + + match (keycode, state) { + (1 /*ESC*/, KeyState::Pressed) => { + self.running.store(false, Ordering::SeqCst); + }, + (keycode, state) => { + let serial = self.next_serial(); + self.keyboard.input(keycode, state, serial, |_, _| true); + } + } + } + fn on_pointer_move(&mut self, _: &input::Seat, evt: event::pointer::PointerMotionEvent) { + let (x, y) = (evt.dx(), evt.dy()); + let serial = self.next_serial(); + let mut location = self.pointer_location.lock().unwrap(); + location.0 += x; + location.1 += y; + let under = self.window_map.borrow().get_surface_under((location.0, location.1)); + self.pointer.motion( + under.as_ref().map(|&(ref s, (x, y))| (s, x, y)), + serial, + evt.time(), + ); + } + fn on_pointer_move_absolute(&mut self, _: &input::Seat, evt: event::pointer::PointerMotionAbsoluteEvent) { + let (x, y) = (evt.absolute_x_transformed(self.screen_size.0), evt.absolute_y_transformed(self.screen_size.1)); + *self.pointer_location.lock().unwrap() = (x, y); + let serial = self.next_serial(); + let under = self.window_map.borrow().get_surface_under((x, y)); + self.pointer.motion( + under.as_ref().map(|&(ref s, (x, y))| (s, x, y)), + serial, + evt.time(), + ); + } + fn on_pointer_button(&mut self, _: &input::Seat, evt: event::pointer::PointerButtonEvent) { + let serial = self.next_serial(); + let button = evt.button(); + let state = match evt.state() { + input::MouseButtonState::Pressed => { + // change the keyboard focus + let under = self.window_map + .borrow_mut() + .get_surface_and_bring_to_top(*self.pointer_location.lock().unwrap()); + self.keyboard + .set_focus(under.as_ref().map(|&(ref s, _)| s), serial); + wl_pointer::ButtonState::Pressed + } + input::MouseButtonState::Released => wl_pointer::ButtonState::Released, + }; + self.pointer.button(button, state, serial, evt.time()); + } + fn on_pointer_axis(&mut self, _: &input::Seat, evt: LibinputPointerAxisEvent) { + let axis = match evt.axis() { + input::Axis::Vertical => wayland_server::protocol::wl_pointer::Axis::VerticalScroll, + input::Axis::Horizontal => wayland_server::protocol::wl_pointer::Axis::HorizontalScroll, + }; + self.pointer.axis(axis, evt.amount(), evt.time()); + } + fn on_touch_down(&mut self, _: &input::Seat, _: event::touch::TouchDownEvent) { + /* not done in this example */ + } + fn on_touch_motion(&mut self, _: &input::Seat, _: event::touch::TouchMotionEvent) { + /* not done in this example */ + } + fn on_touch_up(&mut self, _: &input::Seat, _: event::touch::TouchUpEvent) { + /* not done in this example */ + } + fn on_touch_cancel(&mut self, _: &input::Seat, _: event::touch::TouchCancelEvent) { + /* not done in this example */ + } + fn on_touch_frame(&mut self, _: &input::Seat, _: event::touch::TouchFrameEvent) { + /* not done in this example */ + } + fn on_input_config_changed(&mut self, _: &mut [LibinputDevice]) { + /* not done in this example */ + } +} fn main() { // A logger facility, here we use the terminal for this example @@ -63,7 +186,7 @@ fn main() { * Initialize session on the current tty */ let (session, mut notifier) = DirectSession::new(None, log.clone()).unwrap(); - let session_token = event_loop.state().insert(session); + let session = Arc::new(Mutex::new(session)); let running = Arc::new(AtomicBool::new(true)); let r = running.clone(); @@ -71,48 +194,124 @@ fn main() { r.store(false, Ordering::SeqCst); }).expect("Error setting Ctrl-C handler"); + let pointer_location = Arc::new(Mutex::new((0.0, 0.0))); + /* * Initialize the udev backend */ let context = libudev::Context::new().unwrap(); + let bytes = include_bytes!("resources/cursor2.rgba"); let udev - = UdevBackend::new(&mut event_loop, &context, &session_token, UdevHandlerImpl { + = UdevBackend::new(&mut event_loop, &context, session.clone(), UdevHandlerImpl { shell_state_token, compositor_token, window_map: window_map.clone(), + pointer_location: pointer_location.clone(), + pointer_image: ImageBuffer::from_raw(64, 64, bytes.to_vec()).unwrap(), logger: log.clone(), }, log.clone()).unwrap(); let udev_token = event_loop.state().insert(udev); let udev_session_id = notifier.register(udev_token.clone()); + + let (seat_token, _) = Seat::new(&mut event_loop, session.seat().into(), log.clone()); + + let pointer = event_loop.state().get_mut(&seat_token).add_pointer(); + let keyboard = event_loop + .state() + .get_mut(&seat_token) + .add_keyboard("", "", "", None, 1000, 500) + .expect("Failed to initialize the keyboard"); + + let (output_token, _output_global) = Output::new( + &mut event_loop, + "Drm".into(), + PhysicalProperties { + width: 0, + height: 0, + subpixel: wl_output::Subpixel::Unknown, + maker: "Smithay".into(), + model: "Generic DRM".into(), + }, + log.clone(), + ); + + let (w, h) = (1920, 1080); // Hardcode full-hd res + event_loop + .state() + .get_mut(&output_token) + .change_current_state( + Some(Mode { + width: w as i32, + height: h as i32, + refresh: 60_000, + }), + None, + None, + ); + event_loop + .state() + .get_mut(&output_token) + .set_preferred(Mode { + width: w as i32, + height: h as i32, + refresh: 60_000, + }); + + /* + * Initialize libinput backend + */ + let seat = session.seat(); + let mut libinput_context = Libinput::new_from_udev::>>>(session.into(), &context); + let libinput_session_id = notifier.register(libinput_context.clone()); + libinput_context.udev_assign_seat(&seat); + let mut libinput_backend = LibinputInputBackend::new(libinput_context, log.clone()); + libinput_backend.set_handler(LibinputInputHandler { + log: log.clone(), + pointer, + keyboard, + window_map: window_map.clone(), + pointer_location, + screen_size: (w, h), + serial: 0, + running: running.clone(), + }); + let libinput_event_source = libinput_bind(libinput_backend, &mut event_loop).unwrap(); + let session_event_source = direct_session_bind(notifier, &mut event_loop, log.clone()).unwrap(); let udev_event_source = udev_backend_bind(&mut event_loop, udev_token).unwrap(); + /* - * Add a listening socket: + * Add a listening socket */ let name = display.add_socket_auto().unwrap().into_string().unwrap(); println!("Listening on socket: {}", name); while running.load(Ordering::SeqCst) { - event_loop.dispatch(Some(16)).unwrap(); + event_loop.dispatch(Some(16)); display.flush_clients(); window_map.borrow_mut().refresh(); } + println!("Bye Bye"); + let mut notifier = session_event_source.remove(); notifier.unregister(udev_session_id); + notifier.unregister(libinput_session_id); + + libinput_event_source.remove(); let udev_token = udev_event_source.remove(); let udev = event_loop.state().remove(udev_token); udev.close(event_loop.state()); - - event_loop.state().remove(session_token); } struct UdevHandlerImpl { shell_state_token: StateToken>, compositor_token: CompositorToken, window_map: Rc>, + pointer_location: Arc>, + pointer_image: ImageBuffer, Vec>, logger: ::slog::Logger, } @@ -146,6 +345,12 @@ impl UdevHandlerImpl { // create a backend let renderer_token = device.create_backend(&mut state, crtc, mode, vec![connector_info.handle()]).unwrap(); + // create cursor + { + let renderer = state.get_mut(renderer_token); + renderer.set_cursor_representation(&self.pointer_image, (2, 2)).unwrap(); + } + // render first frame { let renderer = state.get_mut(renderer_token); @@ -172,6 +377,7 @@ impl UdevHandler, DrmHandlerImpl> for UdevHandlerImpl { shell_state_token: self.shell_state_token.clone(), compositor_token: self.compositor_token.clone(), window_map: self.window_map.clone(), + pointer_location: self.pointer_location.clone(), logger: self.logger.clone(), }) } @@ -202,6 +408,7 @@ pub struct DrmHandlerImpl { shell_state_token: StateToken>, compositor_token: CompositorToken, window_map: Rc>, + pointer_location: Arc>, logger: ::slog::Logger, } @@ -210,6 +417,10 @@ impl DrmHandler> for DrmHandlerImpl { backend: &StateToken>, _crtc: crtc::Handle, _frame: u32, _duration: Duration) { let state = state.into(); let drawer = state.get(backend); + { + let (x, y) = *self.pointer_location.lock().unwrap(); + let _ = (**drawer).set_cursor_position(x.trunc().abs() as u32, y.trunc().abs() as u32); + } let mut frame = drawer.draw(); frame.clear_color(0.8, 0.8, 0.9, 1.0); // redraw the frame, in a simple but inneficient way diff --git a/src/backend/drm/backend.rs b/src/backend/drm/backend.rs index d2197bc..e784321 100644 --- a/src/backend/drm/backend.rs +++ b/src/backend/drm/backend.rs @@ -424,7 +424,7 @@ impl GraphicsBackend for DrmBackend { }) } - fn set_cursor_representation(&self, buffer: ImageBuffer, Vec>, hotspot: (u32, u32)) + fn set_cursor_representation(&self, buffer: &ImageBuffer, Vec>, hotspot: (u32, u32)) -> Result<()> { let (w, h) = buffer.dimensions(); @@ -445,7 +445,7 @@ impl GraphicsBackend for DrmBackend { ) .chain_err(|| ErrorKind::GbmInitFailed)?; cursor - .write(&*buffer.into_raw()) + .write(&**buffer) .chain_err(|| ErrorKind::GbmInitFailed)?; trace!(self.logger, "Set the new imported cursor"); diff --git a/src/backend/graphics/mod.rs b/src/backend/graphics/mod.rs index 0a082d7..2f0917e 100644 --- a/src/backend/graphics/mod.rs +++ b/src/backend/graphics/mod.rs @@ -29,7 +29,7 @@ pub trait 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)) + fn set_cursor_representation(&self, cursor: &Self::CursorFormat, hotspot: (u32, u32)) -> Result<(), Self::Error>; } diff --git a/src/backend/libinput.rs b/src/backend/libinput.rs index 4f7cd06..ca7051b 100644 --- a/src/backend/libinput.rs +++ b/src/backend/libinput.rs @@ -1,12 +1,18 @@ //! Implementation of input backend trait for types provided by `libinput` +#[cfg(feature = "backend_session")] +use backend::session::{AsErrno, Session, SessionObserver}; use backend::input as backend; use input as libinput; use input::event; use std::collections::hash_map::{DefaultHasher, Entry, HashMap}; use std::hash::{Hash, Hasher}; -use std::io::Error as IoError; +use std::io::{Error as IoError, Result as IoResult}; use std::rc::Rc; +use std::path::Path; +use std::os::unix::io::RawFd; +use wayland_server::{EventLoopHandle, StateProxy}; +use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest}; /// Libinput based `InputBackend`. /// @@ -260,7 +266,7 @@ impl backend::InputBackend for LibinputInputBackend { if self.handler.is_some() { self.clear_handler(); } - info!(self.logger, "New input handler set."); + info!(self.logger, "New input handler set"); for seat in self.seats.values() { trace!(self.logger, "Calling on_seat_created with {:?}", seat); handler.on_seat_created(seat); @@ -548,3 +554,59 @@ impl From for backend::MouseButtonState { } } } + +impl SessionObserver for libinput::Libinput { + fn pause<'a>(&mut self, _state: &mut StateProxy<'a>) { + self.suspend() + } + + fn activate<'a>(&mut self, _state: &mut StateProxy<'a>) { + // TODO Is this the best way to handle this failure? + self.resume().expect("Unable to resume libinput context"); + } +} + +pub struct LibinputSessionInterface(S); + +impl From for LibinputSessionInterface { + fn from(session: S) -> LibinputSessionInterface { + LibinputSessionInterface(session) + } +} + +impl libinput::LibinputInterface for LibinputSessionInterface { + fn open_restricted(&mut self, path: &Path, flags: i32) -> Result { + use nix::fcntl::OFlag; + self.0.open(path, OFlag::from_bits_truncate(flags)).map_err(|err| err.as_errno().unwrap_or(1 /*Use EPERM by default*/)) + } + + fn close_restricted(&mut self, fd: RawFd) { + let _ = self.0.close(fd); + } +} + +pub fn libinput_bind(backend: LibinputInputBackend, evlh: &mut EventLoopHandle) + -> IoResult> +{ + let fd = unsafe { backend.context.fd() }; + evlh.add_fd_event_source( + fd, + fd_event_source_implementation(), + backend, + FdInterest::READ, + ) +} + +fn fd_event_source_implementation() -> FdEventSourceImpl { + FdEventSourceImpl { + ready: |_evlh, ref mut backend, _, _| { + use ::backend::input::InputBackend; + if let Err(error) = backend.dispatch_new_events() { + warn!(backend.logger, "Libinput errored: {}", error); + } + }, + error: |_evlh, ref backend, _, error| { + warn!(backend.logger, "Libinput fd errored: {}", error); + } + } +} diff --git a/src/backend/session/direct.rs b/src/backend/session/direct.rs index a74def2..741096d 100644 --- a/src/backend/session/direct.rs +++ b/src/backend/session/direct.rs @@ -4,7 +4,7 @@ use std::os::unix::io::RawFd; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; use nix::{Error as NixError, Result as NixResult}; -use nix::fcntl::{self, open}; +use nix::fcntl::{self, open, OFlag}; use nix::libc::c_int; use nix::sys::signal::{self, Signal}; use nix::sys::stat::{dev_t, major, minor, Mode, fstat}; @@ -15,7 +15,7 @@ use wayland_server::sources::SignalEventSource; #[cfg(feature = "backend_session_udev")] use libudev::Context; -use super::{Session, SessionNotifier, SessionObserver}; +use super::{AsErrno, Session, SessionNotifier, SessionObserver}; mod tty { ioctl!(bad read kd_get_mode with 0x4B3B; i16); @@ -34,6 +34,7 @@ mod tty { ioctl!(bad write_int vt_activate with 0x5606); ioctl!(bad write_int vt_wait_active with 0x5607); ioctl!(bad write_ptr vt_set_mode with 0x5602; VtMode); + ioctl!(bad write_int vt_rel_disp with 0x5605); #[repr(C)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub struct VtMode { @@ -50,7 +51,7 @@ mod tty { } pub const VT_AUTO: i8 = 0x00; pub const VT_PROCESS: i8 = 0x01; - pub const VT_ACKACQ: i8 = 0x02; + pub const VT_ACKACQ: i32 = 0x02; extern { pub fn __libc_current_sigrtmin() -> i8; @@ -146,6 +147,7 @@ pub struct DirectSession { } pub struct DirectSessionNotifier { + tty: RawFd, active: Arc, signals: Vec>>, signal: Signal, @@ -175,6 +177,7 @@ impl DirectSession { old_keyboard_mode, logger: logger.new(o!("vt" => format!("{}", vt), "component" => "session")), }, DirectSessionNotifier { + tty: fd, active, signals: Vec::new(), signal, @@ -194,7 +197,7 @@ impl DirectSession { bail!(ErrorKind::NotRunningFromTTY); } - let vt_num = minor(stat.st_dev) as i32 - 1; + let vt_num = minor(stat.st_rdev) as i32; info!(logger, "Running from tty: {}", vt_num); let mut mode = 0; @@ -246,8 +249,9 @@ impl DirectSession { impl Session for DirectSession { type Error = NixError; - fn open(&mut self, path: &Path) -> NixResult { - open(path, fcntl::O_RDWR | fcntl::O_CLOEXEC | fcntl::O_NOCTTY | fcntl::O_NONBLOCK, Mode::empty()) + fn open(&mut self, path: &Path, flags: OFlag) -> NixResult { + info!(self.logger, "Opening device: {:?}", path); + open(path, flags, Mode::empty()) } fn close(&mut self, fd: RawFd) -> NixResult<()> { @@ -258,9 +262,9 @@ impl Session for DirectSession { self.active.load(Ordering::SeqCst) } - fn seat(&self) -> &str { + fn seat(&self) -> String { // The VT api can only be used on seat0 - return "seat0" + String::from("seat0") } fn change_vt(&mut self, vt_num: i32) -> NixResult<()> { @@ -268,8 +272,19 @@ impl Session for DirectSession { } } +impl AsErrno for NixError { + fn as_errno(&self) -> Option { + match *self { + NixError::Sys(errno) => Some(errno as i32), + _ => None, + } + } +} + impl Drop for DirectSession { fn drop(&mut self) { + info!(self.logger, "Deallocating tty {}", self.tty); + if let Err(err) = unsafe { tty::kd_set_kb_mode(self.tty, self.old_keyboard_mode) } { warn!(self.logger, "Unable to restore vt keyboard mode. Error: {}", err); } @@ -318,7 +333,13 @@ where if let &mut Some(ref mut signal) = signal {signal.pause(&mut evlh.state().as_proxy()); } } notifier.active.store(false, Ordering::SeqCst); + unsafe { + tty::vt_rel_disp(notifier.tty, 1).expect("Unable to release tty lock"); + } } else { + unsafe { + tty::vt_rel_disp(notifier.tty, tty::VT_ACKACQ).expect("Unable to acquire tty lock"); + } for signal in &mut notifier.signals { if let &mut Some(ref mut signal) = signal { signal.activate(&mut evlh.state().as_proxy()); } } diff --git a/src/backend/session/mod.rs b/src/backend/session/mod.rs index 235fc66..507e986 100644 --- a/src/backend/session/mod.rs +++ b/src/backend/session/mod.rs @@ -1,17 +1,19 @@ use std::path::Path; +use std::sync::{Arc, Mutex}; use std::os::unix::io::RawFd; +use nix::fcntl::OFlag; use wayland_server::StateProxy; pub trait Session { - type Error: ::std::fmt::Debug; + type Error: AsErrno; - fn open(&mut self, path: &Path) -> Result; + fn open(&mut self, path: &Path, flags: OFlag) -> Result; fn close(&mut self, fd: RawFd) -> Result<(), Self::Error>; fn change_vt(&mut self, vt: i32) -> Result<(), Self::Error>; fn is_active(&self) -> bool; - fn seat(&self) -> &str; + fn seat(&self) -> String; } pub trait SessionNotifier { @@ -30,13 +32,47 @@ pub trait SessionObserver { impl Session for () { type Error = (); - fn open(&mut self, _path: &Path) -> Result { Err(()) } + fn open(&mut self, _path: &Path, _flags: OFlag) -> Result { Err(()) } fn close(&mut self, _fd: RawFd) -> Result<(), Self::Error> { Err(()) } fn change_vt(&mut self, _vt: i32) -> Result<(), Self::Error> { Err(()) } fn is_active(&self) -> bool { false } - fn seat(&self) -> &str { "seat0" } + fn seat(&self) -> String { String::from("seat0") } +} + +impl Session for Arc> { + type Error = S::Error; + + fn open(&mut self, path: &Path, flags: OFlag) -> Result { + self.lock().unwrap().open(path, flags) + } + + fn close(&mut self, fd: RawFd) -> Result<(), Self::Error> { + self.lock().unwrap().close(fd) + } + + fn change_vt(&mut self, vt: i32) -> Result<(), Self::Error> { + self.lock().unwrap().change_vt(vt) + } + + fn is_active(&self) -> bool { + self.lock().unwrap().is_active() + } + + fn seat(&self) -> String { + self.lock().unwrap().seat() + } +} + +pub trait AsErrno: ::std::fmt::Debug { + fn as_errno(&self) -> Option; +} + +impl AsErrno for () { + fn as_errno(&self) -> Option { + None + } } pub mod direct; diff --git a/src/backend/udev.rs b/src/backend/udev.rs index 2ddef60..794cb76 100644 --- a/src/backend/udev.rs +++ b/src/backend/udev.rs @@ -1,4 +1,5 @@ use libudev::{Context, MonitorBuilder, MonitorSocket, Event, EventType, Enumerator, Result as UdevResult}; +use nix::fcntl; use nix::sys::stat::{dev_t, fstat}; use std::borrow::Borrow; use std::collections::HashMap; @@ -16,7 +17,7 @@ use ::backend::session::{Session, SessionObserver}; pub struct UdevBackend + 'static, H: DrmHandler + 'static, S: Session + 'static, T: UdevHandler + 'static> { devices: HashMap>, FdEventSource<(StateToken>, H)>)>, monitor: MonitorSocket, - session: StateToken, + session: S, handler: T, logger: ::slog::Logger, } @@ -24,7 +25,7 @@ pub struct UdevBackend + 'static, H: DrmHandler + 'stat impl + Borrow + 'static, H: DrmHandler + 'static, S: Session + 'static, T: UdevHandler + 'static> UdevBackend { pub fn new<'a, L>(mut evlh: &mut EventLoopHandle, context: &Context, - session_token: &StateToken, + mut session: S, mut handler: T, logger: L) -> Result> @@ -32,7 +33,7 @@ impl + Borrow + 'static, H: DrmHandler + 'sta L: Into> { let logger = ::slog_or_stdlog(logger).new(o!("smithay_module" => "backend_udev")); - let seat = String::from(evlh.state().get(&session_token).seat()); + let seat = session.seat(); let devices = all_gpus(context, seat) .chain_err(|| ErrorKind::FailedToScan)? .into_iter() @@ -40,8 +41,7 @@ impl + Borrow + 'static, H: DrmHandler + 'sta .flat_map(|path| { match unsafe { DrmDevice::new_from_fd( { - let session = evlh.state().get_mut(session_token); - match session.open(&path) { + match session.open(&path, fcntl::O_RDWR | fcntl::O_CLOEXEC | fcntl::O_NOCTTY | fcntl::O_NONBLOCK) { Ok(fd) => fd, Err(err) => { warn!(logger, "Unable to open drm device {:?}, Error: {:?}. Skipping", path, err); @@ -63,7 +63,6 @@ impl + Borrow + 'static, H: DrmHandler + 'sta let device = evlh.state().remove(token); let fd = device.as_raw_fd(); drop(device); - let session = evlh.state().get_mut(session_token); if let Err(err) = session.close(fd) { warn!(logger, "Failed to close dropped device. Error: {:?}. Ignoring", err); }; @@ -78,7 +77,6 @@ impl + Borrow + 'static, H: DrmHandler + 'sta let device = evlh.state().remove(token); let fd = device.as_raw_fd(); drop(device); - let session = evlh.state().get_mut(session_token); if let Err(err) = session.close(fd) { warn!(logger, "Failed to close dropped device. Error: {:?}. Ignoring", err); }; @@ -88,7 +86,6 @@ impl + Borrow + 'static, H: DrmHandler + 'sta None => { let fd = device.as_raw_fd(); drop(device); //drops master - let session = evlh.state().get_mut(session_token); if let Err(err) = session.close(fd) { warn!(logger, "Failed to close device. Error: {:?}. Ignoring", err); } @@ -110,7 +107,7 @@ impl + Borrow + 'static, H: DrmHandler + 'sta Ok(UdevBackend { devices, monitor, - session: session_token.clone(), + session, handler, logger, }) @@ -124,11 +121,11 @@ impl + Borrow + 'static, H: DrmHandler + 'sta let device = state.remove(device); let fd = device.as_raw_fd(); drop(device); - let session = state.get_mut(&self.session); - if let Err(err) = session.close(fd) { + if let Err(err) = self.session.close(fd) { warn!(self.logger, "Failed to close device. Error: {:?}. Ignoring", err); }; } + info!(self.logger, "All devices closed"); } } @@ -187,10 +184,8 @@ where let mut device = { match unsafe { DrmDevice::new_from_fd( { - let session_token = evlh.state().get(token).session.clone(); let logger = evlh.state().get(token).logger.clone(); - let session = evlh.state().get_mut(&session_token); - match session.open(path) { + match evlh.state().get_mut(token).session.open(path, fcntl::O_RDWR | fcntl::O_CLOEXEC | fcntl::O_NOCTTY | fcntl::O_NONBLOCK) { Ok(fd) => fd, Err(err) => { warn!(logger, "Unable to open drm device {:?}, Error: {:?}. Skipping", path, err); @@ -213,29 +208,24 @@ where evlh.state().get_mut(token).devices.insert(devnum, (dev_token, fd_event_source)); } else { evlh.state().with_value(token, |state, udev| { - let session_token = udev.session.clone(); - state.with_value(&session_token, |state, session| { - let mut state: StateProxy = state.into(); - udev.handler.device_removed(&mut state, &dev_token); - let device = state.remove(dev_token); - let fd = device.as_raw_fd(); - drop(device); - if let Err(err) = session.close(fd) { - warn!(udev.logger, "Failed to close dropped device. Error: {:?}. Ignoring", err); - }; - }) + let mut state: StateProxy = state.into(); + udev.handler.device_removed(&mut state, &dev_token); + let device = state.remove(dev_token); + let fd = device.as_raw_fd(); + drop(device); + if let Err(err) = udev.session.close(fd) { + warn!(udev.logger, "Failed to close dropped device. Error: {:?}. Ignoring", err); + }; }) } }, None => { let fd = device.as_raw_fd(); drop(device); - evlh.state().with_value(token, |state, udev| { - state.with_value(&udev.session, |_, session| { - if let Err(err) = session.close(fd) { - warn!(udev.logger, "Failed to close unused device. Error: {:?}", err); - } - }) + evlh.state().with_value(token, |_state, udev| { + if let Err(err) = udev.session.close(fd) { + warn!(udev.logger, "Failed to close unused device. Error: {:?}", err); + } }) }, }; @@ -244,23 +234,20 @@ where // Device removed EventType::Remove => { evlh.state().with_value(token, |state, udev| { - let session_token = udev.session.clone(); - state.with_value(&session_token, |state, session| { - info!(udev.logger, "Device Remove"); - if let Some(devnum) = event.devnum() { - if let Some((device, fd_event_source)) = udev.devices.remove(&devnum) { - fd_event_source.remove(); - let mut state: StateProxy = state.into(); - udev.handler.device_removed(&mut state, &device); - let device = state.remove(device); - let fd = device.as_raw_fd(); - drop(device); - if let Err(err) = session.close(fd) { - warn!(udev.logger, "Failed to close device {:?}. Error: {:?}. Ignoring", event.sysname(), err); - }; - } + info!(udev.logger, "Device Remove"); + if let Some(devnum) = event.devnum() { + if let Some((device, fd_event_source)) = udev.devices.remove(&devnum) { + fd_event_source.remove(); + let mut state: StateProxy = state.into(); + udev.handler.device_removed(&mut state, &device); + let device = state.remove(device); + let fd = device.as_raw_fd(); + drop(device); + if let Err(err) = udev.session.close(fd) { + warn!(udev.logger, "Failed to close device {:?}. Error: {:?}. Ignoring", event.sysname(), err); + }; } - }) + } }) }, // New connector diff --git a/src/backend/winit.rs b/src/backend/winit.rs index 50c8e8b..177897a 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -190,11 +190,11 @@ impl GraphicsBackend for WinitGraphicsBackend { self.window.head().set_cursor_position(x as i32, y as i32) } - fn set_cursor_representation(&self, cursor: Self::CursorFormat, _hotspot: (u32, u32)) + fn set_cursor_representation(&self, cursor: &Self::CursorFormat, _hotspot: (u32, u32)) -> ::std::result::Result<(), ()> { // Cannot log this one, as `CursorFormat` is not `Debug` and should not be debug!(self.logger, "Changing cursor representation"); - self.window.head().set_cursor(cursor); + self.window.head().set_cursor(*cursor); Ok(()) } } From cd27436470c7eebcc6af6630a54f89305b2a888b Mon Sep 17 00:00:00 2001 From: Drakulix Date: Sat, 25 Nov 2017 13:27:52 +0100 Subject: [PATCH 03/29] Use xkb for a nice shutdown command --- examples/udev.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/examples/udev.rs b/examples/udev.rs index 60c9e4e..6c188b2 100644 --- a/examples/udev.rs +++ b/examples/udev.rs @@ -7,6 +7,7 @@ extern crate image; extern crate libudev; #[macro_use(define_roles)] extern crate smithay; +extern crate xkbcommon; extern crate wayland_server; #[macro_use] @@ -32,7 +33,7 @@ use slog::{Drain, Logger}; use smithay::backend::drm::{DrmBackend, DrmDevice, DrmHandler}; use smithay::backend::graphics::GraphicsBackend; use smithay::backend::graphics::egl::EGLGraphicsBackend; -use smithay::backend::input::{self, Event, InputBackend, InputHandler, KeyboardKeyEvent, KeyState, PointerButtonEvent, +use smithay::backend::input::{self, Event, InputBackend, InputHandler, KeyboardKeyEvent, PointerButtonEvent, PointerAxisEvent}; use smithay::backend::libinput::{LibinputInputBackend, libinput_bind, PointerAxisEvent as LibinputPointerAxisEvent, LibinputSessionInterface}; use smithay::backend::udev::{UdevBackend, UdevHandler, udev_backend_bind}; @@ -51,6 +52,7 @@ use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use std::time::Duration; +use xkbcommon::xkb::keysyms as xkb; use wayland_server::{StateToken, StateProxy}; use wayland_server::protocol::{wl_output, wl_pointer}; @@ -87,15 +89,15 @@ impl InputHandler for LibinputInputHandler { let state = evt.state(); debug!(self.log, "key"; "keycode" => keycode, "state" => format!("{:?}", state)); - match (keycode, state) { - (1 /*ESC*/, KeyState::Pressed) => { + let serial = self.next_serial(); + self.keyboard.input(keycode, state, serial, |modifiers, keysym| { + if modifiers.ctrl && modifiers.alt && keysym == xkb::KEY_BackSpace { self.running.store(false, Ordering::SeqCst); - }, - (keycode, state) => { - let serial = self.next_serial(); - self.keyboard.input(keycode, state, serial, |_, _| true); + false + } else { + true } - } + }); } fn on_pointer_move(&mut self, _: &input::Seat, evt: event::pointer::PointerMotionEvent) { let (x, y) = (evt.dx(), evt.dy()); From 606efebe1b98eb02dd3a09a45a8aea77998c308c Mon Sep 17 00:00:00 2001 From: Drakulix Date: Sat, 25 Nov 2017 13:28:49 +0100 Subject: [PATCH 04/29] Remove unnecessary mutexes --- examples/udev.rs | 24 ++++++++++++------------ src/backend/session/mod.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/examples/udev.rs b/examples/udev.rs index 6c188b2..142b7d9 100644 --- a/examples/udev.rs +++ b/examples/udev.rs @@ -49,8 +49,8 @@ use std::cell::RefCell; use std::collections::HashSet; use std::io::Error as IoError; use std::rc::Rc; +use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::{Arc, Mutex}; use std::time::Duration; use xkbcommon::xkb::keysyms as xkb; use wayland_server::{StateToken, StateProxy}; @@ -61,7 +61,7 @@ struct LibinputInputHandler { pointer: PointerHandle, keyboard: KeyboardHandle, window_map: Rc>, - pointer_location: Arc>, + pointer_location: Rc>, screen_size: (u32, u32), serial: u32, running: Arc, @@ -102,7 +102,7 @@ impl InputHandler for LibinputInputHandler { fn on_pointer_move(&mut self, _: &input::Seat, evt: event::pointer::PointerMotionEvent) { let (x, y) = (evt.dx(), evt.dy()); let serial = self.next_serial(); - let mut location = self.pointer_location.lock().unwrap(); + let mut location = self.pointer_location.borrow_mut(); location.0 += x; location.1 += y; let under = self.window_map.borrow().get_surface_under((location.0, location.1)); @@ -114,7 +114,7 @@ impl InputHandler for LibinputInputHandler { } fn on_pointer_move_absolute(&mut self, _: &input::Seat, evt: event::pointer::PointerMotionAbsoluteEvent) { let (x, y) = (evt.absolute_x_transformed(self.screen_size.0), evt.absolute_y_transformed(self.screen_size.1)); - *self.pointer_location.lock().unwrap() = (x, y); + *self.pointer_location.borrow_mut() = (x, y); let serial = self.next_serial(); let under = self.window_map.borrow().get_surface_under((x, y)); self.pointer.motion( @@ -131,7 +131,7 @@ impl InputHandler for LibinputInputHandler { // change the keyboard focus let under = self.window_map .borrow_mut() - .get_surface_and_bring_to_top(*self.pointer_location.lock().unwrap()); + .get_surface_and_bring_to_top(*self.pointer_location.borrow()); self.keyboard .set_focus(under.as_ref().map(|&(ref s, _)| s), serial); wl_pointer::ButtonState::Pressed @@ -188,7 +188,7 @@ fn main() { * Initialize session on the current tty */ let (session, mut notifier) = DirectSession::new(None, log.clone()).unwrap(); - let session = Arc::new(Mutex::new(session)); + let session = Rc::new(RefCell::new(session)); let running = Arc::new(AtomicBool::new(true)); let r = running.clone(); @@ -196,7 +196,7 @@ fn main() { r.store(false, Ordering::SeqCst); }).expect("Error setting Ctrl-C handler"); - let pointer_location = Arc::new(Mutex::new((0.0, 0.0))); + let pointer_location = Rc::new(RefCell::new((0.0, 0.0))); /* * Initialize the udev backend @@ -264,9 +264,9 @@ fn main() { * Initialize libinput backend */ let seat = session.seat(); - let mut libinput_context = Libinput::new_from_udev::>>>(session.into(), &context); + let mut libinput_context = Libinput::new_from_udev::>>>(session.into(), &context); let libinput_session_id = notifier.register(libinput_context.clone()); - libinput_context.udev_assign_seat(&seat); + libinput_context.udev_assign_seat(&seat).unwrap(); let mut libinput_backend = LibinputInputBackend::new(libinput_context, log.clone()); libinput_backend.set_handler(LibinputInputHandler { log: log.clone(), @@ -312,7 +312,7 @@ struct UdevHandlerImpl { shell_state_token: StateToken>, compositor_token: CompositorToken, window_map: Rc>, - pointer_location: Arc>, + pointer_location: Rc>, pointer_image: ImageBuffer, Vec>, logger: ::slog::Logger, } @@ -410,7 +410,7 @@ pub struct DrmHandlerImpl { shell_state_token: StateToken>, compositor_token: CompositorToken, window_map: Rc>, - pointer_location: Arc>, + pointer_location: Rc>, logger: ::slog::Logger, } @@ -420,7 +420,7 @@ impl DrmHandler> for DrmHandlerImpl { let state = state.into(); let drawer = state.get(backend); { - let (x, y) = *self.pointer_location.lock().unwrap(); + let (x, y) = *self.pointer_location.borrow(); let _ = (**drawer).set_cursor_position(x.trunc().abs() as u32, y.trunc().abs() as u32); } let mut frame = drawer.draw(); diff --git a/src/backend/session/mod.rs b/src/backend/session/mod.rs index 507e986..e69cd59 100644 --- a/src/backend/session/mod.rs +++ b/src/backend/session/mod.rs @@ -1,5 +1,7 @@ use std::path::Path; use std::sync::{Arc, Mutex}; +use std::rc::Rc; +use std::cell::RefCell; use std::os::unix::io::RawFd; use nix::fcntl::OFlag; use wayland_server::StateProxy; @@ -41,6 +43,30 @@ impl Session for () { fn seat(&self) -> String { String::from("seat0") } } +impl Session for Rc> { + type Error = S::Error; + + fn open(&mut self, path: &Path, flags: OFlag) -> Result { + self.borrow_mut().open(path, flags) + } + + fn close(&mut self, fd: RawFd) -> Result<(), Self::Error> { + self.borrow_mut().close(fd) + } + + fn change_vt(&mut self, vt: i32) -> Result<(), Self::Error> { + self.borrow_mut().change_vt(vt) + } + + fn is_active(&self) -> bool { + self.borrow().is_active() + } + + fn seat(&self) -> String { + self.borrow().seat() + } +} + impl Session for Arc> { type Error = S::Error; From 24f2d499f3c9f3197c81cb75ea72d9563d341a5d Mon Sep 17 00:00:00 2001 From: Drakulix Date: Sat, 25 Nov 2017 13:55:26 +0100 Subject: [PATCH 05/29] Add more logging to session --- src/backend/session/direct.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/backend/session/direct.rs b/src/backend/session/direct.rs index 741096d..2aad341 100644 --- a/src/backend/session/direct.rs +++ b/src/backend/session/direct.rs @@ -250,11 +250,14 @@ impl Session for DirectSession { type Error = NixError; fn open(&mut self, path: &Path, flags: OFlag) -> NixResult { - info!(self.logger, "Opening device: {:?}", path); - open(path, flags, Mode::empty()) + debug!(self.logger, "Opening device: {:?}", path); + let fd = open(path, flags, Mode::empty())?; + trace!(self.logger, "Fd num: {:?}", fd); + Ok(fd) } fn close(&mut self, fd: RawFd) -> NixResult<()> { + debug!(self.logger, "Closing device: {:?}", fd); close(fd) } From b4be5a5786c107c7aa771cf26b19fa145231ad88 Mon Sep 17 00:00:00 2001 From: Drakulix Date: Sat, 25 Nov 2017 13:56:38 +0100 Subject: [PATCH 06/29] Comment out logind integration for now --- Cargo.toml | 3 ++- src/backend/session/mod.rs | 5 +++-- src/lib.rs | 6 ++++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 06ba3f0..c1cab99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ wayland-protocols = { version = "0.12.0", features = ["unstable_protocols", "ser image = "0.17.0" error-chain = "0.11.0" dbus = { version = "0.5.4", optional = true } +systemd = { version = "0.1.0", optional = true } [build-dependencies] gl_generator = "0.5" @@ -43,6 +44,6 @@ backend_drm = ["drm", "gbm"] backend_libinput = ["input"] backend_session = [] backend_session_udev = ["libudev", "backend_session"] -backend_session_logind = ["backend_session", "dbus"] +backend_session_logind = ["backend_session", "dbus", "systemd"] backend_udev = ["libudev", "backend_drm", "backend_session_udev"] renderer_glium = ["glium"] diff --git a/src/backend/session/mod.rs b/src/backend/session/mod.rs index e69cd59..5a297ca 100644 --- a/src/backend/session/mod.rs +++ b/src/backend/session/mod.rs @@ -102,5 +102,6 @@ impl AsErrno for () { } pub mod direct; -#[cfg(feature = "backend_session_logind")] -pub mod logind; + +//#[cfg(feature = "backend_session_logind")] +//pub mod logind; diff --git a/src/lib.rs b/src/lib.rs index 95042e3..0ac797e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,6 +25,12 @@ extern crate gbm; extern crate input; #[cfg(feature = "backend_udev")] extern crate libudev; +/* +#[cfg(feature = "backend_session_logind")] +extern crate dbus; +#[cfg(feature = "backend_session_logind")] +extern crate systemd; +*/ #[cfg(feature = "backend_winit")] extern crate wayland_client; #[cfg(feature = "backend_winit")] From ed04f2d2939dbe89a5a3d0a914b68c44e3e528ed Mon Sep 17 00:00:00 2001 From: Drakulix Date: Sat, 25 Nov 2017 13:58:59 +0100 Subject: [PATCH 07/29] Clarify comment --- examples/udev.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/udev.rs b/examples/udev.rs index 142b7d9..48aa1d9 100644 --- a/examples/udev.rs +++ b/examples/udev.rs @@ -385,7 +385,7 @@ impl UdevHandler, DrmHandlerImpl> for UdevHandlerImpl { } fn device_changed<'a, S: Into>>(&mut self, state: S, device: &StateToken>>) { - //quick and dirty + //quick and dirt, just re-init the device let mut state = state.into(); self.device_removed(&mut state, device); state.with_value(device, |state, device| self.scan_connectors(state, device)); From ef2e0571e74925628f4b58ec6a6028aaac109fcc Mon Sep 17 00:00:00 2001 From: Drakulix Date: Sat, 25 Nov 2017 14:02:15 +0100 Subject: [PATCH 08/29] Swap session signal to usr2, because xwayland may use usr1 --- src/backend/session/direct.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backend/session/direct.rs b/src/backend/session/direct.rs index 2aad341..3212a38 100644 --- a/src/backend/session/direct.rs +++ b/src/backend/session/direct.rs @@ -225,11 +225,11 @@ impl DirectSession { /* let signal = if tty::__libc_current_sigrtmin() > tty::__libc_current_sigrtmax() { warn!(logger, "Not enough real-time signals available, falling back to USR1"); - nix::sys::signal::SIGUSR1 as i32 + nix::sys::signal::SIGUSR2 as i32 } else { tty::__libc_current_sigrtmin() };*/ - let signal = signal::SIGUSR1; + let signal = signal::SIGUSR2; let mode = tty::VtMode { mode: tty::VT_PROCESS, From 0350dca972b2bd92c08958763a3735c83fe0be93 Mon Sep 17 00:00:00 2001 From: Drakulix Date: Mon, 27 Nov 2017 17:12:35 +0100 Subject: [PATCH 09/29] Automatically add udev backend to the event loop store --- examples/udev.rs | 3 +-- src/backend/udev.rs | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/examples/udev.rs b/examples/udev.rs index 48aa1d9..0bee0a6 100644 --- a/examples/udev.rs +++ b/examples/udev.rs @@ -203,7 +203,7 @@ fn main() { */ let context = libudev::Context::new().unwrap(); let bytes = include_bytes!("resources/cursor2.rgba"); - let udev + let udev_token = UdevBackend::new(&mut event_loop, &context, session.clone(), UdevHandlerImpl { shell_state_token, compositor_token, @@ -213,7 +213,6 @@ fn main() { logger: log.clone(), }, log.clone()).unwrap(); - let udev_token = event_loop.state().insert(udev); let udev_session_id = notifier.register(udev_token.clone()); let (seat_token, _) = Seat::new(&mut event_loop, session.seat().into(), log.clone()); diff --git a/src/backend/udev.rs b/src/backend/udev.rs index 794cb76..e7aac4a 100644 --- a/src/backend/udev.rs +++ b/src/backend/udev.rs @@ -28,7 +28,7 @@ impl + Borrow + 'static, H: DrmHandler + 'sta mut session: S, mut handler: T, logger: L) - -> Result> + -> Result>> where L: Into> { @@ -104,13 +104,13 @@ impl + Borrow + 'static, H: DrmHandler + 'sta builder.match_subsystem("drm").chain_err(|| ErrorKind::FailedToInitMonitor)?; let monitor = builder.listen().chain_err(|| ErrorKind::FailedToInitMonitor)?; - Ok(UdevBackend { + Ok(evlh.state().insert(UdevBackend { devices, monitor, session, handler, logger, - }) + })) } pub fn close<'a, ST: Into>>(mut self, state: ST) { From 13be5b1634416d955539f4b302102b8b387adf32 Mon Sep 17 00:00:00 2001 From: Drakulix Date: Wed, 29 Nov 2017 20:00:16 +0100 Subject: [PATCH 10/29] Add documentation --- src/backend/drm/backend.rs | 1 + src/backend/drm/mod.rs | 22 ++++++- src/backend/libinput.rs | 6 ++ src/backend/session/direct.rs | 111 +++++++++++++++++++++------------- src/backend/session/logind.rs | 27 +++++++++ src/backend/session/mod.rs | 48 +++++++++++++++ src/backend/udev.rs | 67 ++++++++++++++++++++ 7 files changed, 237 insertions(+), 45 deletions(-) diff --git a/src/backend/drm/backend.rs b/src/backend/drm/backend.rs index e784321..9343003 100644 --- a/src/backend/drm/backend.rs +++ b/src/backend/drm/backend.rs @@ -375,6 +375,7 @@ impl DrmBackend { Ok(()) } + /// Returns the crtc id used by this backend pub fn crtc(&self) -> crtc::Handle { self.crtc } diff --git a/src/backend/drm/mod.rs b/src/backend/drm/mod.rs index 1b8bacd..c603463 100644 --- a/src/backend/drm/mod.rs +++ b/src/backend/drm/mod.rs @@ -457,14 +457,20 @@ impl + Borrow + 'static> DrmDevice { Ok(self.backends.get(&crtc).unwrap()) } + /// Get the current backend for a given crtc if any pub fn backend_for_crtc(&self, crtc: &crtc::Handle) -> Option<&StateToken> { self.backends.get(crtc) } + /// Get all belonging backends pub fn current_backends(&self) -> Vec<&StateToken> { self.backends.values().collect() } + /// Destroy the backend using a given crtc if any + /// + /// ## Panics + /// Panics if the backend is already borrowed from the state pub fn destroy_backend<'a, S>(&mut self, state: S, crtc: &crtc::Handle) where S: Into> @@ -474,6 +480,11 @@ impl + Borrow + 'static> DrmDevice { } } + /// Close the device + /// + /// ## Warning + /// Never call this function if the device is managed by another backend e.g. the `UdevBackend`. + /// Only use this function for manually initialized devices. pub fn close(self) -> NixResult<()> { let fd = self.as_raw_fd(); mem::drop(self); @@ -517,12 +528,20 @@ pub trait DrmHandler + 'static> { /// /// The `id` argument is the `Id` of the `DrmBackend` that finished rendering, /// check using `DrmBackend::is`. + /// + /// ## Panics + /// The device is already borrowed from the given `state`. Borrowing it again will panic + /// and is not necessary as it is already provided via the `device` parameter. fn ready<'a, S: Into>>(&mut self, state: S, device: &mut DrmDevice, backend: &StateToken, 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. + /// the whole stack has to be recreated.. + /// + /// ## Panics + /// The device is already borrowed from the given `state`. Borrowing it again will panic + /// and is not necessary as it is already provided via the `device` parameter. fn error<'a, S: Into>>(&mut self, state: S, device: &mut DrmDevice, error: DrmError); } @@ -610,6 +629,5 @@ impl + 'static> SessionObserver for StateToken(S); impl From for LibinputSessionInterface { @@ -585,6 +587,10 @@ impl libinput::LibinputInterface for LibinputSessionInterface { } } +/// Binds a `LibinputInputBackend` to a given `EventLoop`. +/// +/// Automatically feeds the backend with incoming events without any manual calls to +/// `dispatch_new_events`. Should be used to achieve the smallest possible latency. pub fn libinput_bind(backend: LibinputInputBackend, evlh: &mut EventLoopHandle) -> IoResult> { diff --git a/src/backend/session/direct.rs b/src/backend/session/direct.rs index 3212a38..f72acf7 100644 --- a/src/backend/session/direct.rs +++ b/src/backend/session/direct.rs @@ -1,3 +1,51 @@ +//! +//! Implementation of the `Session` trait through the legacy vt kernel interface. +//! +//! This requires write permissions for the given tty device and any devices opened through this +//! interface. This means it will almost certainly require root permissions and not allow to run +//! the compositor as an unpriviledged user. Use this session type *only* as a fallback or for testing, +//! if anything better is available. +//! +//! ## How to use it +//! +//! ### Initialization +//! +//! To initialize the session you may pass the path to any tty device, that shall be used. +//! If no path is given the tty used to start this compositor (if any) will be used. +//! A new session and its notifier will be returned. +//! +//! ```rust,no_run +//! extern crate smithay; +//! +//! use smithay::backend::session::direct::DirectSession; +//! +//! # fn main() { +//! let (session, mut notifier) = DirectSession::new(None, None).unwrap(); +//! # } +//! ``` +//! +//! ### Usage of the session +//! +//! The session may be used to open devices manually through the `Session` interface +//! or be passed to other object that need to open devices themselves. +//! +//! Examples for those are e.g. the `LibinputInputBackend` (its context might be initialized through a +//! `Session` via the `LibinputSessionInterface`) or the `UdevBackend`. +//! +//! In case you want to pass the same `Session` to multiple objects, `Session` is implement for +//! every `Rc>` or `Arc>`. +//! +//! ### Usage of the session notifier +//! +//! The notifier might be used to pause device access, when the session gets paused (e.g. by +//! switching the tty via `DirectSession::change_vt`) and to automatically enable it again, +//! when the session becomes active again. +//! +//! It is crutial to avoid errors during that state. Examples for object that might be registered +//! for notifications are the `Libinput` context, the `UdevBackend` or a `DrmDevice` (handled +//! automatically by the `UdevBackend`, if not done manually). +//! ``` + use std::io::Result as IoResult; use std::path::Path; use std::os::unix::io::RawFd; @@ -17,6 +65,7 @@ use libudev::Context; use super::{AsErrno, Session, SessionNotifier, SessionObserver}; +#[allow(dead_code)] mod tty { ioctl!(bad read kd_get_mode with 0x4B3B; i16); ioctl!(bad write_int kd_set_mode with 0x4B3A); @@ -59,60 +108,17 @@ mod tty { } } - -// on freebsd and dragonfly -#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] -const DRM_MAJOR: u64 = 145; - -// on netbsd -#[cfg(target_os = "netbsd")] -const DRM_MAJOR: u64 = 34; - -// on openbsd (32 & 64 bit) -#[cfg(all(target_os = "openbsd", target_pointer_width = "32"))] -const DRM_MAJOR: u64 = 88; -#[cfg(all(target_os = "openbsd", target_pointer_width = "64"))] -const DRM_MAJOR: u64 = 87; - -// on linux/android -#[cfg(any(target_os = "linux", target_os = "android"))] -const DRM_MAJOR: u64 = 226; - #[cfg(any(target_os = "linux", target_os = "android"))] 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"))] -fn is_drm_device(dev: dev_t, _path: &Path) -> bool { - major(dev) == DRM_MAJOR -} - #[cfg(not(feature = "backend_session_udev"))] fn is_tty_device(dev: dev_t, _path: Option<&Path>) -> bool { major(dev) == TTY_MAJOR } -#[cfg(feature = "backend_session_udev")] -fn is_drm_device(dev: dev_t, path: &Path) -> bool { - let udev = match Context::new() { - Ok(context) => context, - Err(_) => return major(dev) == DRM_MAJOR, - }; - - let device = match udev.device_from_syspath(path) { - Ok(device) => device, - Err(_) => return major(dev) == DRM_MAJOR, - }; - - if let Some(subsystem) = device.subsystem() { - subsystem == "drm" - } else { - major(dev) == DRM_MAJOR - } -} - #[cfg(feature = "backend_session_udev")] fn is_tty_device(dev: dev_t, path: Option<&Path>) -> bool { match path { @@ -138,6 +144,7 @@ fn is_tty_device(dev: dev_t, path: Option<&Path>) -> bool { } } +/// `Session` via the virtual terminal direct kernel interface pub struct DirectSession { tty: RawFd, active: Arc, @@ -146,6 +153,7 @@ pub struct DirectSession { logger: ::slog::Logger, } +/// `SessionNotifier` via the virtual terminal direct kernel interface pub struct DirectSessionNotifier { tty: RawFd, active: Arc, @@ -155,6 +163,9 @@ pub struct DirectSessionNotifier { } impl DirectSession { + /// Tries to creates a new session via the legacy virtual terminal interface. + /// + /// If you do not provide a tty device path, it will try to open the currently active tty if any. pub fn new(tty: Option<&Path>, logger: L) -> Result<(DirectSession, DirectSessionNotifier)> where L: Into> @@ -244,6 +255,11 @@ impl DirectSession { Ok((vt_num, old_keyboard_mode, signal)) } + + /// Get the number of the virtual terminal used by this session + pub fn vt(&self) -> i32 { + self.vt + } } impl Session for DirectSession { @@ -323,6 +339,11 @@ impl SessionNotifier for DirectSessionNotifier { } } +/// Bind a `DirectSessionNotifier` to an `EventLoop`. +/// +/// Allows the `DirectSessionNotifier` to listen for the incoming signals signalling the session state. +/// If you don't use this function `DirectSessionNotifier` will not correctly tell you the current +/// session state. pub fn direct_session_bind(notifier: DirectSessionNotifier, evlh: &mut EventLoopHandle, _logger: L) -> IoResult> where @@ -332,6 +353,7 @@ where evlh.add_signal_event_source(|evlh, notifier, _| { if notifier.is_active() { + info!(notifier.logger, "Session shall become inactive"); for signal in &mut notifier.signals { if let &mut Some(ref mut signal) = signal {signal.pause(&mut evlh.state().as_proxy()); } } @@ -339,7 +361,9 @@ where unsafe { tty::vt_rel_disp(notifier.tty, 1).expect("Unable to release tty lock"); } + debug!(notifier.logger, "Session is now inactive"); } else { + debug!(notifier.logger, "Session will become active again"); unsafe { tty::vt_rel_disp(notifier.tty, tty::VT_ACKACQ).expect("Unable to acquire tty lock"); } @@ -347,6 +371,7 @@ where if let &mut Some(ref mut signal) = signal { signal.activate(&mut evlh.state().as_proxy()); } } notifier.active.store(true, Ordering::SeqCst); + info!(notifier.logger, "Session is now active again"); } }, notifier, signal) } diff --git a/src/backend/session/logind.rs b/src/backend/session/logind.rs index e69de29..2df3563 100644 --- a/src/backend/session/logind.rs +++ b/src/backend/session/logind.rs @@ -0,0 +1,27 @@ +use dbus::{BusType, Connection as DbusConnection}; +use systemd::login as logind; + +pub struct LogindSession { + dbus: DbusConnection, +} + +impl Session for LogindSession { + +} + +impl LogindSession { + pub fn new() -> Result { + let session = logind::get_session(None)?; + let vt = logind::get_vt(&session)?; + let seat = logind::get_seat(&session)?; + + let dbus = DbusConnection::get_private(BusType::System)?; + + } +} + +error_chain! { + errors { + + } +} diff --git a/src/backend/session/mod.rs b/src/backend/session/mod.rs index 5a297ca..67aab3d 100644 --- a/src/backend/session/mod.rs +++ b/src/backend/session/mod.rs @@ -1,3 +1,15 @@ +//! +//! Abstraction of different session apis. +//! +//! Sessions provide a way for multiple graphical systems to run in parallel by providing +//! mechanisms to switch between and handle device access and permissions for every running +//! instance. +//! +//! They are crutial to allow unpriviledged processes to use graphical or input devices. +//! +//! The following mechanisms are currently provided: +//! - direct - legacy tty / virtual terminal kernel api +//! use std::path::Path; use std::sync::{Arc, Mutex}; use std::rc::Rc; @@ -6,28 +18,62 @@ use std::os::unix::io::RawFd; use nix::fcntl::OFlag; use wayland_server::StateProxy; +/// General session interface. +/// +/// Provides a way to open and close devices and change the active vt. pub trait Session { + /// Error type of the implementation type Error: AsErrno; + /// Opens a device at the given `path` with the given flags. + /// + /// Returns a raw file descriptor fn open(&mut self, path: &Path, flags: OFlag) -> Result; + /// Close a previously opened file descriptor fn close(&mut self, fd: RawFd) -> Result<(), Self::Error>; + /// Change the currently active virtual terminal fn change_vt(&mut self, vt: i32) -> Result<(), Self::Error>; + /// Check if this session is currently active fn is_active(&self) -> bool; + /// Which seat this session is on fn seat(&self) -> String; } +/// Interface for registering for notifications for a given session. +/// +/// Part of the session api which allows to get notified, when the given session +/// gets paused or becomes active again. Any object implementing the `SessionObserver` trait +/// may be registered. pub trait SessionNotifier { + /// Registers a given `SessionObserver`. + /// + /// Returns an id of the inserted observer, can be used to remove it again. fn register(&mut self, signal: S) -> usize; + /// Removes an observer by its given id from `SessionNotifier::register`. fn unregister(&mut self, signal: usize); + /// 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 be notified when the session pauses or becomes active again. +/// +/// It might be impossible to interact with devices while the session is disabled. +/// This interface provides callbacks for when that happens. pub trait SessionObserver { + /// Session is about to be paused. + /// + /// In case the implementor is a `StateToken` the state of the `EventLoop` + /// is provided via a `StateProxy`. fn pause<'a>(&mut self, state: &mut StateProxy<'a>); + /// Session got active again + /// + /// In case the implementor is a `StateToken` the state of the `EventLoop` + /// is provided via a `StateProxy`. fn activate<'a>(&mut self, state: &mut StateProxy<'a>); } @@ -91,7 +137,9 @@ impl Session for Arc> { } } +/// Allows errors to be described by an error number pub trait AsErrno: ::std::fmt::Debug { + /// Returns the error number representing this error if any fn as_errno(&self) -> Option; } diff --git a/src/backend/udev.rs b/src/backend/udev.rs index e7aac4a..daf8754 100644 --- a/src/backend/udev.rs +++ b/src/backend/udev.rs @@ -1,3 +1,14 @@ +//! +//! Provides `udev` related functionality for automated device scanning. +//! +//! This module mainly provides the `UdevBackend`, which constantly monitors available drm devices +//! and notifies a user supplied `UdevHandler` of any changes. +//! +//! Additionally this contains some utility functions related to scanning. +//! +//! See also `examples/udev.rs` for pure hardware backed example of a compositor utilizing this +//! backend. + use libudev::{Context, MonitorBuilder, MonitorSocket, Event, EventType, Enumerator, Result as UdevResult}; use nix::fcntl; use nix::sys::stat::{dev_t, fstat}; @@ -14,6 +25,11 @@ use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest}; use ::backend::drm::{DrmDevice, DrmBackend, DrmHandler, drm_device_bind}; use ::backend::session::{Session, SessionObserver}; +/// Graphical backend that monitors 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 +/// attached monitors. pub struct UdevBackend + 'static, H: DrmHandler + 'static, S: Session + 'static, T: UdevHandler + 'static> { devices: HashMap>, FdEventSource<(StateToken>, H)>)>, monitor: MonitorSocket, @@ -23,6 +39,14 @@ pub struct UdevBackend + 'static, H: DrmHandler + 'stat } impl + Borrow + 'static, H: DrmHandler + 'static, S: Session + 'static, T: UdevHandler + 'static> UdevBackend { + /// 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 + /// `logger` - slog Logger to be used by the backend and its `DrmDevices`. pub fn new<'a, L>(mut evlh: &mut EventLoopHandle, context: &Context, mut session: S, @@ -113,6 +137,14 @@ impl + Borrow + 'static, H: DrmHandler + 'sta })) } + /// Closes the udev backend and frees all remaining open devices. + /// + /// Needs to be called after the `FdEventSource` was removed and the backend was removed from + /// the `EventLoop`'s `State`. + /// + /// ## Panics + /// The given state might be passed to the registered `UdevHandler::device_removed` callback. + /// Make sure not to borrow any tokens twice. pub fn close<'a, ST: Into>>(mut self, state: ST) { let mut state = state.into(); for (_, (mut device, event_source)) in self.devices.drain() { @@ -147,6 +179,10 @@ impl + 'static, H: DrmHandler + 'static, S: Session + ' } } +/// Binds a `UdevBackend` to a given `EventLoop`. +/// +/// Allows the backend to recieve kernel events and thus to drive the `UdevHandler`. +/// No runtime functionality can be provided without using this function. pub fn udev_backend_bind(evlh: &mut EventLoopHandle, udev: StateToken>) -> IoResult>>> where @@ -275,13 +311,41 @@ where } } +/// Handler for the `UdevBackend`, allows to open, close and update drm devices as they change during runtime. pub trait UdevHandler + 'static, H: DrmHandler + '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<'a, S: Into>>(&mut self, state: S, device: &mut DrmDevice) -> Option; + /// 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<'a, S: Into>>(&mut self, state: S, device: &StateToken>); + /// 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<'a, S: Into>>(&mut self, state: S, device: &StateToken>); + /// 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<'a, S: Into>>(&mut self, state: S, error: IoError); } +/// Returns the path of the primary gpu device if any +/// +/// Might be used for filtering in `UdevHandler::device_added` or for manual `DrmDevice` initialization pub fn primary_gpu>(context: &Context, seat: S) -> UdevResult> { let mut enumerator = Enumerator::new(context)?; enumerator.match_subsystem("drm")?; @@ -304,6 +368,9 @@ pub fn primary_gpu>(context: &Context, seat: S) -> UdevResult>(context: &Context, seat: S) -> UdevResult> { let mut enumerator = Enumerator::new(context)?; enumerator.match_subsystem("drm")?; From b5b5af9b996c368454152c2d457a18ef0522326a Mon Sep 17 00:00:00 2001 From: Drakulix Date: Thu, 30 Nov 2017 01:45:53 +0100 Subject: [PATCH 11/29] Temporary build fix --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c1cab99..8f7332b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ libloading = "0.4.0" wayland-client = { version = "0.9.9", optional = true } winit = { version = "0.8.2", optional = true } drm = { version = "^0.3.0", optional = true } -gbm = { version = "^0.3.0", optional = true } +gbm = { version = "^0.3.0", optional = true, default-features = false, features = ["drm-support"] } glium = { version = "0.17.1", optional = true, default-features = false } input = { version = "0.4.0", git = "https://github.com/Smithay/input.rs.git", branch = "feature/udev", optional = true } libudev = { git = "https://github.com/Drakulix/libudev-rs.git", branch = "feature/raw_ffi_access", optional = true } From 612436e42de06759a98b8f1f6c10f6f4be652603 Mon Sep 17 00:00:00 2001 From: Drakulix Date: Sat, 2 Dec 2017 13:51:08 +0100 Subject: [PATCH 12/29] Correctly scan for any encoders --- src/backend/drm/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/drm/mod.rs b/src/backend/drm/mod.rs index c603463..bca5375 100644 --- a/src/backend/drm/mod.rs +++ b/src/backend/drm/mod.rs @@ -441,7 +441,7 @@ impl + Borrow + 'static> DrmDevice { if !encoders .iter() .map(|encoder| encoder.possible_crtcs()) - .all(|crtc_list| { + .any(|crtc_list| { resource_handles.filter_crtcs(crtc_list).contains(&crtc) }) { bail!(ErrorKind::NoSuitableEncoder(con_info, crtc)) From 33286df0d60ce8fdc962b1cce7c1220059d9dfad Mon Sep 17 00:00:00 2001 From: Drakulix Date: Sat, 2 Dec 2017 14:24:39 +0100 Subject: [PATCH 13/29] Make it impossible to pass invalid `SessionObserver` ids. - Add an associated type to the `SessionNotifier` trait for the returned Id's instead of using usize. - Create a new Id type for the `DirectSessionNotifier`'s implementation, wrapping the previously used usize. - Derive necessary traits of the new wrapper, make internal value inaccessible and Id's not publically constructable. --- src/backend/session/direct.rs | 13 +++++++++---- src/backend/session/mod.rs | 7 +++++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/backend/session/direct.rs b/src/backend/session/direct.rs index f72acf7..1385fbd 100644 --- a/src/backend/session/direct.rs +++ b/src/backend/session/direct.rs @@ -322,13 +322,18 @@ impl Drop for DirectSession { } } +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct Id(usize); + impl SessionNotifier for DirectSessionNotifier { - fn register(&mut self, signal: S) -> usize { + type Id = Id; + + fn register(&mut self, signal: S) -> Id { self.signals.push(Some(Box::new(signal))); - self.signals.len() - 1 + Id(self.signals.len() - 1) } - fn unregister(&mut self, signal: usize) { - self.signals[signal] = None; + fn unregister(&mut self, signal: Id) { + self.signals[signal.0] = None; } fn is_active(&self) -> bool { diff --git a/src/backend/session/mod.rs b/src/backend/session/mod.rs index 67aab3d..986a114 100644 --- a/src/backend/session/mod.rs +++ b/src/backend/session/mod.rs @@ -47,12 +47,15 @@ pub trait Session { /// gets paused or becomes active again. Any object implementing the `SessionObserver` trait /// may be registered. pub trait SessionNotifier { + /// Id type of registered observers + type Id: PartialEq + Eq; + /// Registers a given `SessionObserver`. /// /// Returns an id of the inserted observer, can be used to remove it again. - fn register(&mut self, signal: S) -> usize; + fn register(&mut self, signal: S) -> Self::Id; /// Removes an observer by its given id from `SessionNotifier::register`. - fn unregister(&mut self, signal: usize); + fn unregister(&mut self, signal: Self::Id); /// Check if this session is currently active fn is_active(&self) -> bool; From bd83bc5b2dcc06759f1eed499cea8c58623a05c8 Mon Sep 17 00:00:00 2001 From: Drakulix Date: Sat, 2 Dec 2017 17:07:52 +0100 Subject: [PATCH 14/29] Restore drm state correctly --- Cargo.toml | 3 +++ src/backend/drm/backend.rs | 4 ++++ src/backend/drm/mod.rs | 38 +++++++++++++++++++++++++++++++++++--- 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8f7332b..8d079e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,3 +47,6 @@ backend_session_udev = ["libudev", "backend_session"] backend_session_logind = ["backend_session", "dbus", "systemd"] backend_udev = ["libudev", "backend_drm", "backend_session_udev"] renderer_glium = ["glium"] + +[patch.crates-io] +drm = { git = "https://github.com/Drakulix/drm-rs.git", branch = "feature/current_encoder" } diff --git a/src/backend/drm/backend.rs b/src/backend/drm/backend.rs index 9343003..4ff7533 100644 --- a/src/backend/drm/backend.rs +++ b/src/backend/drm/backend.rs @@ -385,6 +385,7 @@ impl Drop for DrmBackend { fn drop(&mut self) { // Drop framebuffers attached to the userdata of the gbm surface buffers. // (They don't implement drop, as they need the device) + let crtc = self.crtc; self.graphics.rent_all_mut(|graphics| { if let Some(fb) = graphics.gbm.surface.rent(|egl| { if let Some(mut next) = egl.buffers.next_buffer.take() { @@ -406,6 +407,9 @@ impl Drop for DrmBackend { // ignore failure at this point let _ = framebuffer::destroy(&*graphics.context.devices.drm, fb.handle()); } + + // ignore failure at this point + let _ = crtc::clear_cursor(&*graphics.context.devices.drm, crtc); }) } } diff --git a/src/backend/drm/mod.rs b/src/backend/drm/mod.rs index bca5375..aa63895 100644 --- a/src/backend/drm/mod.rs +++ b/src/backend/drm/mod.rs @@ -262,6 +262,7 @@ use self::devices::{Context, Devices}; pub struct DrmDevice + 'static> { context: Rc, backends: HashMap>, + old_state: HashMap)>, active: bool, logger: ::slog::Logger, } @@ -361,6 +362,31 @@ impl + Borrow + 'static> DrmDevice { // we want to mode-set, so we better be the master drm.set_master().chain_err(|| ErrorKind::DrmMasterFailed)?; + let mut old_state = HashMap::new(); + let res_handles = drm.resource_handles() + .chain_err(|| { + ErrorKind::DrmDev(format!("Loading drm resources on {:?}", drm)) + })?; + for &con in res_handles.connectors() { + let con_info = connector::Info::load_from_device(&drm, con) + .chain_err(|| { + ErrorKind::DrmDev(format!("Loading connector info on {:?}", drm)) + })?; + if let Some(enc) = con_info.current_encoder() { + let enc_info = encoder::Info::load_from_device(&drm, enc) + .chain_err(|| { + ErrorKind::DrmDev(format!("Loading encoder info on {:?}", drm)) + })?; + if let Some(crtc) = enc_info.current_crtc() { + let info = crtc::Info::load_from_device(&drm, crtc) + .chain_err(|| { + ErrorKind::DrmDev(format!("Loading crtc info on {:?}", drm)) + })?; + old_state.entry(crtc).or_insert((info, Vec::new())).1.push(con); + } + } + } + // Open the gbm device from the drm device and create a context based on that Ok(DrmDevice { context: Rc::new(Context::try_new( @@ -384,6 +410,7 @@ impl + Borrow + 'static> DrmDevice { }, )?), backends: HashMap::new(), + old_state, active: true, logger: log, }) @@ -415,7 +442,7 @@ impl + Borrow + 'static> DrmDevice { for connector in &connectors { let con_info = connector::Info::load_from_device(self.context.head().head(), *connector) .chain_err(|| { - ErrorKind::DrmDev(format!("{:?}", self.context.head().head())) + ErrorKind::DrmDev(format!("Loading connector info on {:?}", self.context.head().head())) })?; // check the mode @@ -429,14 +456,14 @@ impl + Borrow + 'static> DrmDevice { .iter() .map(|encoder| { encoder::Info::load_from_device(self.context.head().head(), *encoder).chain_err(|| { - ErrorKind::DrmDev(format!("{:?}", self.context.head().head())) + ErrorKind::DrmDev(format!("Loading encoder info on {:?}", self.context.head().head())) }) }) .collect::>>()?; // and if any encoder supports the selected crtc let resource_handles = self.resource_handles().chain_err(|| { - ErrorKind::DrmDev(format!("{:?}", self.context.head().head())) + ErrorKind::DrmDev(format!("Loading drm resources on {:?}", self.context.head().head())) })?; if !encoders .iter() @@ -507,6 +534,11 @@ impl + 'static> Drop for DrmDevice { if Rc::strong_count(&self.context) > 1 { panic!("Pending DrmBackends. Please 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.head().head(), handle, info.fb(), &connectors, info.position(), info.mode()) { + error!(self.logger, "Failed to reset crtc ({:?}). Error: {}", handle, err); + } + } if let Err(err) = self.drop_master() { error!(self.logger, "Failed to drop drm master state. Error: {}", err); } From c5e66176c6eea422f9c2ffcf965e14be9a2d154f Mon Sep 17 00:00:00 2001 From: Drakulix Date: Sat, 2 Dec 2017 17:09:34 +0100 Subject: [PATCH 15/29] Document DirectSessionNotifier Id type --- src/backend/session/direct.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/backend/session/direct.rs b/src/backend/session/direct.rs index 1385fbd..9d0958a 100644 --- a/src/backend/session/direct.rs +++ b/src/backend/session/direct.rs @@ -322,6 +322,7 @@ impl Drop for DirectSession { } } +/// Ids of registered `SessionObserver`s of the `DirectSessionNotifier` #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct Id(usize); From 35b20450c5f045cfd16a0dc2d275eed4e09b4c43 Mon Sep 17 00:00:00 2001 From: Drakulix Date: Sun, 3 Dec 2017 20:24:22 +0100 Subject: [PATCH 16/29] Fixup patched drm-rs --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 8d079e5..16b73e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,4 +49,4 @@ backend_udev = ["libudev", "backend_drm", "backend_session_udev"] renderer_glium = ["glium"] [patch.crates-io] -drm = { git = "https://github.com/Drakulix/drm-rs.git", branch = "feature/current_encoder" } +drm = { git = "https://github.com/Drakulix/drm-rs.git", branch = "future" } From ae5df5039d34d94060e0f357ab48cc97e2ab0752 Mon Sep 17 00:00:00 2001 From: Drakulix Date: Sun, 3 Dec 2017 22:33:21 +0100 Subject: [PATCH 17/29] Fix drm example and tests --- examples/drm.rs | 17 ++++++++--------- src/backend/drm/mod.rs | 39 ++++++++++++++++++++++----------------- 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/examples/drm.rs b/examples/drm.rs index 319b8aa..197dcc7 100644 --- a/examples/drm.rs +++ b/examples/drm.rs @@ -31,7 +31,7 @@ use std::cell::RefCell; use std::fs::OpenOptions; use std::rc::Rc; use std::time::Duration; -use wayland_server::{EventLoopHandle, StateToken}; +use wayland_server::{StateToken, StateProxy}; fn main() { // A logger facility, here we use the terminal for this example @@ -84,13 +84,13 @@ fn main() { { // Initialize the hardware backend let renderer = device - .create_backend(crtc, mode, vec![connector_info.handle()]) + .create_backend(event_loop.state(), crtc, mode, vec![connector_info.handle()]) .unwrap(); /* * Initialize glium */ - let mut frame = renderer.draw(); + let mut frame = event_loop.state().get(renderer).draw(); frame.clear_color(0.8, 0.8, 0.9, 1.0); frame.finish().unwrap(); } @@ -140,11 +140,10 @@ pub struct DrmHandlerImpl { } impl DrmHandler> for DrmHandlerImpl { - fn ready(&mut self, evlh: &mut EventLoopHandle, device: &StateToken>>, - crtc: &crtc::Handle, _frame: u32, _duration: Duration) { - let state = evlh.state(); - let dev = state.get(device); - let drawer = dev.backend_for_crtc(crtc).unwrap(); + fn ready<'a, S: Into>>(&mut self, state: S, _device: &mut DrmDevice>, + backend: &StateToken>, _crtc: crtc::Handle, _frame: u32, _duration: Duration) { + let state = state.into(); + let drawer = state.get(backend); let mut frame = drawer.draw(); frame.clear_color(0.8, 0.8, 0.9, 1.0); // redraw the frame, in a simple but inneficient way @@ -187,7 +186,7 @@ impl DrmHandler> for DrmHandlerImpl { frame.finish().unwrap(); } - fn error(&mut self, _evlh: &mut EventLoopHandle, _device: &StateToken>>, + fn error<'a, S: Into>>(&mut self, _state: S, _device: &mut DrmDevice>, error: DrmError) { panic!("{:?}", error); } diff --git a/src/backend/drm/mod.rs b/src/backend/drm/mod.rs index aa63895..c7aba96 100644 --- a/src/backend/drm/mod.rs +++ b/src/backend/drm/mod.rs @@ -90,11 +90,11 @@ //! //! // Create the backend //! let backend: StateToken = device.create_backend( -//! &mut event_loop, +//! event_loop.state(), //! crtc, //! mode, //! vec![connector_info.handle()] -//! ).unwrap(); +//! ).unwrap().clone(); //! # } //! ``` //! @@ -118,12 +118,13 @@ //! # //! # 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::OpenOptions; //! # use std::time::Duration; //! use smithay::backend::drm::{DrmDevice, DrmBackend, DrmHandler, drm_device_bind}; //! use smithay::backend::graphics::egl::EGLGraphicsBackend; -//! use wayland_server::{EventLoopHandle, StateToken}; +//! use wayland_server::{StateToken, StateProxy}; //! # //! # fn main() { //! # @@ -145,29 +146,32 @@ //! # let crtc = res_handles.crtcs()[0]; //! # let mode = connector_info.modes()[0]; //! # let backend: StateToken = device.create_backend( -//! # &mut event_loop, +//! # event_loop.state(), //! # crtc, //! # mode, //! # vec![connector_info.handle()] -//! # ).unwrap(); +//! # ).unwrap().clone(); //! //! struct MyDrmHandler; //! //! impl DrmHandler for MyDrmHandler { -//! fn ready(&mut self, -//! evlh: &mut EventLoopHandle, -//! _device: &mut DrmDevice, -//! backend: &StateToken, -//! _frame: u32, -//! _duration: Duration) +//! fn ready<'a, S: Into>>( +//! &mut self, +//! state: S, +//! _device: &mut DrmDevice, +//! backend: &StateToken, +//! _crtc: CrtcHandle, +//! _frame: u32, +//! _duration: Duration) //! { //! // render surfaces and swap again -//! evlh.state().get(backend).swap_buffers().unwrap(); +//! state.into().get(backend).swap_buffers().unwrap(); //! } -//! fn error(&mut self, -//! _: &mut EventLoopHandle, -//! device: &mut DrmDevice, -//! error: DrmError) +//! fn error<'a, S: Into>>( +//! &mut self, +//! _state: S, +//! device: &mut DrmDevice, +//! error: DrmError) //! { //! panic!("DrmDevice errored: {}", error); //! } @@ -176,7 +180,8 @@ //! // render something (like clear_color) //! event_loop.state().get(&backend).swap_buffers().unwrap(); //! -//! let _source = drm_device_bind(&mut event_loop, device, MyDrmHandler).unwrap(); +//! let device_token = event_loop.state().insert(device); +//! let _source = drm_device_bind(&mut event_loop, device_token, MyDrmHandler).unwrap(); //! //! event_loop.run().unwrap(); //! # } From 71f107118fafad12e05486b409e4bb2a31cb54fe Mon Sep 17 00:00:00 2001 From: Drakulix Date: Sun, 3 Dec 2017 22:44:45 +0100 Subject: [PATCH 18/29] Make libinput backend work without the session feature --- src/backend/libinput.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/backend/libinput.rs b/src/backend/libinput.rs index 80d2eb8..4c27a3b 100644 --- a/src/backend/libinput.rs +++ b/src/backend/libinput.rs @@ -555,6 +555,7 @@ impl From for backend::MouseButtonState { } } +#[cfg(feature = "backend_session")] impl SessionObserver for libinput::Libinput { fn pause<'a>(&mut self, _state: &mut StateProxy<'a>) { self.suspend() @@ -568,14 +569,17 @@ impl SessionObserver for libinput::Libinput { /// Wrapper for types implementing the `Session` trait to provide /// a `LibinputInterface` implementation. +#[cfg(feature = "backend_session")] pub struct LibinputSessionInterface(S); +#[cfg(feature = "backend_session")] impl From for LibinputSessionInterface { fn from(session: S) -> LibinputSessionInterface { LibinputSessionInterface(session) } } +#[cfg(feature = "backend_session")] impl libinput::LibinputInterface for LibinputSessionInterface { fn open_restricted(&mut self, path: &Path, flags: i32) -> Result { use nix::fcntl::OFlag; From 40ef1236ea3e3a1fe57676dd8e77d7cd2f1d1cc5 Mon Sep 17 00:00:00 2001 From: Drakulix Date: Wed, 6 Dec 2017 02:06:00 +0100 Subject: [PATCH 19/29] Update to released udev/libinput versions --- Cargo.toml | 8 ++++---- src/backend/session/direct.rs | 2 +- src/backend/udev.rs | 2 +- src/lib.rs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 16b73e6..475fd93 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,8 +19,8 @@ winit = { version = "0.8.2", optional = true } drm = { version = "^0.3.0", optional = true } gbm = { version = "^0.3.0", optional = true, default-features = false, features = ["drm-support"] } glium = { version = "0.17.1", optional = true, default-features = false } -input = { version = "0.4.0", git = "https://github.com/Smithay/input.rs.git", branch = "feature/udev", optional = true } -libudev = { git = "https://github.com/Drakulix/libudev-rs.git", branch = "feature/raw_ffi_access", optional = true } +input = { version = "0.4.0", optional = true } +udev = { version = "0.2.0", optional = true } rental = "0.4.11" wayland-protocols = { version = "0.12.0", features = ["unstable_protocols", "server"] } image = "0.17.0" @@ -43,9 +43,9 @@ backend_winit = ["winit", "wayland-server/dlopen", "wayland-client/dlopen"] backend_drm = ["drm", "gbm"] backend_libinput = ["input"] backend_session = [] -backend_session_udev = ["libudev", "backend_session"] +backend_session_udev = ["udev", "backend_session"] backend_session_logind = ["backend_session", "dbus", "systemd"] -backend_udev = ["libudev", "backend_drm", "backend_session_udev"] +backend_udev = ["udev", "backend_drm", "backend_session_udev"] renderer_glium = ["glium"] [patch.crates-io] diff --git a/src/backend/session/direct.rs b/src/backend/session/direct.rs index 9d0958a..47b9a63 100644 --- a/src/backend/session/direct.rs +++ b/src/backend/session/direct.rs @@ -61,7 +61,7 @@ use wayland_server::EventLoopHandle; use wayland_server::sources::SignalEventSource; #[cfg(feature = "backend_session_udev")] -use libudev::Context; +use udev::Context; use super::{AsErrno, Session, SessionNotifier, SessionObserver}; diff --git a/src/backend/udev.rs b/src/backend/udev.rs index daf8754..23f6eb5 100644 --- a/src/backend/udev.rs +++ b/src/backend/udev.rs @@ -9,7 +9,7 @@ //! See also `examples/udev.rs` for pure hardware backed example of a compositor utilizing this //! backend. -use libudev::{Context, MonitorBuilder, MonitorSocket, Event, EventType, Enumerator, Result as UdevResult}; +use udev::{Context, MonitorBuilder, MonitorSocket, Event, EventType, Enumerator, Result as UdevResult}; use nix::fcntl; use nix::sys::stat::{dev_t, fstat}; use std::borrow::Borrow; diff --git a/src/lib.rs b/src/lib.rs index 0ac797e..074dd9d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,7 +24,7 @@ extern crate gbm; #[cfg(feature = "backend_libinput")] extern crate input; #[cfg(feature = "backend_udev")] -extern crate libudev; +extern crate udev; /* #[cfg(feature = "backend_session_logind")] extern crate dbus; From 5f59ded3f0820f23d31aff1dff465d581954d55a Mon Sep 17 00:00:00 2001 From: Drakulix Date: Thu, 7 Dec 2017 15:20:33 +0100 Subject: [PATCH 20/29] Fix udev example --- examples/udev.rs | 4 ++-- src/backend/udev.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/udev.rs b/examples/udev.rs index 0bee0a6..accbb0b 100644 --- a/examples/udev.rs +++ b/examples/udev.rs @@ -4,7 +4,7 @@ extern crate glium; extern crate rand; extern crate input as libinput; extern crate image; -extern crate libudev; +extern crate udev; #[macro_use(define_roles)] extern crate smithay; extern crate xkbcommon; @@ -201,7 +201,7 @@ fn main() { /* * Initialize the udev backend */ - let context = libudev::Context::new().unwrap(); + let context = udev::Context::new().unwrap(); let bytes = include_bytes!("resources/cursor2.rgba"); let udev_token = UdevBackend::new(&mut event_loop, &context, session.clone(), UdevHandlerImpl { diff --git a/src/backend/udev.rs b/src/backend/udev.rs index 23f6eb5..66fb0e3 100644 --- a/src/backend/udev.rs +++ b/src/backend/udev.rs @@ -147,7 +147,7 @@ impl + Borrow + 'static, H: DrmHandler + 'sta /// Make sure not to borrow any tokens twice. pub fn close<'a, ST: Into>>(mut self, state: ST) { let mut state = state.into(); - for (_, (mut device, event_source)) in self.devices.drain() { + for (_, (device, event_source)) in self.devices.drain() { event_source.remove(); self.handler.device_removed(&mut state, &device); let device = state.remove(device); From c9e7624a339468f4bee75de6f8c633bb75dfb051 Mon Sep 17 00:00:00 2001 From: Drakulix Date: Thu, 7 Dec 2017 15:25:21 +0100 Subject: [PATCH 21/29] Remove (incomplete) logind support for now --- Cargo.toml | 3 --- src/backend/session/logind.rs | 27 --------------------------- src/backend/session/mod.rs | 3 --- 3 files changed, 33 deletions(-) delete mode 100644 src/backend/session/logind.rs diff --git a/Cargo.toml b/Cargo.toml index 475fd93..9def7cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,8 +25,6 @@ rental = "0.4.11" wayland-protocols = { version = "0.12.0", features = ["unstable_protocols", "server"] } image = "0.17.0" error-chain = "0.11.0" -dbus = { version = "0.5.4", optional = true } -systemd = { version = "0.1.0", optional = true } [build-dependencies] gl_generator = "0.5" @@ -44,7 +42,6 @@ backend_drm = ["drm", "gbm"] backend_libinput = ["input"] backend_session = [] backend_session_udev = ["udev", "backend_session"] -backend_session_logind = ["backend_session", "dbus", "systemd"] backend_udev = ["udev", "backend_drm", "backend_session_udev"] renderer_glium = ["glium"] diff --git a/src/backend/session/logind.rs b/src/backend/session/logind.rs deleted file mode 100644 index 2df3563..0000000 --- a/src/backend/session/logind.rs +++ /dev/null @@ -1,27 +0,0 @@ -use dbus::{BusType, Connection as DbusConnection}; -use systemd::login as logind; - -pub struct LogindSession { - dbus: DbusConnection, -} - -impl Session for LogindSession { - -} - -impl LogindSession { - pub fn new() -> Result { - let session = logind::get_session(None)?; - let vt = logind::get_vt(&session)?; - let seat = logind::get_seat(&session)?; - - let dbus = DbusConnection::get_private(BusType::System)?; - - } -} - -error_chain! { - errors { - - } -} diff --git a/src/backend/session/mod.rs b/src/backend/session/mod.rs index 986a114..74a966f 100644 --- a/src/backend/session/mod.rs +++ b/src/backend/session/mod.rs @@ -153,6 +153,3 @@ impl AsErrno for () { } pub mod direct; - -//#[cfg(feature = "backend_session_logind")] -//pub mod logind; From ed2174168dcb6ba0cfd92b7555de1822d7a47e28 Mon Sep 17 00:00:00 2001 From: Drakulix Date: Thu, 14 Dec 2017 21:50:28 +0100 Subject: [PATCH 22/29] Use stable drm-rs master --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 9def7cb..86c6014 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,4 +46,4 @@ backend_udev = ["udev", "backend_drm", "backend_session_udev"] renderer_glium = ["glium"] [patch.crates-io] -drm = { git = "https://github.com/Drakulix/drm-rs.git", branch = "future" } +drm = { git = "https://github.com/Smithay/drm-rs.git" } From b0d4bdc36d7b09564ebde0154b515648a62af5d9 Mon Sep 17 00:00:00 2001 From: Drakulix Date: Thu, 14 Dec 2017 21:46:57 +0100 Subject: [PATCH 23/29] Fix travis build with updated libinput --- .gitignore | 1 + .travis.yml | 82 +++++++++++++++++++++++------------------------------ vagga.yaml | 77 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+), 46 deletions(-) create mode 100644 vagga.yaml diff --git a/.gitignore b/.gitignore index e63a49c..0bf1ddb 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ target Cargo.lock *.bk .vscode +.vagga diff --git a/.travis.yml b/.travis.yml index d8e9b98..bd28f52 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,40 +1,32 @@ language: rust -cache: cargo - -sudo: required - +# We need this for the matrix, install is quick although unused rust: - stable - beta - nightly -os: - - linux +sudo: required dist: trusty +# We cannot cache .vagga, because we actually do not have read permissions +# without sudo and travis cache script runs not as sudo... +cache: + directories: + - .vagga/stable-home + - .vagga/beta-home + - .vagga/nightly-home + - .vagga/.cache + addons: apt: packages: - - libssl-dev - - libudev-dev - - libgbm-dev - - libxkbcommon-dev - - libegl1-mesa-dev - -before_install: - - sudo add-apt-repository -y ppa:wayland.admin/daily-builds - - sudo apt-get update -qq || echo "Ignoring failed apt-get update..." - - sudo apt-get install -y libwayland-dev libinput-dev - -branches: - only: - - master + - libcurl4-openssl-dev + - libelf-dev + - libdw-dev env: - global: - - RUST_BACKTRACE=1 matrix: - FEATURES="" # test individual features @@ -58,38 +50,36 @@ matrix: - rust: nightly before_script: - - export PATH=$HOME/.local/bin:$HOME/.cargo/bin:$PATH + - 'echo ubuntu-mirror: http://mirrors.us.kernel.org/ubuntu/ > ~/.vagga.yaml' + - 'echo alpine-mirror: http://mirrors.gigenet.com/alpinelinux/ >> ~/.vagga.yaml' - | - if [ "$FEATURES" = "cargo-fmt" ]; then - cargo install rustfmt-nightly --force; - fi - - | - if [ "$FEATURES" = "cargo-clippy" ]; then - cargo install clippy --force; - fi - - mkdir $(pwd)/socket - - export XDG_RUNTIME_DIR="$(pwd)/socket" + echo "$(id -un):100000:65536" | sudo tee /etc/subuid | sudo tee /etc/subgid + sudo apt-get install uidmap -y + curl http://files.zerogw.com/vagga/vagga-install.sh | sh + - vagga update-$TRAVIS_RUST_VERSION script: - | case $FEATURES in "all") - cargo test --all-features && - cargo doc --no-deps --all-features + vagga cargo-$TRAVIS_RUST_VERSION test --all-features && + vagga cargo-$TRAVIS_RUST_VERSION doc --no-deps --all-features ;; "default") - cargo test && - cargo doc --no-deps + vagga cargo-$TRAVIS_RUST_VERSION test && + vagga cargo-$TRAVIS_RUST_VERSION doc --no-deps ;; "cargo-fmt") - cargo fmt -- --write-mode=diff + vagga cargo-$TRAVIS_RUST_VERSION install -f rustfmt-nightly && + vagga cargo-$TRAVIS_RUST_VERSION fmt -- --write-mode=diff ;; "cargo-clippy") - cargo clippy --all-features -- -D warnings + vagga cargo-$TRAVIS_RUST_VERSION install -f clippy && + vagga cargo-$TRAVIS_RUST_VERSION clippy --all-features -- -D warnings ;; *) - cargo test --lib --doc --tests --no-default-features --features "$FEATURES" && - cargo doc --no-deps --no-default-features --features "$FEATURES" + vagga cargo-$TRAVIS_RUST_VERSION test --lib --doc --tests --no-default-features --features "$FEATURES" && + vagga cargo-$TRAVIS_RUST_VERSION doc --no-deps --no-default-features --features "$FEATURES" esac after_success: @@ -106,9 +96,9 @@ deploy: condition: "$FEATURES = all" notifications: - webhooks: - urls: - - "https://scalar.vector.im/api/neb/services/hooks/dHJhdmlzLWNpLyU0MGxldmFucyUzQXNhZmFyYWRlZy5uZXQvJTIxRkt4aGprSUNwakJWelZlQ2RGJTNBc2FmYXJhZGVnLm5ldA" - on_success: change - on_failure: always - on_start: never + webhooks: + urls: + - "https://scalar.vector.im/api/neb/services/hooks/dHJhdmlzLWNpLyU0MGxldmFucyUzQXNhZmFyYWRlZy5uZXQvJTIxRkt4aGprSUNwakJWelZlQ2RGJTNBc2FmYXJhZGVnLm5ldA" + on_success: change + on_failure: always + on_start: never diff --git a/vagga.yaml b/vagga.yaml new file mode 100644 index 0000000..48e4845 --- /dev/null +++ b/vagga.yaml @@ -0,0 +1,77 @@ +minimum-vagga: v0.5.0 + +containers: + base: + auto-clean: true + setup: + - !UbuntuRelease { codename: artful } + - !UbuntuUniverse + - !Install [build-essential, wget, curl, pkg-config, file, openssl, sudo, ca-certificates, libssl-dev, cmake, libudev-dev, libgbm-dev, libxkbcommon-dev, libegl1-mesa-dev, libwayland-dev, libinput-dev] + + stable: + auto-clean: true + environ: + HOME: /work/.vagga/stable-home + PATH: /bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/work/.vagga/stable-home/.cargo/bin:/work/.vagga/stable-home/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/bin/ + RUST_BACKTRACE: 1 + XDG_RUNTIME_DIR: /work/.vagga/stable-home + setup: + - !Container base + - !Env HOME: /work/.vagga/stable-home + - !Sh curl https://sh.rustup.rs -sSf | sh -s -- -y --default-host x86_64-unknown-linux-gnu --default-toolchain stable --no-modify-path + + beta: + auto-clean: true + environ: + HOME: /work/.vagga/beta-home + PATH: /bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/work/.vagga/beta-home/.cargo/bin:/work/.vagga/beta-home/.rustup/toolchains/beta-x86_64-unknown-linux-gnu/bin/ + RUST_BACKTRACE: 1 + XDG_RUNTIME_DIR: /work/.vagga/beta-home + setup: + - !Container base + - !Env HOME: /work/.vagga/beta-home + - !Sh curl https://sh.rustup.rs -sSf | sh -s -- -y --default-host x86_64-unknown-linux-gnu --default-toolchain beta --no-modify-path + + nightly: + auto-clean: true + environ: + HOME: /work/.vagga/nightly-home + PATH: /bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/work/.vagga/nightly-home/.cargo/bin:/work/.vagga/nightly-home/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/ + RUST_BACKTRACE: 1 + XDG_RUNTIME_DIR: /work/.vagga/nightly-home + setup: + - !Container base + - !Env HOME: /work/.vagga/nightly-home + - !Sh curl https://sh.rustup.rs -sSf | sh -s -- -y --default-host x86_64-unknown-linux-gnu --default-toolchain nightly --no-modify-path + +commands: + update-stable: !Command + description: Update container + container: stable + run: rustup update + + update-beta: !Command + description: Update container + container: beta + run: rustup update + + update-nightly: !Command + description: Update container + container: nightly + run: | + rustup update + + cargo-stable: !Command + description: Run cargo + container: stable + run: ["/work/.vagga/stable-home/.cargo/bin/cargo"] + + cargo-beta: !Command + description: Run cargo + container: beta + run: ["/work/.vagga/beta-home/.cargo/bin/cargo"] + + cargo-nightly: !Command + description: Run cargo + container: nightly + run: ["/work/.vagga/nightly-home/.cargo/bin/cargo"] From 4207611e6de4417853ec82a5ebdc49bf4005a715 Mon Sep 17 00:00:00 2001 From: Drakulix Date: Fri, 15 Dec 2017 18:38:10 +0100 Subject: [PATCH 24/29] rustfmt code --- src/backend/drm/backend.rs | 76 +++++----- src/backend/drm/mod.rs | 121 +++++++++++----- src/backend/graphics/egl.rs | 44 +++--- src/backend/graphics/mod.rs | 5 +- src/backend/graphics/software.rs | 1 - src/backend/libinput.rs | 20 +-- src/backend/session/direct.rs | 147 +++++++++++-------- src/backend/session/mod.rs | 28 ++-- src/backend/udev.rs | 220 +++++++++++++++++++---------- src/backend/winit.rs | 32 +++-- src/wayland/compositor/handlers.rs | 22 +-- src/wayland/compositor/mod.rs | 57 ++++---- src/wayland/compositor/tree.rs | 35 ++--- src/wayland/output/mod.rs | 21 +-- src/wayland/seat/keyboard.rs | 16 +-- src/wayland/seat/mod.rs | 17 ++- src/wayland/shell/mod.rs | 88 ++++++------ src/wayland/shell/wl_handlers.rs | 56 ++++---- src/wayland/shell/xdg_handlers.rs | 134 +++++++++--------- src/wayland/shm/mod.rs | 6 +- src/wayland/shm/pool.rs | 2 - 21 files changed, 648 insertions(+), 500 deletions(-) diff --git a/src/backend/drm/backend.rs b/src/backend/drm/backend.rs index 4ff7533..f48feb2 100644 --- a/src/backend/drm/backend.rs +++ b/src/backend/drm/backend.rs @@ -60,7 +60,6 @@ rental! { } use self::graphics::{Graphics, Surface}; - /// Backend based on a `DrmDevice` and a given crtc pub struct DrmBackend { graphics: Graphics, @@ -71,9 +70,10 @@ pub struct DrmBackend { } impl DrmBackend { - pub(crate) fn new(context: Rc, crtc: crtc::Handle, mode: Mode, - connectors: Vec, logger: ::slog::Logger) - -> Result { + pub(crate) fn new( + context: Rc, crtc: crtc::Handle, mode: Mode, connectors: Vec, + logger: ::slog::Logger, + ) -> Result { // logger already initialized by the DrmDevice let log = ::slog_or_stdlog(logger); info!(log, "Initializing DrmBackend"); @@ -145,9 +145,7 @@ impl DrmBackend { &connectors, (0, 0), Some(mode), - ).chain_err( - || ErrorKind::DrmDev(format!("{:?}", context.devices.drm)), - )?; + ).chain_err(|| ErrorKind::DrmDev(format!("{:?}", context.devices.drm)))?; front_bo.set_userdata(fb); Ok(EGL { @@ -193,9 +191,7 @@ impl DrmBackend { /// Errors if the new connector does not support the currently set `Mode` pub fn add_connector(&mut self, connector: connector::Handle) -> Result<()> { let info = connector::Info::load_from_device(self.graphics.head().head().head(), connector) - .chain_err(|| { - ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head())) - })?; + .chain_err(|| ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head())))?; // check if the connector can handle the current mode if info.modes().contains(&self.mode) { @@ -203,9 +199,8 @@ impl DrmBackend { let encoders = info.encoders() .iter() .map(|encoder| { - encoder::Info::load_from_device(self.graphics.head().head().head(), *encoder).chain_err( - || ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head())), - ) + encoder::Info::load_from_device(self.graphics.head().head().head(), *encoder) + .chain_err(|| ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head()))) }) .collect::>>()?; @@ -215,9 +210,7 @@ impl DrmBackend { .head() .head() .resource_handles() - .chain_err(|| { - ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head())) - })?; + .chain_err(|| ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head())))?; if !encoders .iter() .map(|encoder| encoder.possible_crtcs()) @@ -272,9 +265,7 @@ impl DrmBackend { // check the connectors for connector in &self.connectors { if !connector::Info::load_from_device(self.graphics.head().head().head(), *connector) - .chain_err(|| { - ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head())) - })? + .chain_err(|| ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head())))? .modes() .contains(&mode) { @@ -295,9 +286,7 @@ impl DrmBackend { // resolution. debug!( logger_ref, - "Reinitializing surface for new mode: {}:{}", - w, - h + "Reinitializing surface for new mode: {}:{}", w, h ); graphics.gbm.surface = Surface::try_new( { @@ -340,9 +329,8 @@ impl DrmBackend { front_bo.format() ); // we need a framebuffer per front_buffer - let fb = framebuffer::create(graphics.context.devices.drm, &*front_bo).chain_err(|| { - ErrorKind::DrmDev(format!("{:?}", graphics.context.devices.drm)) - })?; + let fb = framebuffer::create(graphics.context.devices.drm, &*front_bo) + .chain_err(|| ErrorKind::DrmDev(format!("{:?}", graphics.context.devices.drm)))?; debug!(logger_ref, "Initialize screen"); crtc::set( @@ -352,9 +340,7 @@ impl DrmBackend { connectors_ref, (0, 0), Some(mode), - ).chain_err(|| { - ErrorKind::DrmDev(format!("{:?}", graphics.context.devices.drm)) - })?; + ).chain_err(|| ErrorKind::DrmDev(format!("{:?}", graphics.context.devices.drm)))?; front_bo.set_userdata(fb); Ok(EGL { @@ -424,13 +410,12 @@ impl GraphicsBackend for DrmBackend { self.graphics.head().head().head(), self.crtc, (x as i32, y as i32), - ).chain_err(|| { - ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head())) - }) + ).chain_err(|| ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head()))) } - fn set_cursor_representation(&self, buffer: &ImageBuffer, Vec>, hotspot: (u32, u32)) - -> Result<()> { + fn set_cursor_representation( + &self, buffer: &ImageBuffer, Vec>, hotspot: (u32, u32) + ) -> Result<()> { let (w, h) = buffer.dimensions(); debug!(self.logger, "Importing cursor"); @@ -463,9 +448,9 @@ impl GraphicsBackend for DrmBackend { (hotspot.0 as i32, hotspot.1 as i32), ).is_err() { - crtc::set_cursor(self.graphics.head().head().head(), self.crtc, &cursor).chain_err(|| { - ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head())) - })?; + crtc::set_cursor(self.graphics.head().head().head(), self.crtc, &cursor).chain_err( + || ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head())), + )?; } // and store it @@ -498,7 +483,10 @@ impl EGLGraphicsBackend for DrmBackend { // would most likely result in a lot of flickering. // neither weston, wlc or wlroots bother with that as well. // so we just assume we got at least two buffers to do flipping - let mut next_bo = surface.gbm.lock_front_buffer().expect("Surface only has one front buffer. Not supported by smithay"); + let mut next_bo = surface + .gbm + .lock_front_buffer() + .expect("Surface only has one front buffer. Not supported by smithay"); // create a framebuffer if the front buffer does not have one already // (they are reused by gbm) @@ -506,7 +494,8 @@ impl EGLGraphicsBackend for DrmBackend { let fb = if let Some(info) = maybe_fb { info } else { - let fb = framebuffer::create(graphics.context.devices.drm, &*next_bo).map_err(|_| SwapBuffersError::ContextLost)?; + let fb = framebuffer::create(graphics.context.devices.drm, &*next_bo) + .map_err(|_| SwapBuffersError::ContextLost)?; next_bo.set_userdata(fb); fb }; @@ -515,7 +504,12 @@ impl EGLGraphicsBackend for DrmBackend { trace!(self.logger, "Queueing Page flip"); // and flip - crtc::page_flip(graphics.context.devices.drm, self.crtc, fb.handle(), &[crtc::PageFlipFlags::PageFlipEvent]).map_err(|_| SwapBuffersError::ContextLost) + crtc::page_flip( + graphics.context.devices.drm, + self.crtc, + fb.handle(), + &[crtc::PageFlipFlags::PageFlipEvent], + ).map_err(|_| SwapBuffersError::ContextLost) }) }) } @@ -532,7 +526,9 @@ impl EGLGraphicsBackend for DrmBackend { } fn is_current(&self) -> bool { - self.graphics.rent_all(|graphics| graphics.context.egl.is_current() && graphics.gbm.surface.rent(|egl| egl.surface.is_current())) + self.graphics.rent_all(|graphics| { + graphics.context.egl.is_current() && graphics.gbm.surface.rent(|egl| egl.surface.is_current()) + }) } unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> { diff --git a/src/backend/drm/mod.rs b/src/backend/drm/mod.rs index c7aba96..bc321b6 100644 --- a/src/backend/drm/mod.rs +++ b/src/backend/drm/mod.rs @@ -188,9 +188,9 @@ //! ``` use backend::graphics::egl::{EGLContext, GlAttributes, PixelFormatRequirements}; +use backend::graphics::egl::EGLGraphicsBackend; #[cfg(feature = "backend_session")] use backend::session::SessionObserver; -use backend::graphics::egl::EGLGraphicsBackend; use drm::Device as BasicDevice; use drm::control::{connector, crtc, encoder, Mode, ResourceInfo}; use drm::control::Device as ControlDevice; @@ -204,11 +204,11 @@ use std::collections::HashMap; use std::fs::File; use std::hash::{Hash, Hasher}; use std::io::Result as IoResult; -use std::os::unix::io::{IntoRawFd, AsRawFd, RawFd}; -use std::rc::Rc; use std::mem; +use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd}; +use std::rc::Rc; use std::time::Duration; -use wayland_server::{EventLoopHandle, StateToken, StateProxy}; +use wayland_server::{EventLoopHandle, StateProxy, StateToken}; use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest}; mod backend; @@ -369,25 +369,21 @@ impl + Borrow + 'static> DrmDevice { let mut old_state = HashMap::new(); let res_handles = drm.resource_handles() - .chain_err(|| { - ErrorKind::DrmDev(format!("Loading drm resources on {:?}", drm)) - })?; + .chain_err(|| ErrorKind::DrmDev(format!("Loading drm resources on {:?}", drm)))?; for &con in res_handles.connectors() { let con_info = connector::Info::load_from_device(&drm, con) - .chain_err(|| { - ErrorKind::DrmDev(format!("Loading connector info on {:?}", drm)) - })?; + .chain_err(|| ErrorKind::DrmDev(format!("Loading connector info on {:?}", drm)))?; if let Some(enc) = con_info.current_encoder() { let enc_info = encoder::Info::load_from_device(&drm, enc) - .chain_err(|| { - ErrorKind::DrmDev(format!("Loading encoder info on {:?}", drm)) - })?; + .chain_err(|| ErrorKind::DrmDev(format!("Loading encoder info on {:?}", drm)))?; if let Some(crtc) = enc_info.current_crtc() { let info = crtc::Info::load_from_device(&drm, crtc) - .chain_err(|| { - ErrorKind::DrmDev(format!("Loading crtc info on {:?}", drm)) - })?; - old_state.entry(crtc).or_insert((info, Vec::new())).1.push(con); + .chain_err(|| ErrorKind::DrmDev(format!("Loading crtc info on {:?}", drm)))?; + old_state + .entry(crtc) + .or_insert((info, Vec::new())) + .1 + .push(con); } } } @@ -426,8 +422,9 @@ impl + Borrow + 'static> DrmDevice { /// /// Errors if initialization fails or the mode is not available on all given /// connectors. - pub fn create_backend<'a, I, S>(&mut self, state: S, crtc: crtc::Handle, mode: Mode, connectors: I) - -> Result<&StateToken> + pub fn create_backend<'a, I, S>( + &mut self, state: S, crtc: crtc::Handle, mode: Mode, connectors: I + ) -> Result<&StateToken> where I: Into>, S: Into>, @@ -447,7 +444,10 @@ impl + Borrow + 'static> DrmDevice { for connector in &connectors { let con_info = connector::Info::load_from_device(self.context.head().head(), *connector) .chain_err(|| { - ErrorKind::DrmDev(format!("Loading connector info on {:?}", self.context.head().head())) + ErrorKind::DrmDev(format!( + "Loading connector info on {:?}", + self.context.head().head() + )) })?; // check the mode @@ -461,21 +461,26 @@ impl + Borrow + 'static> DrmDevice { .iter() .map(|encoder| { encoder::Info::load_from_device(self.context.head().head(), *encoder).chain_err(|| { - ErrorKind::DrmDev(format!("Loading encoder info on {:?}", self.context.head().head())) + ErrorKind::DrmDev(format!( + "Loading encoder info on {:?}", + self.context.head().head() + )) }) }) .collect::>>()?; // and if any encoder supports the selected crtc let resource_handles = self.resource_handles().chain_err(|| { - ErrorKind::DrmDev(format!("Loading drm resources on {:?}", self.context.head().head())) + ErrorKind::DrmDev(format!( + "Loading drm resources on {:?}", + self.context.head().head() + )) })?; if !encoders .iter() .map(|encoder| encoder.possible_crtcs()) - .any(|crtc_list| { - resource_handles.filter_crtcs(crtc_list).contains(&crtc) - }) { + .any(|crtc_list| resource_handles.filter_crtcs(crtc_list).contains(&crtc)) + { bail!(ErrorKind::NoSuitableEncoder(con_info, crtc)) } } @@ -484,7 +489,8 @@ impl + Borrow + 'static> DrmDevice { let logger = self.logger.new(o!("crtc" => format!("{:?}", crtc))); let backend = DrmBackend::new(self.context.clone(), crtc, mode, connectors, logger)?; - self.backends.insert(crtc, state.into().insert(backend.into())); + self.backends + .insert(crtc, state.into().insert(backend.into())); Ok(self.backends.get(&crtc).unwrap()) } @@ -505,7 +511,7 @@ impl + Borrow + 'static> DrmDevice { /// Panics if the backend is already borrowed from the state pub fn destroy_backend<'a, S>(&mut self, state: S, crtc: &crtc::Handle) where - S: Into> + S: Into>, { if let Some(token) = self.backends.remove(crtc) { state.into().remove(token); @@ -540,12 +546,25 @@ impl + 'static> Drop for DrmDevice { panic!("Pending DrmBackends. Please 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.head().head(), handle, info.fb(), &connectors, info.position(), info.mode()) { - error!(self.logger, "Failed to reset crtc ({:?}). Error: {}", handle, err); + if let Err(err) = crtc::set( + self.context.head().head(), + handle, + info.fb(), + &connectors, + info.position(), + info.mode(), + ) { + error!( + self.logger, + "Failed to reset crtc ({:?}). Error: {}", handle, err + ); } } if let Err(err) = self.drop_master() { - error!(self.logger, "Failed to drop drm master state. Error: {}", err); + error!( + self.logger, + "Failed to drop drm master state. Error: {}", err + ); } } } @@ -569,8 +588,10 @@ pub trait DrmHandler + 'static> { /// ## Panics /// The device is already borrowed from the given `state`. Borrowing it again will panic /// and is not necessary as it is already provided via the `device` parameter. - fn ready<'a, S: Into>>(&mut self, state: S, device: &mut DrmDevice, backend: &StateToken, - crtc: crtc::Handle, frame: u32, duration: Duration); + fn ready<'a, S: Into>>( + &mut self, state: S, device: &mut DrmDevice, backend: &StateToken, crtc: crtc::Handle, + frame: u32, duration: Duration, + ); /// The `DrmDevice` has thrown an error. /// /// The related backends are most likely *not* usable anymore and @@ -585,8 +606,9 @@ pub trait DrmHandler + 'static> { /// 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: StateToken>, handler: H) - -> IoResult>, H)>> +pub fn drm_device_bind( + evlh: &mut EventLoopHandle, device: StateToken>, handler: H +) -> IoResult>, H)>> where B: From + Borrow + 'static, H: DrmHandler + 'static, @@ -624,13 +646,22 @@ where state.get(&backend_token).borrow().unlock_buffer(); trace!(logger, "Handling event for backend {:?}", event.crtc); // and then call the user to render the next frame - handler.ready(state, &mut dev, &backend_token, event.crtc, event.frame, event.duration); + handler.ready( + state, + &mut dev, + &backend_token, + event.crtc, + event.frame, + event.duration, + ); } } }); } }, - Err(err) => evlh.state().with_value(dev_token, |state, mut dev| handler.error(state, &mut dev, err)), + Err(err) => evlh.state().with_value(dev_token, |state, mut dev| { + handler.error(state, &mut dev, err) + }), }; }, error: |evlh, &mut (ref mut dev_token, ref mut handler), _, error| { @@ -648,7 +679,10 @@ impl + 'static> SessionObserver for StateToken = state.get_mut(self); device.active = false; if let Err(err) = device.drop_master() { - error!(device.logger, "Failed to drop drm master state. Error: {}", err); + error!( + device.logger, + "Failed to drop drm master state. Error: {}", err + ); } } @@ -656,13 +690,22 @@ impl + 'static> SessionObserver for StateToken { impl<'a> EGLContext<'a, ()> { /// Create a new context from a given `winit`-`Window` #[cfg(feature = "backend_winit")] - pub fn new_from_winit(window: &'a WinitWindow, attributes: GlAttributes, - reqs: PixelFormatRequirements, logger: L) - -> Result> + pub fn new_from_winit( + window: &'a WinitWindow, attributes: GlAttributes, reqs: PixelFormatRequirements, logger: L + ) -> Result> where L: Into>, { @@ -268,9 +268,9 @@ impl<'a> EGLContext<'a, ()> { /// Create a new context from a given `gbm::Device` #[cfg(feature = "backend_drm")] - pub fn new_from_gbm(gbm: &'a GbmDevice<'a>, attributes: GlAttributes, - reqs: PixelFormatRequirements, logger: L) - -> Result>> + pub fn new_from_gbm( + gbm: &'a GbmDevice<'a>, attributes: GlAttributes, reqs: PixelFormatRequirements, logger: L + ) -> Result>> where L: Into>, { @@ -288,9 +288,10 @@ impl<'a> EGLContext<'a, ()> { } impl<'a, T: NativeSurface> EGLContext<'a, T> { - unsafe fn new(native: NativeDisplayPtr, mut attributes: GlAttributes, reqs: PixelFormatRequirements, - log: ::slog::Logger) - -> Result> + unsafe fn new( + native: NativeDisplayPtr, mut attributes: GlAttributes, reqs: PixelFormatRequirements, + log: ::slog::Logger, + ) -> Result> where T: NativeSurface, { @@ -322,8 +323,7 @@ impl<'a, T: NativeSurface> EGLContext<'a, T> { Some(version) => { error!( log, - "OpenGLES {:?} is unknown and not supported by the EGL renderer backend", - version + "OpenGLES {:?} is unknown and not supported by the EGL renderer backend", version ); bail!(ErrorKind::OpenGlVersionNotSupported(version)); } @@ -423,9 +423,9 @@ impl<'a, T: NativeSurface> EGLContext<'a, T> { ) } - NativeDisplayPtr::X11(display) | - NativeDisplayPtr::Gbm(display) | - NativeDisplayPtr::Wayland(display) => { + NativeDisplayPtr::X11(display) + | NativeDisplayPtr::Gbm(display) + | NativeDisplayPtr::Wayland(display) => { trace!(log, "Default EGL Display Initialization via GetDisplay"); egl.GetDisplay(display as *mut _) } @@ -727,9 +727,9 @@ impl<'a, T: NativeSurface> EGLContext<'a, T> { self.display, self.config_id, match surface { - NativeSurfacePtr::X11(ptr) | - NativeSurfacePtr::Wayland(ptr) | - NativeSurfacePtr::Gbm(ptr) => ptr, + NativeSurfacePtr::X11(ptr) + | NativeSurfacePtr::Wayland(ptr) + | NativeSurfacePtr::Gbm(ptr) => ptr, }, self.surface_attributes.as_ptr(), ) @@ -850,8 +850,10 @@ impl<'context, 'surface, T: NativeSurface> EGLSurface<'context, 'surface, T> { /// Returns true if the OpenGL surface is the current one in the thread. pub fn is_current(&self) -> bool { - unsafe { self.context.egl.GetCurrentSurface(ffi::egl::DRAW as _) == self.surface as *const _ && - self.context.egl.GetCurrentSurface(ffi::egl::READ as _) == self.surface as *const _ } + unsafe { + self.context.egl.GetCurrentSurface(ffi::egl::DRAW as _) == self.surface as *const _ + && self.context.egl.GetCurrentSurface(ffi::egl::READ as _) == self.surface as *const _ + } } } @@ -905,10 +907,8 @@ impl error::Error for SwapBuffersError { 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" } + SwapBuffersError::Unknown(_) => "Unknown Open GL error occurred", } } diff --git a/src/backend/graphics/mod.rs b/src/backend/graphics/mod.rs index 2f0917e..05d276e 100644 --- a/src/backend/graphics/mod.rs +++ b/src/backend/graphics/mod.rs @@ -29,8 +29,9 @@ pub trait 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>; + fn set_cursor_representation( + &self, cursor: &Self::CursorFormat, hotspot: (u32, u32) + ) -> Result<(), Self::Error>; } pub mod software; diff --git a/src/backend/graphics/software.rs b/src/backend/graphics/software.rs index e053c77..2a96606 100644 --- a/src/backend/graphics/software.rs +++ b/src/backend/graphics/software.rs @@ -1,6 +1,5 @@ //! 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; diff --git a/src/backend/libinput.rs b/src/backend/libinput.rs index 4c27a3b..7c5475b 100644 --- a/src/backend/libinput.rs +++ b/src/backend/libinput.rs @@ -1,16 +1,16 @@ //! Implementation of input backend trait for types provided by `libinput` +use backend::input as backend; #[cfg(feature = "backend_session")] use backend::session::{AsErrno, Session, SessionObserver}; -use backend::input as backend; use input as libinput; use input::event; use std::collections::hash_map::{DefaultHasher, Entry, HashMap}; use std::hash::{Hash, Hasher}; use std::io::{Error as IoError, Result as IoResult}; -use std::rc::Rc; -use std::path::Path; use std::os::unix::io::RawFd; +use std::path::Path; +use std::rc::Rc; use wayland_server::{EventLoopHandle, StateProxy}; use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest}; @@ -583,7 +583,9 @@ impl From for LibinputSessionInterface { impl libinput::LibinputInterface for LibinputSessionInterface { fn open_restricted(&mut self, path: &Path, flags: i32) -> Result { use nix::fcntl::OFlag; - self.0.open(path, OFlag::from_bits_truncate(flags)).map_err(|err| err.as_errno().unwrap_or(1 /*Use EPERM by default*/)) + self.0 + .open(path, OFlag::from_bits_truncate(flags)) + .map_err(|err| err.as_errno().unwrap_or(1 /*Use EPERM by default*/)) } fn close_restricted(&mut self, fd: RawFd) { @@ -595,9 +597,9 @@ impl libinput::LibinputInterface for LibinputSessionInterface { /// /// Automatically feeds the backend with incoming events without any manual calls to /// `dispatch_new_events`. Should be used to achieve the smallest possible latency. -pub fn libinput_bind(backend: LibinputInputBackend, evlh: &mut EventLoopHandle) - -> IoResult> -{ +pub fn libinput_bind( + backend: LibinputInputBackend, evlh: &mut EventLoopHandle +) -> IoResult> { let fd = unsafe { backend.context.fd() }; evlh.add_fd_event_source( fd, @@ -610,13 +612,13 @@ pub fn libinput_bind(backend: LibinputInputBackend, evlh: &mut EventLoopHandle) fn fd_event_source_implementation() -> FdEventSourceImpl { FdEventSourceImpl { ready: |_evlh, ref mut backend, _, _| { - use ::backend::input::InputBackend; + use backend::input::InputBackend; if let Err(error) = backend.dispatch_new_events() { warn!(backend.logger, "Libinput errored: {}", error); } }, error: |_evlh, ref backend, _, error| { warn!(backend.logger, "Libinput fd errored: {}", error); - } + }, } } diff --git a/src/backend/session/direct.rs b/src/backend/session/direct.rs index 47b9a63..2b23d04 100644 --- a/src/backend/session/direct.rs +++ b/src/backend/session/direct.rs @@ -46,24 +46,22 @@ //! automatically by the `UdevBackend`, if not done manually). //! ``` -use std::io::Result as IoResult; -use std::path::Path; -use std::os::unix::io::RawFd; -use std::sync::Arc; -use std::sync::atomic::{AtomicBool, Ordering}; +use super::{AsErrno, Session, SessionNotifier, SessionObserver}; use nix::{Error as NixError, Result as NixResult}; use nix::fcntl::{self, open, OFlag}; use nix::libc::c_int; use nix::sys::signal::{self, Signal}; -use nix::sys::stat::{dev_t, major, minor, Mode, fstat}; -use nix::unistd::{dup, close}; -use wayland_server::EventLoopHandle; -use wayland_server::sources::SignalEventSource; - +use nix::sys::stat::{dev_t, fstat, major, minor, Mode}; +use nix::unistd::{close, dup}; +use std::io::Result as IoResult; +use std::os::unix::io::RawFd; +use std::path::Path; +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; #[cfg(feature = "backend_session_udev")] use udev::Context; - -use super::{AsErrno, Session, SessionNotifier, SessionObserver}; +use wayland_server::EventLoopHandle; +use wayland_server::sources::SignalEventSource; #[allow(dead_code)] mod tty { @@ -102,7 +100,7 @@ mod tty { pub const VT_PROCESS: i8 = 0x01; pub const VT_ACKACQ: i32 = 0x02; - extern { + extern "C" { pub fn __libc_current_sigrtmin() -> i8; pub fn __libc_current_sigrtmax() -> i8; } @@ -139,8 +137,8 @@ fn is_tty_device(dev: dev_t, path: Option<&Path>) -> bool { major(dev) == TTY_MAJOR }; res || minor(dev) != 0 - }, - None => major(dev) == TTY_MAJOR || minor(dev) != 0 + } + None => major(dev) == TTY_MAJOR || minor(dev) != 0, } } @@ -167,34 +165,36 @@ impl DirectSession { /// /// If you do not provide a tty device path, it will try to open the currently active tty if any. pub fn new(tty: Option<&Path>, logger: L) -> Result<(DirectSession, DirectSessionNotifier)> - where - L: Into> + where + L: Into>, { - let logger = ::slog_or_stdlog(logger).new(o!("smithay_module" => "backend_session", "session_type" => "direct/vt")); + let logger = ::slog_or_stdlog(logger) + .new(o!("smithay_module" => "backend_session", "session_type" => "direct/vt")); - let fd = tty - .map(|path| open(path, fcntl::O_RDWR | fcntl::O_CLOEXEC, Mode::empty()) - .chain_err(|| ErrorKind::FailedToOpenTTY(String::from(path.to_string_lossy())))) - .unwrap_or(dup(0 /*stdin*/).chain_err(|| ErrorKind::FailedToOpenTTY(String::from(""))))?; + let fd = tty.map(|path| { + open(path, fcntl::O_RDWR | fcntl::O_CLOEXEC, Mode::empty()) + .chain_err(|| ErrorKind::FailedToOpenTTY(String::from(path.to_string_lossy()))) + }).unwrap_or(dup(0 /*stdin*/).chain_err(|| ErrorKind::FailedToOpenTTY(String::from(""))))?; let active = Arc::new(AtomicBool::new(true)); match DirectSession::setup_tty(tty, fd, logger.clone()) { - Ok((vt, old_keyboard_mode, signal)) => { - Ok((DirectSession { + Ok((vt, old_keyboard_mode, signal)) => Ok(( + DirectSession { tty: fd, active: active.clone(), vt, old_keyboard_mode, logger: logger.new(o!("vt" => format!("{}", vt), "component" => "session")), - }, DirectSessionNotifier { + }, + DirectSessionNotifier { tty: fd, active, signals: Vec::new(), signal, - logger: logger.new(o!("vt" => format!("{}", vt), "component" => "session_notifier")) - })) - }, + logger: logger.new(o!("vt" => format!("{}", vt), "component" => "session_notifier")), + }, + )), Err(err) => { let _ = close(fd); Err(err) @@ -226,9 +226,11 @@ impl DirectSession { let mut old_keyboard_mode = 0; unsafe { - tty::kd_get_kb_mode(tty, &mut old_keyboard_mode).chain_err(|| ErrorKind::FailedToSaveTTYState(vt_num))?; + tty::kd_get_kb_mode(tty, &mut old_keyboard_mode) + .chain_err(|| ErrorKind::FailedToSaveTTYState(vt_num))?; tty::kd_set_kb_mode(tty, tty::K_OFF).chain_err(|| ErrorKind::FailedToSetTTYKbMode(vt_num))?; - tty::kd_set_mode(tty, tty::KD_GRAPHICS as i32).chain_err(|| ErrorKind::FailedToSetTTYMode(vt_num))?; + tty::kd_set_mode(tty, tty::KD_GRAPHICS as i32) + .chain_err(|| ErrorKind::FailedToSetTTYMode(vt_num))?; } // TODO: Support realtime signals @@ -305,19 +307,33 @@ impl Drop for DirectSession { info!(self.logger, "Deallocating tty {}", self.tty); if let Err(err) = unsafe { tty::kd_set_kb_mode(self.tty, self.old_keyboard_mode) } { - warn!(self.logger, "Unable to restore vt keyboard mode. Error: {}", err); + warn!( + self.logger, + "Unable to restore vt keyboard mode. Error: {}", err + ); } if let Err(err) = unsafe { tty::kd_set_mode(self.tty, tty::KD_TEXT as i32) } { - warn!(self.logger, "Unable to restore vt text mode. Error: {}", err); + warn!( + self.logger, + "Unable to restore vt text mode. Error: {}", err + ); } - if let Err(err) = unsafe { tty::vt_set_mode(self.tty, &tty::VtMode { - mode: tty::VT_AUTO, - ..Default::default() - }) } { + if let Err(err) = unsafe { + tty::vt_set_mode( + self.tty, + &tty::VtMode { + mode: tty::VT_AUTO, + ..Default::default() + }, + ) + } { error!(self.logger, "Failed to reset vt handling. Error: {}", err); } if let Err(err) = close(self.tty) { - error!(self.logger, "Failed to close tty file descriptor. Error: {}", err); + error!( + self.logger, + "Failed to close tty file descriptor. Error: {}", err + ); } } } @@ -350,36 +366,45 @@ impl SessionNotifier for DirectSessionNotifier { /// Allows the `DirectSessionNotifier` to listen for the incoming signals signalling the session state. /// If you don't use this function `DirectSessionNotifier` will not correctly tell you the current /// session state. -pub fn direct_session_bind(notifier: DirectSessionNotifier, evlh: &mut EventLoopHandle, _logger: L) - -> IoResult> +pub fn direct_session_bind( + notifier: DirectSessionNotifier, evlh: &mut EventLoopHandle, _logger: L +) -> IoResult> where L: Into>, { let signal = notifier.signal; - evlh.add_signal_event_source(|evlh, notifier, _| { - if notifier.is_active() { - info!(notifier.logger, "Session shall become inactive"); - for signal in &mut notifier.signals { - if let &mut Some(ref mut signal) = signal {signal.pause(&mut evlh.state().as_proxy()); } + evlh.add_signal_event_source( + |evlh, notifier, _| { + if notifier.is_active() { + info!(notifier.logger, "Session shall become inactive"); + for signal in &mut notifier.signals { + if let &mut Some(ref mut signal) = signal { + signal.pause(&mut evlh.state().as_proxy()); + } + } + notifier.active.store(false, Ordering::SeqCst); + unsafe { + tty::vt_rel_disp(notifier.tty, 1).expect("Unable to release tty lock"); + } + debug!(notifier.logger, "Session is now inactive"); + } else { + debug!(notifier.logger, "Session will become active again"); + unsafe { + tty::vt_rel_disp(notifier.tty, tty::VT_ACKACQ).expect("Unable to acquire tty lock"); + } + for signal in &mut notifier.signals { + if let &mut Some(ref mut signal) = signal { + signal.activate(&mut evlh.state().as_proxy()); + } + } + notifier.active.store(true, Ordering::SeqCst); + info!(notifier.logger, "Session is now active again"); } - notifier.active.store(false, Ordering::SeqCst); - unsafe { - tty::vt_rel_disp(notifier.tty, 1).expect("Unable to release tty lock"); - } - debug!(notifier.logger, "Session is now inactive"); - } else { - debug!(notifier.logger, "Session will become active again"); - unsafe { - tty::vt_rel_disp(notifier.tty, tty::VT_ACKACQ).expect("Unable to acquire tty lock"); - } - for signal in &mut notifier.signals { - if let &mut Some(ref mut signal) = signal { signal.activate(&mut evlh.state().as_proxy()); } - } - notifier.active.store(true, Ordering::SeqCst); - info!(notifier.logger, "Session is now active again"); - } - }, notifier, signal) + }, + notifier, + signal, + ) } error_chain! { diff --git a/src/backend/session/mod.rs b/src/backend/session/mod.rs index 74a966f..0d16259 100644 --- a/src/backend/session/mod.rs +++ b/src/backend/session/mod.rs @@ -10,12 +10,12 @@ //! The following mechanisms are currently provided: //! - direct - legacy tty / virtual terminal kernel api //! -use std::path::Path; -use std::sync::{Arc, Mutex}; -use std::rc::Rc; +use nix::fcntl::OFlag; use std::cell::RefCell; use std::os::unix::io::RawFd; -use nix::fcntl::OFlag; +use std::path::Path; +use std::rc::Rc; +use std::sync::{Arc, Mutex}; use wayland_server::StateProxy; /// General session interface. @@ -83,13 +83,23 @@ pub trait SessionObserver { impl Session for () { type Error = (); - fn open(&mut self, _path: &Path, _flags: OFlag) -> Result { Err(()) } - fn close(&mut self, _fd: RawFd) -> Result<(), Self::Error> { Err(()) } + fn open(&mut self, _path: &Path, _flags: OFlag) -> Result { + Err(()) + } + fn close(&mut self, _fd: RawFd) -> Result<(), Self::Error> { + Err(()) + } - fn change_vt(&mut self, _vt: i32) -> Result<(), Self::Error> { Err(()) } + fn change_vt(&mut self, _vt: i32) -> Result<(), Self::Error> { + Err(()) + } - fn is_active(&self) -> bool { false } - fn seat(&self) -> String { String::from("seat0") } + fn is_active(&self) -> bool { + false + } + fn seat(&self) -> String { + String::from("seat0") + } } impl Session for Rc> { diff --git a/src/backend/udev.rs b/src/backend/udev.rs index 66fb0e3..a04b670 100644 --- a/src/backend/udev.rs +++ b/src/backend/udev.rs @@ -9,36 +9,51 @@ //! See also `examples/udev.rs` for pure hardware backed example of a compositor utilizing this //! backend. -use udev::{Context, MonitorBuilder, MonitorSocket, Event, EventType, Enumerator, Result as UdevResult}; +use backend::drm::{drm_device_bind, DrmBackend, DrmDevice, DrmHandler}; +use backend::session::{Session, SessionObserver}; use nix::fcntl; use nix::sys::stat::{dev_t, fstat}; use std::borrow::Borrow; use std::collections::HashMap; -use std::io::{Error as IoError, Result as IoResult}; use std::ffi::OsString; +use std::io::{Error as IoError, Result as IoResult}; use std::mem::drop; -use std::path::{PathBuf, Path}; use std::os::unix::io::AsRawFd; -use wayland_server::{EventLoopHandle, StateToken, StateProxy}; +use std::path::{Path, PathBuf}; +use udev::{Context, Enumerator, Event, EventType, MonitorBuilder, MonitorSocket, Result as UdevResult}; +use wayland_server::{EventLoopHandle, StateProxy, StateToken}; use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest}; -use ::backend::drm::{DrmDevice, DrmBackend, DrmHandler, drm_device_bind}; -use ::backend::session::{Session, SessionObserver}; - /// Graphical backend that monitors 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 /// attached monitors. -pub struct UdevBackend + 'static, H: DrmHandler + 'static, S: Session + 'static, T: UdevHandler + 'static> { - devices: HashMap>, FdEventSource<(StateToken>, H)>)>, +pub struct UdevBackend< + B: Borrow + 'static, + H: DrmHandler + 'static, + S: Session + 'static, + T: UdevHandler + 'static, +> { + devices: HashMap< + dev_t, + ( + StateToken>, + FdEventSource<(StateToken>, H)>, + ), + >, monitor: MonitorSocket, session: S, handler: T, logger: ::slog::Logger, } -impl + Borrow + 'static, H: DrmHandler + 'static, S: Session + 'static, T: UdevHandler + 'static> UdevBackend { +impl< + B: From + Borrow + 'static, + H: DrmHandler + 'static, + S: Session + 'static, + T: UdevHandler + 'static, +> UdevBackend { /// Creates a new `UdevBackend` and adds it to the given `EventLoop`'s state. /// /// ## Arguments @@ -47,14 +62,11 @@ impl + Borrow + 'static, H: DrmHandler + 'sta /// `session` - A session used to open and close devices as they become available /// `handler` - User-provided handler to respond to any detected changes /// `logger` - slog Logger to be used by the backend and its `DrmDevices`. - pub fn new<'a, L>(mut evlh: &mut EventLoopHandle, - context: &Context, - mut session: S, - mut handler: T, - logger: L) - -> Result>> + pub fn new<'a, L>( + mut evlh: &mut EventLoopHandle, context: &Context, mut session: S, mut handler: T, logger: L + ) -> Result>> where - L: Into> + L: Into>, { let logger = ::slog_or_stdlog(logger).new(o!("smithay_module" => "backend_udev")); let seat = session.seat(); @@ -125,8 +137,12 @@ impl + Borrow + 'static, H: DrmHandler + 'sta .collect::>, FdEventSource<(StateToken>, H)>)>>(); 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)?; + builder + .match_subsystem("drm") + .chain_err(|| ErrorKind::FailedToInitMonitor)?; + let monitor = builder + .listen() + .chain_err(|| ErrorKind::FailedToInitMonitor)?; Ok(evlh.state().insert(UdevBackend { devices, @@ -154,14 +170,22 @@ impl + Borrow + 'static, H: DrmHandler + 'sta 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); + warn!( + self.logger, + "Failed to close device. Error: {:?}. Ignoring", err + ); }; } info!(self.logger, "All devices closed"); } } -impl + 'static, H: DrmHandler + 'static, S: Session + 'static, T: UdevHandler + 'static> SessionObserver for StateToken> { +impl< + B: Borrow + 'static, + H: DrmHandler + 'static, + S: Session + 'static, + T: UdevHandler + 'static, +> SessionObserver for StateToken> { fn pause<'a>(&mut self, state: &mut StateProxy<'a>) { state.with_value(self, |state, udev| { for &mut (ref mut device, _) in udev.devices.values_mut() { @@ -183,8 +207,9 @@ impl + 'static, H: DrmHandler + 'static, S: Session + ' /// /// Allows the backend to recieve kernel events and thus to drive the `UdevHandler`. /// No runtime functionality can be provided without using this function. -pub fn udev_backend_bind(evlh: &mut EventLoopHandle, udev: StateToken>) - -> IoResult>>> +pub fn udev_backend_bind( + evlh: &mut EventLoopHandle, udev: StateToken> +) -> IoResult>>> where B: From + Borrow + 'static, H: DrmHandler + 'static, @@ -192,16 +217,10 @@ where S: Session + 'static, { let fd = evlh.state().get(&udev).monitor.as_raw_fd(); - evlh.add_fd_event_source( - fd, - fd_event_source_implementation(), - udev, - FdInterest::READ, - ) + evlh.add_fd_event_source(fd, fd_event_source_implementation(), udev, FdInterest::READ) } -fn fd_event_source_implementation() - -> FdEventSourceImpl>> +fn fd_event_source_implementation() -> FdEventSourceImpl>> where B: From + Borrow + 'static, H: DrmHandler + 'static, @@ -210,7 +229,11 @@ where { FdEventSourceImpl { ready: |mut evlh, token, _, _| { - let events = evlh.state().get(token).monitor.clone().collect::>(); + let events = evlh.state() + .get(token) + .monitor + .clone() + .collect::>(); for event in events { match event.event_type() { // New device @@ -218,30 +241,49 @@ where info!(evlh.state().get(token).logger, "Device Added"); if let (Some(path), Some(devnum)) = (event.devnode(), event.devnum()) { let mut device = { - match unsafe { DrmDevice::new_from_fd( - { - let logger = evlh.state().get(token).logger.clone(); - match evlh.state().get_mut(token).session.open(path, fcntl::O_RDWR | fcntl::O_CLOEXEC | fcntl::O_NOCTTY | fcntl::O_NONBLOCK) { - Ok(fd) => fd, - Err(err) => { - warn!(logger, "Unable to open drm device {:?}, Error: {:?}. Skipping", path, err); - continue; + match unsafe { + DrmDevice::new_from_fd( + { + let logger = evlh.state().get(token).logger.clone(); + match evlh.state().get_mut(token).session.open( + path, + fcntl::O_RDWR | fcntl::O_CLOEXEC | fcntl::O_NOCTTY + | fcntl::O_NONBLOCK, + ) { + Ok(fd) => fd, + Err(err) => { + warn!(logger, "Unable to open drm device {:?}, Error: {:?}. Skipping", path, err); + continue; + } } - } - }, evlh.state().get(token).logger.clone() - ) } { + }, + evlh.state().get(token).logger.clone(), + ) + } { Ok(dev) => dev, Err(err) => { - warn!(evlh.state().get(token).logger, "Failed to initialize device {:?}. Error: {}. Skipping", path, err); + warn!( + evlh.state().get(token).logger, + "Failed to initialize device {:?}. Error: {}. Skipping", + path, + err + ); continue; } } }; - match evlh.state().with_value(token, |state, udev| udev.handler.device_added(state, &mut device)) { + match evlh.state().with_value(token, |state, udev| { + udev.handler.device_added(state, &mut device) + }) { Some(drm_handler) => { let dev_token = evlh.state().insert(device); - if let Ok(fd_event_source) = drm_device_bind(&mut evlh, dev_token.clone(), drm_handler) { - evlh.state().get_mut(token).devices.insert(devnum, (dev_token, fd_event_source)); + if let Ok(fd_event_source) = + drm_device_bind(&mut evlh, dev_token.clone(), drm_handler) + { + evlh.state() + .get_mut(token) + .devices + .insert(devnum, (dev_token, fd_event_source)); } else { evlh.state().with_value(token, |state, udev| { let mut state: StateProxy = state.into(); @@ -250,42 +292,52 @@ where let fd = device.as_raw_fd(); drop(device); if let Err(err) = udev.session.close(fd) { - warn!(udev.logger, "Failed to close dropped device. Error: {:?}. Ignoring", err); + warn!( + udev.logger, + "Failed to close dropped device. Error: {:?}. Ignoring", + err + ); }; }) } - }, + } None => { let fd = device.as_raw_fd(); drop(device); evlh.state().with_value(token, |_state, udev| { if let Err(err) = udev.session.close(fd) { - warn!(udev.logger, "Failed to close unused device. Error: {:?}", err); + warn!( + udev.logger, + "Failed to close unused device. Error: {:?}", err + ); } }) - }, + } }; } - }, + } // Device removed - EventType::Remove => { - evlh.state().with_value(token, |state, udev| { - info!(udev.logger, "Device Remove"); - if let Some(devnum) = event.devnum() { - if let Some((device, fd_event_source)) = udev.devices.remove(&devnum) { - fd_event_source.remove(); - let mut state: StateProxy = state.into(); - udev.handler.device_removed(&mut state, &device); - let device = state.remove(device); - let fd = device.as_raw_fd(); - drop(device); - if let Err(err) = udev.session.close(fd) { - warn!(udev.logger, "Failed to close device {:?}. Error: {:?}. Ignoring", event.sysname(), err); - }; - } + EventType::Remove => evlh.state().with_value(token, |state, udev| { + info!(udev.logger, "Device Remove"); + if let Some(devnum) = event.devnum() { + if let Some((device, fd_event_source)) = udev.devices.remove(&devnum) { + fd_event_source.remove(); + let mut state: StateProxy = state.into(); + udev.handler.device_removed(&mut state, &device); + let device = state.remove(device); + let fd = device.as_raw_fd(); + drop(device); + if let Err(err) = udev.session.close(fd) { + warn!( + udev.logger, + "Failed to close device {:?}. Error: {:?}. Ignoring", + event.sysname(), + err + ); + }; } - }) - }, + } + }), // New connector EventType::Change => evlh.state().with_value(token, |state, udev| { info!(udev.logger, "Device Changed"); @@ -301,25 +353,28 @@ where info!(udev.logger, "changed, but no devnum"); } }), - _ => {}, + _ => {} } } }, error: |evlh, token, _, err| { - evlh.state().with_value(token, |state, udev| udev.handler.error(state, err)) + evlh.state() + .with_value(token, |state, udev| udev.handler.error(state, err)) }, } } /// Handler for the `UdevBackend`, allows to open, close and update drm devices as they change during runtime. -pub trait UdevHandler + 'static, H: DrmHandler + 'static> { +pub trait UdevHandler + 'static, H: DrmHandler + '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<'a, S: Into>>(&mut self, state: S, device: &mut DrmDevice) -> Option; + fn device_added<'a, S: Into>>(&mut self, state: S, device: &mut DrmDevice) + -> Option; /// Called when an open device is changed. /// /// This usually indicates that some connectors did become available or were unplugged. The handler @@ -353,7 +408,11 @@ pub fn primary_gpu>(context: &Context, seat: S) -> UdevResult>(context: &Context, seat: S) -> UdevResult(builder: WindowBuilder, logger: L) - -> Result<(WinitGraphicsBackend, WinitInputBackend)> +pub fn init_from_builder( + builder: WindowBuilder, logger: L +) -> Result<(WinitGraphicsBackend, WinitInputBackend)> where L: Into>, { @@ -120,8 +121,9 @@ where /// graphics backend trait, from a given `WindowBuilder` struct, as well as given /// `GlAttributes` for further customization of the rendering pipeline and a /// corresponding `WinitInputBackend`, which implements the `InputBackend` trait. -pub fn init_from_builder_with_gl_attr(builder: WindowBuilder, attributes: GlAttributes, logger: L) - -> Result<(WinitGraphicsBackend, WinitInputBackend)> +pub fn init_from_builder_with_gl_attr( + builder: WindowBuilder, attributes: GlAttributes, logger: L +) -> Result<(WinitGraphicsBackend, WinitInputBackend)> where L: Into>, { @@ -190,8 +192,9 @@ impl GraphicsBackend for WinitGraphicsBackend { self.window.head().set_cursor_position(x as i32, y as i32) } - fn set_cursor_representation(&self, cursor: &Self::CursorFormat, _hotspot: (u32, u32)) - -> ::std::result::Result<(), ()> { + fn set_cursor_representation( + &self, cursor: &Self::CursorFormat, _hotspot: (u32, u32) + ) -> ::std::result::Result<(), ()> { // Cannot log this one, as `CursorFormat` is not `Debug` and should not be debug!(self.logger, "Changing cursor representation"); self.window.head().set_cursor(*cursor); @@ -228,7 +231,8 @@ impl EGLGraphicsBackend for WinitGraphicsBackend { } fn is_current(&self) -> bool { - self.window.rent(|egl| egl.rent_all(|egl| egl.context.is_current() && egl.surface.is_current())) + self.window + .rent(|egl| egl.rent_all(|egl| egl.context.is_current() && egl.surface.is_current())) } unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> { @@ -371,10 +375,10 @@ impl PointerAxisEvent for WinitMouseWheelEvent { fn amount(&self) -> f64 { match (self.axis, self.delta) { - (Axis::Horizontal, MouseScrollDelta::LineDelta(x, _)) | - (Axis::Horizontal, MouseScrollDelta::PixelDelta(x, _)) => x as f64, - (Axis::Vertical, MouseScrollDelta::LineDelta(_, y)) | - (Axis::Vertical, MouseScrollDelta::PixelDelta(_, y)) => y as f64, + (Axis::Horizontal, MouseScrollDelta::LineDelta(x, _)) + | (Axis::Horizontal, MouseScrollDelta::PixelDelta(x, _)) => x as f64, + (Axis::Vertical, MouseScrollDelta::LineDelta(_, y)) + | (Axis::Vertical, MouseScrollDelta::PixelDelta(_, y)) => y as f64, } } } @@ -624,8 +628,10 @@ impl InputBackend for WinitInputBackend { trace!(logger, "Resizing window to {:?}", (x, y)); window.head().set_inner_size(x, y); window.rent(|egl| { - egl.rent(|surface| if let Some(wegl_surface) = (**surface).as_ref() { - wegl_surface.resize(x as i32, y as i32, 0, 0) + egl.rent(|surface| { + if let Some(wegl_surface) = (**surface).as_ref() { + wegl_surface.resize(x as i32, y as i32, 0, 0) + } }) }); } diff --git a/src/wayland/compositor/handlers.rs b/src/wayland/compositor/handlers.rs index 7cf4df6..da2e760 100644 --- a/src/wayland/compositor/handlers.rs +++ b/src/wayland/compositor/handlers.rs @@ -11,9 +11,10 @@ use wayland_server::protocol::{wl_compositor, wl_region, wl_subcompositor, wl_su * wl_compositor */ -pub(crate) fn compositor_bind(evlh: &mut EventLoopHandle, idata: &mut SurfaceIData, - _: &Client, compositor: wl_compositor::WlCompositor) -where +pub(crate) fn compositor_bind( + evlh: &mut EventLoopHandle, idata: &mut SurfaceIData, _: &Client, + compositor: wl_compositor::WlCompositor, +) where U: Default + 'static, R: Default + 'static, ID: 'static, @@ -65,8 +66,9 @@ pub struct SurfaceIData { } impl SurfaceIData { - pub(crate) fn make(log: ::slog::Logger, implem: SurfaceUserImplementation, idata: ID) - -> SurfaceIData { + pub(crate) fn make( + log: ::slog::Logger, implem: SurfaceUserImplementation, idata: ID + ) -> SurfaceIData { SurfaceIData { log: log, implem: implem, @@ -86,9 +88,7 @@ impl Clone for SurfaceIData { } pub(crate) fn surface_implementation( - ) - -> wl_surface::Implementation> -{ +) -> wl_surface::Implementation> { wl_surface::Implementation { attach: |_, _, _, surface, buffer, x, y| unsafe { SurfaceData::::with_data(surface, |d| { @@ -199,9 +199,9 @@ fn destroy_region(region: &wl_region::WlRegion) { * wl_subcompositor */ -pub(crate) fn subcompositor_bind(evlh: &mut EventLoopHandle, _: &mut (), _: &Client, - subcompositor: wl_subcompositor::WlSubcompositor) -where +pub(crate) fn subcompositor_bind( + evlh: &mut EventLoopHandle, _: &mut (), _: &Client, subcompositor: wl_subcompositor::WlSubcompositor +) where R: RoleType + Role + 'static, U: 'static, { diff --git a/src/wayland/compositor/mod.rs b/src/wayland/compositor/mod.rs index 6cdc9b1..2265d6c 100644 --- a/src/wayland/compositor/mod.rs +++ b/src/wayland/compositor/mod.rs @@ -314,11 +314,11 @@ where /// /// If the surface not managed by the CompositorGlobal that provided this token, this /// will panic (having more than one compositor is not supported). - pub fn with_surface_tree_upward(&self, surface: &wl_surface::WlSurface, initial: T, f: F) - -> Result<(), ()> + pub fn with_surface_tree_upward( + &self, surface: &wl_surface::WlSurface, initial: T, f: F + ) -> Result<(), ()> where - F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes, &mut R, &T) - -> TraversalAction, + F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes, &mut R, &T) -> TraversalAction, { assert!( resource_is_registered( @@ -340,11 +340,11 @@ where /// supposed to be drawn: top-most first. /// /// Behavior is the same as `with_surface_tree_upward`. - pub fn with_surface_tree_downward(&self, surface: &wl_surface::WlSurface, initial: T, f: F) - -> Result<(), ()> + pub fn with_surface_tree_downward( + &self, surface: &wl_surface::WlSurface, initial: T, f: F + ) -> Result<(), ()> where - F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes, &mut R, &T) - -> TraversalAction, + F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes, &mut R, &T) -> TraversalAction, { assert!( resource_is_registered( @@ -426,7 +426,6 @@ impl CompositorToken { unsafe { SurfaceData::::has_role::(surface) } } - /// Register that this surface has given role with default data /// /// Fails if the surface already has a role. @@ -454,8 +453,9 @@ impl CompositorToken { /// /// If the surface is not managed by the CompositorGlobal that provided this token, this /// will panic (having more than one compositor is not supported). - pub fn give_role_with(&self, surface: &wl_surface::WlSurface, data: RoleData) - -> Result<(), RoleData> + pub fn give_role_with( + &self, surface: &wl_surface::WlSurface, data: RoleData + ) -> Result<(), RoleData> where R: Role, { @@ -475,8 +475,9 @@ impl CompositorToken { /// /// If the surface is not managed by the CompositorGlobal that provided this token, this /// will panic (having more than one compositor is not supported). - pub fn with_role_data(&self, surface: &wl_surface::WlSurface, f: F) - -> Result + pub fn with_role_data( + &self, surface: &wl_surface::WlSurface, f: F + ) -> Result where R: Role, F: FnOnce(&mut RoleData) -> T, @@ -533,12 +534,12 @@ impl CompositorToken { /// It also returns the two global handles, in case you whish to remove these /// globals from the event loop in the future. pub fn compositor_init( - evl: &mut EventLoop, implem: SurfaceUserImplementation, idata: ID, logger: L) - -> ( - CompositorToken, - Global>, - Global, - ) + evl: &mut EventLoop, implem: SurfaceUserImplementation, idata: ID, logger: L +) -> ( + CompositorToken, + Global>, + Global, +) where L: Into>, U: Default + 'static, @@ -580,10 +581,10 @@ pub struct SurfaceUserImplementation { /// See [`wayland_server::protocol::wl_surface::Implementation::commit`](https://docs.rs/wayland-server/0.10.1/wayland_server/protocol/wl_surface/struct.Implementation.html#structfield.commit) /// for more details pub commit: fn( - evlh: &mut EventLoopHandle, - idata: &mut ID, - surface: &wl_surface::WlSurface, - token: CompositorToken, + evlh: &mut EventLoopHandle, + idata: &mut ID, + surface: &wl_surface::WlSurface, + token: CompositorToken, ), /// The client asks to be notified when would be a good time to update the contents of this surface /// @@ -593,11 +594,11 @@ pub struct SurfaceUserImplementation { /// See [`wayland_server::protocol::wl_surface::Implementation::frame`](https://docs.rs/wayland-server/0.10.1/wayland_server/protocol/wl_surface/struct.Implementation.html#structfield.frame) /// for more details pub frame: fn( - evlh: &mut EventLoopHandle, - idata: &mut ID, - surface: &wl_surface::WlSurface, - callback: wl_callback::WlCallback, - token: CompositorToken, + evlh: &mut EventLoopHandle, + idata: &mut ID, + surface: &wl_surface::WlSurface, + callback: wl_callback::WlCallback, + token: CompositorToken, ), } diff --git a/src/wayland/compositor/tree.rs b/src/wayland/compositor/tree.rs index 7cbecc9..10fd2a1 100644 --- a/src/wayland/compositor/tree.rs +++ b/src/wayland/compositor/tree.rs @@ -132,8 +132,9 @@ impl SurfaceData { /// Register that this surface has a role with given data /// /// Fails if it already has one and returns the data - pub unsafe fn give_role_with(surface: &wl_surface::WlSurface, data: RoleData) - -> Result<(), RoleData> + pub unsafe fn give_role_with( + surface: &wl_surface::WlSurface, data: RoleData + ) -> Result<(), RoleData> where R: Role, { @@ -158,8 +159,9 @@ impl SurfaceData { } /// Access to the role data - pub unsafe fn with_role_data(surface: &wl_surface::WlSurface, f: F) - -> Result + pub unsafe fn with_role_data( + surface: &wl_surface::WlSurface, f: F + ) -> Result where R: Role, F: FnOnce(&mut RoleData) -> T, @@ -177,8 +179,9 @@ impl + 'static> SurfaceData /// /// if this surface already has a role, does nothing and fails, otherwise /// its role is now to be a subsurface - pub unsafe fn set_parent(child: &wl_surface::WlSurface, parent: &wl_surface::WlSurface) - -> Result<(), ()> { + pub unsafe fn set_parent( + child: &wl_surface::WlSurface, parent: &wl_surface::WlSurface + ) -> Result<(), ()> { debug_assert!(child.status() == Liveness::Alive); debug_assert!(parent.status() == Liveness::Alive); @@ -246,9 +249,9 @@ impl + 'static> SurfaceData /// Reorders a surface relative to one of its sibling /// /// Fails if `relative_to` is not a sibling or parent of `surface`. - pub unsafe fn reorder(surface: &wl_surface::WlSurface, to: Location, - relative_to: &wl_surface::WlSurface) - -> Result<(), ()> { + pub unsafe fn reorder( + surface: &wl_surface::WlSurface, to: Location, relative_to: &wl_surface::WlSurface + ) -> Result<(), ()> { let parent = { let data_mutex = Self::get_data(surface); let data_guard = data_mutex.lock().unwrap(); @@ -316,17 +319,15 @@ impl SurfaceData { /// false will cause an early-stopping. pub unsafe fn map_tree(root: &wl_surface::WlSurface, initial: T, mut f: F, reverse: bool) where - F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes, &mut R, &T) - -> TraversalAction, + F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes, &mut R, &T) -> TraversalAction, { // helper function for recursion - unsafe fn map(surface: &wl_surface::WlSurface, - root: &wl_surface::WlSurface, initial: &T, f: &mut F, - reverse: bool) - -> bool + unsafe fn map( + surface: &wl_surface::WlSurface, root: &wl_surface::WlSurface, initial: &T, f: &mut F, + reverse: bool, + ) -> bool where - F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes, &mut R, &T) - -> TraversalAction, + F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes, &mut R, &T) -> TraversalAction, { // stop if we met the root, so to not deadlock/inifinte loop if surface.equals(root) { diff --git a/src/wayland/output/mod.rs b/src/wayland/output/mod.rs index 3caf46a..9404d64 100644 --- a/src/wayland/output/mod.rs +++ b/src/wayland/output/mod.rs @@ -113,11 +113,11 @@ impl Output { /// returns the state token allowing you to access it, as well as the global handle, /// in case you whish to remove this global in the future. pub fn new( - evl: &mut EventLoop, name: String, physical: PhysicalProperties, logger: L) - -> ( - StateToken, - Global>, - ) + evl: &mut EventLoop, name: String, physical: PhysicalProperties, logger: L + ) -> ( + StateToken, + Global>, + ) where L: Into>, { @@ -229,8 +229,10 @@ impl Output { /// internal list. /// /// By default, transform status is `Normal`, and scale is `1`. - pub fn change_current_state(&mut self, new_mode: Option, - new_transform: Option, new_scale: Option) { + pub fn change_current_state( + &mut self, new_mode: Option, new_transform: Option, + new_scale: Option, + ) { if let Some(mode) = new_mode { if self.modes.iter().find(|&m| *m == mode).is_none() { self.modes.push(mode); @@ -283,8 +285,9 @@ impl Output { } } -fn output_bind(evlh: &mut EventLoopHandle, token: &mut StateToken, _: &Client, - global: wl_output::WlOutput) { +fn output_bind( + evlh: &mut EventLoopHandle, token: &mut StateToken, _: &Client, global: wl_output::WlOutput +) { evlh.register(&global, output_implementation(), token.clone(), None); evlh.state().get_mut(token).new_global(global); } diff --git a/src/wayland/seat/keyboard.rs b/src/wayland/seat/keyboard.rs index 4d23023..0a9c309 100644 --- a/src/wayland/seat/keyboard.rs +++ b/src/wayland/seat/keyboard.rs @@ -66,9 +66,10 @@ struct KbdInternal { } impl KbdInternal { - fn new(rules: &str, model: &str, layout: &str, variant: &str, options: Option, - repeat_rate: i32, repeat_delay: i32) - -> Result { + fn new( + rules: &str, model: &str, layout: &str, variant: &str, options: Option, repeat_rate: i32, + repeat_delay: i32, + ) -> Result { // we create a new contex for each keyboard because libxkbcommon is actually NOT threadsafe // so confining it inside the KbdInternal allows us to use Rusts mutability rules to make // sure nothing goes wrong. @@ -168,10 +169,10 @@ pub enum Error { } /// Create a keyboard handler from a set of RMLVO rules -pub(crate) fn create_keyboard_handler(rules: &str, model: &str, layout: &str, variant: &str, - options: Option, repeat_delay: i32, repeat_rate: i32, - logger: &::slog::Logger) - -> Result { +pub(crate) fn create_keyboard_handler( + rules: &str, model: &str, layout: &str, variant: &str, options: Option, repeat_delay: i32, + repeat_rate: i32, logger: &::slog::Logger, +) -> Result { let log = logger.new(o!("smithay_module" => "xkbcommon_handler")); info!(log, "Initializing a xkbcommon handler with keymap query"; "rules" => rules, "model" => model, "layout" => layout, "variant" => variant, @@ -192,7 +193,6 @@ pub(crate) fn create_keyboard_handler(rules: &str, model: &str, layout: &str, va info!(log, "Loaded Keymap"; "name" => internal.keymap.layouts().next()); - // prepare a tempfile with the keymap, to send it to clients let mut keymap_file = tempfile().map_err(Error::IoError)?; let keymap_data = internal.keymap.get_as_string(xkb::KEYMAP_FORMAT_TEXT_V1); diff --git a/src/wayland/seat/mod.rs b/src/wayland/seat/mod.rs index 45612d2..afa5e0f 100644 --- a/src/wayland/seat/mod.rs +++ b/src/wayland/seat/mod.rs @@ -87,8 +87,9 @@ impl Seat { /// You are provided with the state token to retrieve it (allowing /// you to add or remove capabilities from it), and the global handle, /// in case you want to remove it. - pub fn new(evl: &mut EventLoop, name: String, logger: L) - -> (StateToken, Global>) + pub fn new( + evl: &mut EventLoop, name: String, logger: L + ) -> (StateToken, Global>) where L: Into>, { @@ -158,9 +159,10 @@ impl Seat { /// Calling this method on a seat that already has a keyboard capability /// will overwrite it, and will be seen by the clients as if the /// keyboard was unplugged and a new one was plugged. - pub fn add_keyboard(&mut self, model: &str, layout: &str, variant: &str, options: Option, - repeat_delay: i32, repeat_rate: i32) - -> Result { + pub fn add_keyboard( + &mut self, model: &str, layout: &str, variant: &str, options: Option, repeat_delay: i32, + repeat_rate: i32, + ) -> Result { let keyboard = self::keyboard::create_keyboard_handler( "evdev", // we need this one model, @@ -229,8 +231,9 @@ impl Seat { } } -fn seat_global_bind(evlh: &mut EventLoopHandle, token: &mut StateToken, _: &Client, - seat: wl_seat::WlSeat) { +fn seat_global_bind( + evlh: &mut EventLoopHandle, token: &mut StateToken, _: &Client, seat: wl_seat::WlSeat +) { evlh.register(&seat, seat_implementation(), token.clone(), None); let seat_mgr = evlh.state().get_mut(token); seat.name(seat_mgr.name.clone()); diff --git a/src/wayland/shell/mod.rs b/src/wayland/shell/mod.rs index 835e310..6a4ca1a 100644 --- a/src/wayland/shell/mod.rs +++ b/src/wayland/shell/mod.rs @@ -302,12 +302,12 @@ impl Clone for ShellSurfaceIData { /// globals from the event loop in the future. pub fn shell_init( evl: &mut EventLoop, token: CompositorToken, - implementation: ShellSurfaceUserImplementation, idata: SID, logger: L) - -> ( - StateToken>, - Global>, - Global>, - ) + implementation: ShellSurfaceUserImplementation, idata: SID, logger: L, +) -> ( + StateToken>, + Global>, + Global>, +) where U: 'static, R: Role + 'static, @@ -449,7 +449,8 @@ impl ShellClient { } match self.kind { ShellClientKind::Wl(ref shell) => { - let mutex = unsafe { &*(shell.get_user_data() as *mut self::wl_handlers::ShellUserData) }; + let mutex = + unsafe { &*(shell.get_user_data() as *mut self::wl_handlers::ShellUserData) }; let mut guard = mutex.lock().unwrap(); if guard.0.pending_ping == 0 { return Err(()); @@ -486,7 +487,8 @@ impl ShellClient { } match self.kind { ShellClientKind::Wl(ref shell) => { - let mutex = unsafe { &*(shell.get_user_data() as *mut self::wl_handlers::ShellUserData) }; + let mutex = + unsafe { &*(shell.get_user_data() as *mut self::wl_handlers::ShellUserData) }; let mut guard = mutex.lock().unwrap(); Ok(f(&mut guard.0.data)) } @@ -844,46 +846,44 @@ pub struct ShellSurfaceUserImplementation { /// /// You need to return a `ToplevelConfigure` from this function, which will be sent /// to the client to configure this surface - pub new_toplevel: fn( - evlh: &mut EventLoopHandle, - idata: &mut SID, - surface: ToplevelSurface, - ) -> ToplevelConfigure, + pub new_toplevel: + fn(evlh: &mut EventLoopHandle, idata: &mut SID, surface: ToplevelSurface) + -> ToplevelConfigure, /// A new popup surface was created /// /// You need to return a `PopupConfigure` from this function, which will be sent /// to the client to configure this surface pub new_popup: fn(evlh: &mut EventLoopHandle, idata: &mut SID, surface: PopupSurface) - -> PopupConfigure, + -> PopupConfigure, /// The client requested the start of an interactive move for this surface pub move_: fn( - evlh: &mut EventLoopHandle, - idata: &mut SID, - surface: ToplevelSurface, - seat: &wl_seat::WlSeat, - serial: u32, + evlh: &mut EventLoopHandle, + idata: &mut SID, + surface: ToplevelSurface, + seat: &wl_seat::WlSeat, + serial: u32, ), /// The client requested the start of an interactive resize for this surface /// /// The `edges` argument specifies which part of the window's border is being dragged. pub resize: fn( - evlh: &mut EventLoopHandle, - idata: &mut SID, - surface: ToplevelSurface, - seat: &wl_seat::WlSeat, - serial: u32, - edges: zxdg_toplevel_v6::ResizeEdge, + evlh: &mut EventLoopHandle, + idata: &mut SID, + surface: ToplevelSurface, + seat: &wl_seat::WlSeat, + serial: u32, + edges: zxdg_toplevel_v6::ResizeEdge, ), /// This popup requests a grab of the pointer /// /// This means it requests to be sent a `popup_done` event when the pointer leaves /// the grab area. pub grab: fn( - evlh: &mut EventLoopHandle, - idata: &mut SID, - surface: PopupSurface, - seat: &wl_seat::WlSeat, - serial: u32, + evlh: &mut EventLoopHandle, + idata: &mut SID, + surface: PopupSurface, + seat: &wl_seat::WlSeat, + serial: u32, ), /// A toplevel surface requested its display state to be changed /// @@ -899,26 +899,26 @@ pub struct ShellSurfaceUserImplementation { /// You are to answer with a `ToplevelConfigure` that will be sent to the client in /// response. pub change_display_state: fn( - evlh: &mut EventLoopHandle, - idata: &mut SID, - surface: ToplevelSurface, - maximized: Option, - minimized: Option, - fullscreen: Option, - output: Option<&wl_output::WlOutput>, + evlh: &mut EventLoopHandle, + idata: &mut SID, + surface: ToplevelSurface, + maximized: Option, + minimized: Option, + fullscreen: Option, + output: Option<&wl_output::WlOutput>, ) -> ToplevelConfigure, /// The client requests the window menu to be displayed on this surface at this location /// /// This menu belongs to the compositor. It is typically expected to contain options for /// control of the window (maximize/minimize/close/move/etc...). pub show_window_menu: fn( - evlh: &mut EventLoopHandle, - idata: &mut SID, - surface: ToplevelSurface, - seat: &wl_seat::WlSeat, - serial: u32, - x: i32, - y: i32, + evlh: &mut EventLoopHandle, + idata: &mut SID, + surface: ToplevelSurface, + seat: &wl_seat::WlSeat, + serial: u32, + x: i32, + y: i32, ), } diff --git a/src/wayland/shell/wl_handlers.rs b/src/wayland/shell/wl_handlers.rs index 7c5ab41..0255f07 100644 --- a/src/wayland/shell/wl_handlers.rs +++ b/src/wayland/shell/wl_handlers.rs @@ -10,10 +10,10 @@ use wayland_protocols::unstable::xdg_shell::v6::server::{zxdg_positioner_v6 as x use wayland_server::{Client, EventLoopHandle, Resource}; use wayland_server::protocol::{wl_output, wl_shell, wl_shell_surface, wl_surface}; -pub(crate) fn wl_shell_bind(evlh: &mut EventLoopHandle, - idata: &mut ShellSurfaceIData, - _: &Client, shell: wl_shell::WlShell) -where +pub(crate) fn wl_shell_bind( + evlh: &mut EventLoopHandle, idata: &mut ShellSurfaceIData, _: &Client, + shell: wl_shell::WlShell, +) where U: 'static, R: Role + 'static, CID: 'static, @@ -56,8 +56,7 @@ pub fn make_shell_client(resource: &wl_shell::WlShell) -> ShellClient { } fn shell_implementation( - ) - -> wl_shell::Implementation> +) -> wl_shell::Implementation> where U: 'static, R: Role + 'static, @@ -122,9 +121,9 @@ fn destroy_shell_surface(shell_surface: &wl_shell_surface::WlShellSurface) { ::std::mem::drop(surface); } -fn make_toplevel_handle(token: CompositorToken, - resource: &wl_shell_surface::WlShellSurface) - -> super::ToplevelSurface { +fn make_toplevel_handle( + token: CompositorToken, resource: &wl_shell_surface::WlShellSurface +) -> super::ToplevelSurface { let ptr = resource.get_user_data(); let &(ref wl_surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) }; super::ToplevelSurface { @@ -135,9 +134,9 @@ fn make_toplevel_handle(token: CompositorToken, } } -fn make_popup_handle(token: CompositorToken, - resource: &wl_shell_surface::WlShellSurface) - -> super::PopupSurface { +fn make_popup_handle( + token: CompositorToken, resource: &wl_shell_surface::WlShellSurface +) -> super::PopupSurface { let ptr = resource.get_user_data(); let &(ref wl_surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) }; super::PopupSurface { @@ -158,12 +157,11 @@ pub fn send_popup_configure(resource: &wl_shell_surface::WlShellSurface, configu resource.configure(wl_shell_surface::Resize::empty(), w, h); } -fn wl_handle_display_state_change(evlh: &mut EventLoopHandle, - idata: &ShellSurfaceIData, - shell_surface: &wl_shell_surface::WlShellSurface, - maximized: Option, minimized: Option, - fullscreen: Option, - output: Option<&wl_output::WlOutput>) { +fn wl_handle_display_state_change( + evlh: &mut EventLoopHandle, idata: &ShellSurfaceIData, + shell_surface: &wl_shell_surface::WlShellSurface, maximized: Option, minimized: Option, + fullscreen: Option, output: Option<&wl_output::WlOutput>, +) { let handle = make_toplevel_handle(idata.compositor_token, shell_surface); // handler callback let mut user_idata = idata.idata.borrow_mut(); @@ -181,10 +179,10 @@ fn wl_handle_display_state_change(evlh: &mut EventLoopHandle shell_surface.configure(wl_shell_surface::Resize::None, w, h); } -fn wl_set_parent(idata: &ShellSurfaceIData, - shell_surface: &wl_shell_surface::WlShellSurface, - parent: Option) -where +fn wl_set_parent( + idata: &ShellSurfaceIData, shell_surface: &wl_shell_surface::WlShellSurface, + parent: Option, +) where U: 'static, R: Role + 'static, CID: 'static, @@ -204,10 +202,10 @@ where .unwrap(); } -fn wl_ensure_toplevel(evlh: &mut EventLoopHandle, - idata: &ShellSurfaceIData, - shell_surface: &wl_shell_surface::WlShellSurface) -where +fn wl_ensure_toplevel( + evlh: &mut EventLoopHandle, idata: &ShellSurfaceIData, + shell_surface: &wl_shell_surface::WlShellSurface, +) where U: 'static, R: Role + 'static, CID: 'static, @@ -263,8 +261,7 @@ where } fn shell_surface_implementation( - ) - -> wl_shell_surface::Implementation> +) -> wl_shell_surface::Implementation> where U: 'static, R: Role + 'static, @@ -274,7 +271,8 @@ where { wl_shell_surface::Implementation { pong: |evlh, idata, _, shell_surface, serial| { - let &(_, ref shell) = unsafe { &*(shell_surface.get_user_data() as *mut ShellSurfaceUserData) }; + let &(_, ref shell) = + unsafe { &*(shell_surface.get_user_data() as *mut ShellSurfaceUserData) }; let valid = { let mutex = unsafe { &*(shell.get_user_data() as *mut ShellUserData) }; let mut guard = mutex.lock().unwrap(); diff --git a/src/wayland/shell/xdg_handlers.rs b/src/wayland/shell/xdg_handlers.rs index 66b5380..203d665 100644 --- a/src/wayland/shell/xdg_handlers.rs +++ b/src/wayland/shell/xdg_handlers.rs @@ -10,10 +10,10 @@ use wayland_protocols::unstable::xdg_shell::v6::server::{zxdg_popup_v6, zxdg_pos use wayland_server::{Client, EventLoopHandle, Resource}; use wayland_server::protocol::{wl_output, wl_surface}; -pub(crate) fn xdg_shell_bind(evlh: &mut EventLoopHandle, - idata: &mut ShellSurfaceIData, - _: &Client, shell: zxdg_shell_v6::ZxdgShellV6) -where +pub(crate) fn xdg_shell_bind( + evlh: &mut EventLoopHandle, idata: &mut ShellSurfaceIData, _: &Client, + shell: zxdg_shell_v6::ZxdgShellV6, +) where U: 'static, R: Role + 'static, CID: 'static, @@ -53,8 +53,7 @@ pub(crate) fn make_shell_client(resource: &zxdg_shell_v6::ZxdgShellV6) -> Sh } fn shell_implementation( - ) - -> zxdg_shell_v6::Implementation> +) -> zxdg_shell_v6::Implementation> where U: 'static, R: Role + 'static, @@ -104,11 +103,10 @@ where ); return; } - xdg_surface.set_user_data( - Box::into_raw(Box::new((unsafe { wl_surface.clone_unchecked() }, unsafe { - shell.clone_unchecked() - }))) as *mut _, - ); + xdg_surface.set_user_data(Box::into_raw(Box::new(( + unsafe { wl_surface.clone_unchecked() }, + unsafe { shell.clone_unchecked() }, + ))) as *mut _); evlh.register( &xdg_surface, surface_implementation(), @@ -151,30 +149,34 @@ fn destroy_positioner(positioner: &zxdg_positioner_v6::ZxdgPositionerV6) { fn positioner_implementation() -> zxdg_positioner_v6::Implementation<()> { zxdg_positioner_v6::Implementation { destroy: |_, _, _, _| {}, - set_size: |_, _, _, positioner, width, height| if width < 1 || height < 1 { - positioner.post_error( - zxdg_positioner_v6::Error::InvalidInput as u32, - "Invalid size for positioner.".into(), - ); - } else { - let ptr = positioner.get_user_data(); - let state = unsafe { &mut *(ptr as *mut PositionerState) }; - state.rect_size = (width, height); + set_size: |_, _, _, positioner, width, height| { + if width < 1 || height < 1 { + positioner.post_error( + zxdg_positioner_v6::Error::InvalidInput as u32, + "Invalid size for positioner.".into(), + ); + } else { + let ptr = positioner.get_user_data(); + let state = unsafe { &mut *(ptr as *mut PositionerState) }; + state.rect_size = (width, height); + } }, - set_anchor_rect: |_, _, _, positioner, x, y, width, height| if width < 1 || height < 1 { - positioner.post_error( - zxdg_positioner_v6::Error::InvalidInput as u32, - "Invalid size for positioner's anchor rectangle.".into(), - ); - } else { - let ptr = positioner.get_user_data(); - let state = unsafe { &mut *(ptr as *mut PositionerState) }; - state.anchor_rect = Rectangle { - x, - y, - width, - height, - }; + set_anchor_rect: |_, _, _, positioner, x, y, width, height| { + if width < 1 || height < 1 { + positioner.post_error( + zxdg_positioner_v6::Error::InvalidInput as u32, + "Invalid size for positioner's anchor rectangle.".into(), + ); + } else { + let ptr = positioner.get_user_data(); + let state = unsafe { &mut *(ptr as *mut PositionerState) }; + state.anchor_rect = Rectangle { + x, + y, + width, + height, + }; + } }, set_anchor: |_, _, _, positioner, anchor| { use self::zxdg_positioner_v6::Anchor; @@ -228,15 +230,15 @@ fn destroy_surface(surface: &zxdg_surface_v6::ZxdgSurfaceV6) { let ptr = surface.get_user_data(); surface.set_user_data(::std::ptr::null_mut()); // drop the state - let data = - unsafe { Box::from_raw(ptr as *mut (zxdg_surface_v6::ZxdgSurfaceV6, zxdg_shell_v6::ZxdgShellV6)) }; + let data = unsafe { + Box::from_raw(ptr as *mut (zxdg_surface_v6::ZxdgSurfaceV6, zxdg_shell_v6::ZxdgShellV6)) + }; // explicit call to drop to not forget what we're doing here ::std::mem::drop(data); } fn surface_implementation( - ) - -> zxdg_surface_v6::Implementation> +) -> zxdg_surface_v6::Implementation> where U: 'static, R: Role + 'static, @@ -417,9 +419,9 @@ fn destroy_toplevel(surface: &zxdg_toplevel_v6::ZxdgToplevelV6) { } // Utility functions allowing to factor out a lot of the upcoming logic -fn with_surface_toplevel_data(idata: &ShellSurfaceIData, - toplevel: &zxdg_toplevel_v6::ZxdgToplevelV6, f: F) -where +fn with_surface_toplevel_data( + idata: &ShellSurfaceIData, toplevel: &zxdg_toplevel_v6::ZxdgToplevelV6, f: F +) where U: 'static, R: Role + 'static, CID: 'static, @@ -438,13 +440,11 @@ where .expect("xdg_toplevel exists but surface has not shell_surface role?!"); } -fn xdg_handle_display_state_change(evlh: &mut EventLoopHandle, - idata: &ShellSurfaceIData, - toplevel: &zxdg_toplevel_v6::ZxdgToplevelV6, - maximized: Option, minimized: Option, - fullscreen: Option, - output: Option<&wl_output::WlOutput>) -where +fn xdg_handle_display_state_change( + evlh: &mut EventLoopHandle, idata: &ShellSurfaceIData, + toplevel: &zxdg_toplevel_v6::ZxdgToplevelV6, maximized: Option, minimized: Option, + fullscreen: Option, output: Option<&wl_output::WlOutput>, +) where U: 'static, R: Role + 'static, CID: 'static, @@ -467,11 +467,10 @@ where send_toplevel_configure(idata.compositor_token, toplevel, configure); } - -pub fn send_toplevel_configure(token: CompositorToken, - resource: &zxdg_toplevel_v6::ZxdgToplevelV6, - configure: ToplevelConfigure) -where +pub fn send_toplevel_configure( + token: CompositorToken, resource: &zxdg_toplevel_v6::ZxdgToplevelV6, + configure: ToplevelConfigure, +) where U: 'static, R: Role + 'static, ID: 'static, @@ -497,9 +496,9 @@ where .expect("xdg_toplevel exists but surface has not shell_surface role?!"); } -fn make_toplevel_handle(token: CompositorToken, - resource: &zxdg_toplevel_v6::ZxdgToplevelV6) - -> super::ToplevelSurface { +fn make_toplevel_handle( + token: CompositorToken, resource: &zxdg_toplevel_v6::ZxdgToplevelV6 +) -> super::ToplevelSurface { let ptr = resource.get_user_data(); let &(ref wl_surface, _, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) }; super::ToplevelSurface { @@ -511,8 +510,7 @@ fn make_toplevel_handle(token: CompositorToken, } fn toplevel_implementation( - ) - -> zxdg_toplevel_v6::Implementation> +) -> zxdg_toplevel_v6::Implementation> where U: 'static, R: Role + 'static, @@ -546,8 +544,9 @@ where with_surface_toplevel_data(idata, toplevel, |toplevel_data| { toplevel_data.parent = parent.map(|toplevel_surface_parent| { let parent_ptr = toplevel_surface_parent.get_user_data(); - let &(ref parent_surface, _) = - unsafe { &*(parent_ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) }; + let &(ref parent_surface, _) = unsafe { + &*(parent_ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) + }; unsafe { parent_surface.clone_unchecked() } }) }); @@ -620,10 +619,9 @@ fn destroy_popup(surface: &zxdg_popup_v6::ZxdgPopupV6) { ::std::mem::drop(data); } -pub(crate) fn send_popup_configure(token: CompositorToken, - resource: &zxdg_popup_v6::ZxdgPopupV6, - configure: PopupConfigure) -where +pub(crate) fn send_popup_configure( + token: CompositorToken, resource: &zxdg_popup_v6::ZxdgPopupV6, configure: PopupConfigure +) where U: 'static, R: Role + 'static, ID: 'static, @@ -641,8 +639,9 @@ where .expect("xdg_toplevel exists but surface has not shell_surface role?!"); } -fn make_popup_handle(token: CompositorToken, resource: &zxdg_popup_v6::ZxdgPopupV6) - -> super::PopupSurface { +fn make_popup_handle( + token: CompositorToken, resource: &zxdg_popup_v6::ZxdgPopupV6 +) -> super::PopupSurface { let ptr = resource.get_user_data(); let &(ref wl_surface, _, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) }; super::PopupSurface { @@ -654,8 +653,7 @@ fn make_popup_handle(token: CompositorToken, resource: &zx } fn popup_implementation( - ) - -> zxdg_popup_v6::Implementation> +) -> zxdg_popup_v6::Implementation> where U: 'static, R: Role + 'static, diff --git a/src/wayland/shm/mod.rs b/src/wayland/shm/mod.rs index e60a02b..0346079 100644 --- a/src/wayland/shm/mod.rs +++ b/src/wayland/shm/mod.rs @@ -61,7 +61,6 @@ //! //! If you are already using an handler for this signal, you probably don't want to use this handler. - use self::pool::{Pool, ResizeError}; use std::rc::Rc; use std::sync::Arc; @@ -89,8 +88,9 @@ pub struct ShmGlobalData { /// The global is directly registered into the eventloop, and this function /// returns the global handle, in case you whish to remove this global in /// the future. -pub fn init_shm_global(evl: &mut EventLoop, mut formats: Vec, logger: L) - -> Global +pub fn init_shm_global( + evl: &mut EventLoop, mut formats: Vec, logger: L +) -> Global where L: Into>, { diff --git a/src/wayland/shm/pool.rs b/src/wayland/shm/pool.rs index 20faa62..b70442a 100644 --- a/src/wayland/shm/pool.rs +++ b/src/wayland/shm/pool.rs @@ -1,5 +1,3 @@ - - use nix::{libc, unistd}; use nix::sys::mman; use nix::sys::signal::{self, SigAction, SigHandler, Signal}; From ec6eae6799c4de66deac05c9ad2e87e5cbbecaed Mon Sep 17 00:00:00 2001 From: Drakulix Date: Thu, 21 Dec 2017 16:23:34 +0100 Subject: [PATCH 25/29] Use stable drm-rs version --- Cargo.toml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 86c6014..b50c13d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ slog-stdlog = "2.0.0-0.2" libloading = "0.4.0" wayland-client = { version = "0.9.9", optional = true } winit = { version = "0.8.2", optional = true } -drm = { version = "^0.3.0", optional = true } +drm = { version = "^0.3.1", optional = true } gbm = { version = "^0.3.0", optional = true, default-features = false, features = ["drm-support"] } glium = { version = "0.17.1", optional = true, default-features = false } input = { version = "0.4.0", optional = true } @@ -44,6 +44,3 @@ backend_session = [] backend_session_udev = ["udev", "backend_session"] backend_udev = ["udev", "backend_drm", "backend_session_udev"] renderer_glium = ["glium"] - -[patch.crates-io] -drm = { git = "https://github.com/Smithay/drm-rs.git" } From d0046e4c60fffd9c0a29e9916a521d1226ef39d2 Mon Sep 17 00:00:00 2001 From: Drakulix Date: Thu, 21 Dec 2017 16:25:21 +0100 Subject: [PATCH 26/29] Fix rare hot-plugging bug --- src/backend/drm/backend.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/backend/drm/backend.rs b/src/backend/drm/backend.rs index f48feb2..768d888 100644 --- a/src/backend/drm/backend.rs +++ b/src/backend/drm/backend.rs @@ -174,11 +174,9 @@ impl DrmBackend { let next_bo = egl.buffers.next_buffer.replace(None); if let Some(next_buffer) = next_bo { - trace!(self.logger, "Releasing all front buffer"); + trace!(self.logger, "Releasing old front buffer"); egl.buffers.front_buffer.set(next_buffer); - // drop and release the old buffer - } else { - unreachable!(); + // drop and release the old buffer } }) }); From 943b59a107ce8f7eec31f4806d0c42ee60da3601 Mon Sep 17 00:00:00 2001 From: Drakulix Date: Thu, 21 Dec 2017 16:26:00 +0100 Subject: [PATCH 27/29] Spawn weston-terminal in udev example on keycode --- examples/udev.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/udev.rs b/examples/udev.rs index accbb0b..830d178 100644 --- a/examples/udev.rs +++ b/examples/udev.rs @@ -34,7 +34,7 @@ use smithay::backend::drm::{DrmBackend, DrmDevice, DrmHandler}; use smithay::backend::graphics::GraphicsBackend; use smithay::backend::graphics::egl::EGLGraphicsBackend; use smithay::backend::input::{self, Event, InputBackend, InputHandler, KeyboardKeyEvent, PointerButtonEvent, - PointerAxisEvent}; + PointerAxisEvent, KeyState}; use smithay::backend::libinput::{LibinputInputBackend, libinput_bind, PointerAxisEvent as LibinputPointerAxisEvent, LibinputSessionInterface}; use smithay::backend::udev::{UdevBackend, UdevHandler, udev_backend_bind}; use smithay::backend::session::{Session, SessionNotifier}; @@ -52,6 +52,7 @@ use std::rc::Rc; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; use std::time::Duration; +use std::process::Command; use xkbcommon::xkb::keysyms as xkb; use wayland_server::{StateToken, StateProxy}; use wayland_server::protocol::{wl_output, wl_pointer}; @@ -94,6 +95,9 @@ impl InputHandler for LibinputInputHandler { if modifiers.ctrl && modifiers.alt && keysym == xkb::KEY_BackSpace { self.running.store(false, Ordering::SeqCst); false + } else if modifiers.logo && keysym == xkb::KEY_Return && state == KeyState::Pressed { + let _ = Command::new("weston-terminal").spawn(); + false } else { true } From d3a8ff629d63d78f395581dac43a849b1b7bd2e8 Mon Sep 17 00:00:00 2001 From: Drakulix Date: Thu, 21 Dec 2017 17:57:19 +0100 Subject: [PATCH 28/29] Test new features --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index bd28f52..9ceec5a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,6 +33,9 @@ env: - FEATURES="backend_winit" - FEATURES="backend_drm" - FEATURES="backend_libinput" + - FEATURES="backend_udev" + - FEATURES="backend_session" + - FEATURES="backend_session_udev" - FEATURES="renderer_glium" # test default features - FEATURES="default" From bfed76593afe0ecdafcecf0a54269cc2560b64de Mon Sep 17 00:00:00 2001 From: Drakulix Date: Thu, 21 Dec 2017 18:11:19 +0100 Subject: [PATCH 29/29] Fix backend_session_udev without backend_udev --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 074dd9d..a69d09f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,7 +23,7 @@ extern crate drm; extern crate gbm; #[cfg(feature = "backend_libinput")] extern crate input; -#[cfg(feature = "backend_udev")] +#[cfg(feature = "udev")] extern crate udev; /* #[cfg(feature = "backend_session_logind")]