Udev backend

This commit is contained in:
Drakulix 2017-10-01 19:21:12 +02:00
parent c31d966fd4
commit c7682e77de
17 changed files with 1247 additions and 89 deletions

View File

@ -17,13 +17,15 @@ libloading = "0.4.0"
wayland-client = { version = "0.9.9", optional = true } wayland-client = { version = "0.9.9", optional = true }
winit = { version = "0.8.2", optional = true } winit = { version = "0.8.2", optional = true }
drm = { version = "^0.3.0", 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 } 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" rental = "0.4.11"
wayland-protocols = { version = "0.12.0", features = ["unstable_protocols", "server"] } wayland-protocols = { version = "0.12.0", features = ["unstable_protocols", "server"] }
image = "0.16.0" image = "0.17.0"
error-chain = "0.11.0" error-chain = "0.11.0"
dbus = { version = "0.5.4", optional = true }
[build-dependencies] [build-dependencies]
gl_generator = "0.5" gl_generator = "0.5"
@ -32,10 +34,15 @@ gl_generator = "0.5"
slog-term = "2.0" slog-term = "2.0"
slog-async = "2.0" slog-async = "2.0"
rand = "0.3" rand = "0.3"
ctrlc = { version = "3.0", features = ["termination"] }
[features] [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_winit = ["winit", "wayland-server/dlopen", "wayland-client/dlopen"]
backend_drm = ["drm", "gbm"] backend_drm = ["drm", "gbm"]
backend_libinput = ["input"] 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"] renderer_glium = ["glium"]

View File

@ -15,6 +15,7 @@ mod helpers;
use drm::control::{Device as ControlDevice, ResourceInfo}; use drm::control::{Device as ControlDevice, ResourceInfo};
use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState}; use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState};
use drm::control::crtc;
use drm::control::encoder::Info as EncoderInfo; use drm::control::encoder::Info as EncoderInfo;
use drm::result::Error as DrmError; use drm::result::Error as DrmError;
use glium::Surface; use glium::Surface;
@ -80,11 +81,20 @@ fn main() {
// Assuming we found a good connector and loaded the info into `connector_info` // Assuming we found a good connector and loaded the info into `connector_info`
let mode = connector_info.modes()[0]; // Use first mode (usually highest resoltion, but in reality you should filter and sort and check and match with other connectors, if you use more then one.) let mode = connector_info.modes()[0]; // Use first mode (usually highest resoltion, but in reality you should filter and sort and check and match with other connectors, if you use more then one.)
{
// Initialize the hardware backend // Initialize the hardware backend
let renderer_token = device let renderer = device
.create_backend(&mut event_loop, crtc, mode, vec![connector_info.handle()]) .create_backend(crtc, mode, vec![connector_info.handle()])
.unwrap(); .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 * Initialize the globals
*/ */
@ -93,16 +103,6 @@ fn main() {
let (compositor_token, shell_state_token, window_map) = init_shell(&mut event_loop, log.clone()); let (compositor_token, shell_state_token, window_map) = init_shell(&mut event_loop, log.clone());
/*
* 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: * Add a listening socket:
*/ */
@ -112,9 +112,10 @@ fn main() {
/* /*
* Register the DrmDevice on the EventLoop * Register the DrmDevice on the EventLoop
*/ */
let device_token = event_loop.state().insert(device);
let _source = drm_device_bind( let _source = drm_device_bind(
&mut event_loop, &mut event_loop,
device, device_token,
DrmHandlerImpl { DrmHandlerImpl {
shell_state_token, shell_state_token,
compositor_token, compositor_token,
@ -139,10 +140,11 @@ pub struct DrmHandlerImpl {
} }
impl DrmHandler<GliumDrawer<DrmBackend>> for DrmHandlerImpl { impl DrmHandler<GliumDrawer<DrmBackend>> for DrmHandlerImpl {
fn ready(&mut self, evlh: &mut EventLoopHandle, _device: &mut DrmDevice<GliumDrawer<DrmBackend>>, fn ready(&mut self, evlh: &mut EventLoopHandle, device: &StateToken<DrmDevice<GliumDrawer<DrmBackend>>>,
backend: &StateToken<GliumDrawer<DrmBackend>>, _frame: u32, _duration: Duration) { crtc: &crtc::Handle, _frame: u32, _duration: Duration) {
let state = evlh.state(); 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(); let mut frame = drawer.draw();
frame.clear_color(0.8, 0.8, 0.9, 1.0); frame.clear_color(0.8, 0.8, 0.9, 1.0);
// redraw the frame, in a simple but inneficient way // redraw the frame, in a simple but inneficient way
@ -185,7 +187,7 @@ impl DrmHandler<GliumDrawer<DrmBackend>> for DrmHandlerImpl {
frame.finish().unwrap(); frame.finish().unwrap();
} }
fn error(&mut self, _evlh: &mut EventLoopHandle, _device: &mut DrmDevice<GliumDrawer<DrmBackend>>, fn error(&mut self, _evlh: &mut EventLoopHandle, _device: &StateToken<DrmDevice<GliumDrawer<DrmBackend>>>,
error: DrmError) { error: DrmError) {
panic!("{:?}", error); panic!("{:?}", error);
} }

261
examples/udev.rs Normal file
View File

@ -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<ShellState<SurfaceData, Roles, (), ()>>,
compositor_token: CompositorToken<SurfaceData, Roles, ()>,
window_map: Rc<RefCell<MyWindowMap>>,
logger: ::slog::Logger,
}
impl UdevHandlerImpl {
pub fn scan_connectors<'a, S: Into<StateProxy<'a>>>(&self, state: S, device: &mut DrmDevice<GliumDrawer<DrmBackend>>) {
// Get a set of all modesetting resource handles (excluding planes):
let res_handles = device.resource_handles().unwrap();
// Use first connected connector
let connector_infos: Vec<ConnectorInfo> = res_handles
.connectors()
.iter()
.map(|conn| {
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<crtc::Handle> = 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::<Vec<EncoderInfo>>();
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<GliumDrawer<DrmBackend>, DrmHandlerImpl> for UdevHandlerImpl {
fn device_added<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &mut DrmDevice<GliumDrawer<DrmBackend>>) -> Option<DrmHandlerImpl>
{
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<StateProxy<'a>>>(&mut self, state: S, device: &StateToken<DrmDevice<GliumDrawer<DrmBackend>>>) {
//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<StateProxy<'a>>>(&mut self, state: S, device: &StateToken<DrmDevice<GliumDrawer<DrmBackend>>>) {
state.into().with_value(device, |state, device| {
let crtcs = device.current_backends().into_iter().map(|backend| state.get(backend).crtc()).collect::<Vec<crtc::Handle>>();
let mut state: StateProxy = state.into();
for crtc in crtcs {
device.destroy_backend(&mut state, &crtc);
}
});
}
fn error<'a, S: Into<StateProxy<'a>>>(&mut self, _state: S, error: IoError) {
error!(self.logger, "{:?}", error);
}
}
pub struct DrmHandlerImpl {
shell_state_token: StateToken<ShellState<SurfaceData, Roles, (), ()>>,
compositor_token: CompositorToken<SurfaceData, Roles, ()>,
window_map: Rc<RefCell<MyWindowMap>>,
logger: ::slog::Logger,
}
impl DrmHandler<GliumDrawer<DrmBackend>> for DrmHandlerImpl {
fn ready<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, _device: &mut DrmDevice<GliumDrawer<DrmBackend>>,
backend: &StateToken<GliumDrawer<DrmBackend>>, _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::<SubsurfaceRole>::data(role) {
x += subdata.x;
y += subdata.y;
}
drawer.render(
&mut frame,
contents,
(w, h),
(x, y),
screen_dimensions,
);
TraversalAction::DoChildren((x, y))
} else {
// we are not display, so our children are neither
TraversalAction::SkipChildren
}
},
)
.unwrap();
}
});
}
if let Err(err) = frame.finish() {
error!(self.logger, "Error during rendering: {:?}", err);
}
}
fn error<'a, S: Into<StateProxy<'a>>>(&mut self, _state: S, _device: &mut DrmDevice<GliumDrawer<DrmBackend>>,
error: DrmError) {
error!(self.logger, "{:?}", error);
}
}

View File

@ -92,7 +92,7 @@ impl DrmBackend {
1, 1,
1, 1,
GbmFormat::ARGB8888, GbmFormat::ARGB8888,
&[BufferObjectFlags::Cursor, BufferObjectFlags::Write], BufferObjectFlags::CURSOR | BufferObjectFlags::WRITE,
) )
.chain_err(|| ErrorKind::GbmInitFailed)?) .chain_err(|| ErrorKind::GbmInitFailed)?)
}, },
@ -107,7 +107,7 @@ impl DrmBackend {
w as u32, w as u32,
h as u32, h as u32,
GbmFormat::XRGB8888, GbmFormat::XRGB8888,
&[BufferObjectFlags::Scanout, BufferObjectFlags::Rendering], BufferObjectFlags::SCANOUT | BufferObjectFlags::RENDERING,
) )
.chain_err(|| ErrorKind::GbmInitFailed)?) .chain_err(|| ErrorKind::GbmInitFailed)?)
}, },
@ -311,7 +311,7 @@ impl DrmBackend {
w as u32, w as u32,
h as u32, h as u32,
GbmFormat::XRGB8888, GbmFormat::XRGB8888,
&[BufferObjectFlags::Scanout, BufferObjectFlags::Rendering], BufferObjectFlags::SCANOUT | BufferObjectFlags::RENDERING,
) )
.chain_err(|| ErrorKind::GbmInitFailed)?) .chain_err(|| ErrorKind::GbmInitFailed)?)
}, },
@ -374,6 +374,10 @@ impl DrmBackend {
self.mode = mode; self.mode = mode;
Ok(()) Ok(())
} }
pub fn crtc(&self) -> crtc::Handle {
self.crtc
}
} }
impl Drop for DrmBackend { impl Drop for DrmBackend {
@ -437,7 +441,7 @@ impl GraphicsBackend for DrmBackend {
w, w,
h, h,
GbmFormat::ARGB8888, GbmFormat::ARGB8888,
&[BufferObjectFlags::Cursor, BufferObjectFlags::Write], BufferObjectFlags::CURSOR | BufferObjectFlags::WRITE,
) )
.chain_err(|| ErrorKind::GbmInitFailed)?; .chain_err(|| ErrorKind::GbmInitFailed)?;
cursor cursor
@ -523,7 +527,7 @@ impl EGLGraphicsBackend for DrmBackend {
} }
fn is_current(&self) -> bool { 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> { unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {

View File

@ -8,6 +8,11 @@ use rental::TryNewError;
error_chain! { error_chain! {
errors { errors {
#[doc = "Unable to acquire drm master"]
DrmMasterFailed {
description("Failed to acquire drm master")
}
#[doc = "The `DrmDevice` encountered an access error"] #[doc = "The `DrmDevice` encountered an access error"]
DrmDev(dev: String) { DrmDev(dev: String) {
description("The drm device encountered an access error"), description("The drm device encountered an access error"),
@ -26,7 +31,13 @@ error_chain! {
display("Swapping front buffers failed"), 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) { ModeNotSuitable(mode: Mode) {
description("Mode is not compatible with all given connectors"), description("Mode is not compatible with all given connectors"),
display("Mode ({:?}) is not compatible with all given connectors", mode), display("Mode ({:?}) is not compatible with all given connectors", mode),

View File

@ -183,20 +183,27 @@
//! ``` //! ```
use backend::graphics::egl::{EGLContext, GlAttributes, PixelFormatRequirements}; 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::Device as BasicDevice;
use drm::control::{connector, crtc, encoder, Mode, ResourceInfo}; use drm::control::{connector, crtc, encoder, Mode, ResourceInfo};
use drm::control::Device as ControlDevice; use drm::control::Device as ControlDevice;
use drm::result::Error as DrmError; use drm::result::Error as DrmError;
use gbm::Device as GbmDevice; use gbm::Device as GbmDevice;
use nix; use nix;
use nix::Result as NixResult;
use nix::unistd::close;
use std::borrow::Borrow; use std::borrow::Borrow;
use std::collections::HashMap; use std::collections::HashMap;
use std::fs::File; use std::fs::File;
use std::hash::{Hash, Hasher};
use std::io::Result as IoResult; 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::rc::Rc;
use std::mem;
use std::time::Duration; use std::time::Duration;
use wayland_server::{EventLoopHandle, StateToken}; use wayland_server::{EventLoopHandle, StateToken, StateProxy};
use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest}; use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest};
mod backend; mod backend;
@ -207,11 +214,11 @@ use self::error::*;
/// Internal struct as required by the drm crate /// Internal struct as required by the drm crate
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct DrmDev(File); pub(crate) struct DrmDev(RawFd);
impl AsRawFd for DrmDev { impl AsRawFd for DrmDev {
fn as_raw_fd(&self) -> RawFd { fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd() self.0
} }
} }
impl BasicDevice for DrmDev {} impl BasicDevice for DrmDev {}
@ -219,12 +226,11 @@ impl ControlDevice for DrmDev {}
impl DrmDev { impl DrmDev {
unsafe fn new_from_fd(fd: RawFd) -> Self { unsafe fn new_from_fd(fd: RawFd) -> Self {
use std::os::unix::io::FromRawFd; DrmDev(fd)
DrmDev(File::from_raw_fd(fd))
} }
fn new_from_file(file: File) -> Self { 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<B: Borrow<DrmBackend> + 'static> { pub struct DrmDevice<B: Borrow<DrmBackend> + 'static> {
context: Rc<Context>, context: Rc<Context>,
backends: HashMap<crtc::Handle, StateToken<B>>, backends: HashMap<crtc::Handle, StateToken<B>>,
active: bool,
logger: ::slog::Logger, logger: ::slog::Logger,
} }
@ -351,6 +358,9 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static> DrmDevice<B> {
info!(log, "DrmDevice initializing"); 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 // Open the gbm device from the drm device and create a context based on that
Ok(DrmDevice { Ok(DrmDevice {
context: Rc::new(Context::try_new( context: Rc::new(Context::try_new(
@ -374,6 +384,7 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static> DrmDevice<B> {
}, },
)?), )?),
backends: HashMap::new(), backends: HashMap::new(),
active: true,
logger: log, logger: log,
}) })
} }
@ -383,16 +394,20 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static> DrmDevice<B> {
/// ///
/// Errors if initialization fails or the mode is not available on all given /// Errors if initialization fails or the mode is not available on all given
/// connectors. /// connectors.
pub fn create_backend<I>(&mut self, evlh: &mut EventLoopHandle, crtc: crtc::Handle, mode: Mode, pub fn create_backend<'a, I, S>(&mut self, state: S, crtc: crtc::Handle, mode: Mode, connectors: I)
connectors: I) -> Result<&StateToken<B>>
-> Result<StateToken<B>>
where where
I: Into<Vec<connector::Handle>>, I: Into<Vec<connector::Handle>>,
S: Into<StateProxy<'a>>,
{ {
if self.backends.contains_key(&crtc) { if self.backends.contains_key(&crtc) {
bail!(ErrorKind::CrtcAlreadyInUse(crtc)); bail!(ErrorKind::CrtcAlreadyInUse(crtc));
} }
if !self.active {
bail!(ErrorKind::DeviceInactive);
}
// check if the given connectors and crtc match // check if the given connectors and crtc match
let connectors = connectors.into(); let connectors = connectors.into();
@ -437,10 +452,32 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static> DrmDevice<B> {
let logger = self.logger.new(o!("crtc" => format!("{:?}", crtc))); let logger = self.logger.new(o!("crtc" => format!("{:?}", crtc)));
let backend = DrmBackend::new(self.context.clone(), crtc, mode, connectors, logger)?; let backend = DrmBackend::new(self.context.clone(), crtc, mode, connectors, logger)?;
let token = evlh.state().insert(backend.into()); self.backends.insert(crtc, state.into().insert(backend.into()));
self.backends.insert(crtc, token.clone());
Ok(token) Ok(self.backends.get(&crtc).unwrap())
}
pub fn backend_for_crtc(&self, crtc: &crtc::Handle) -> Option<&StateToken<B>> {
self.backends.get(crtc)
}
pub fn current_backends(&self) -> Vec<&StateToken<B>> {
self.backends.values().collect()
}
pub fn destroy_backend<'a, S>(&mut self, state: S, crtc: &crtc::Handle)
where
S: Into<StateProxy<'a>>
{
if let Some(token) = self.backends.remove(crtc) {
state.into().remove(token);
}
}
pub fn close(self) -> NixResult<()> {
let fd = self.as_raw_fd();
mem::drop(self);
close(fd)
} }
} }
@ -450,9 +487,27 @@ impl<B: Borrow<DrmBackend> + 'static> AsRawFd for DrmDevice<B> {
self.context.head().head().as_raw_fd() self.context.head().head().as_raw_fd()
} }
} }
impl<B: Borrow<DrmBackend> + 'static> BasicDevice for DrmDevice<B> {} impl<B: Borrow<DrmBackend> + 'static> BasicDevice for DrmDevice<B> {}
impl<B: Borrow<DrmBackend> + 'static> ControlDevice for DrmDevice<B> {} impl<B: Borrow<DrmBackend> + 'static> ControlDevice for DrmDevice<B> {}
impl<B: Borrow<DrmBackend> + 'static> Drop for DrmDevice<B> {
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<B: Borrow<DrmBackend> + 'static> Hash for DrmDevice<B> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_raw_fd().hash(state)
}
}
/// Handler for drm node events /// Handler for drm node events
/// ///
/// See module-level documentation for its use /// See module-level documentation for its use
@ -462,61 +517,99 @@ pub trait DrmHandler<B: Borrow<DrmBackend> + 'static> {
/// ///
/// The `id` argument is the `Id` of the `DrmBackend` that finished rendering, /// The `id` argument is the `Id` of the `DrmBackend` that finished rendering,
/// check using `DrmBackend::is`. /// check using `DrmBackend::is`.
fn ready(&mut self, evlh: &mut EventLoopHandle, device: &mut DrmDevice<B>, backend: &StateToken<B>, fn ready<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &mut DrmDevice<B>, backend: &StateToken<B>,
frame: u32, duration: Duration); crtc: crtc::Handle, frame: u32, duration: Duration);
/// The `DrmDevice` has thrown an error. /// The `DrmDevice` has thrown an error.
/// ///
/// The related backends are most likely *not* usable anymore and /// The related backends are most likely *not* usable anymore and
/// the whole stack has to be recreated. /// the whole stack has to be recreated.
fn error(&mut self, evlh: &mut EventLoopHandle, device: &mut DrmDevice<B>, error: DrmError); fn error<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &mut DrmDevice<B>, error: DrmError);
} }
/// Bind a `DrmDevice` to an `EventLoop`, /// Bind a `DrmDevice` to an `EventLoop`,
/// ///
/// This will cause it to recieve events and feed them into an `DrmHandler` /// This will cause it to recieve events and feed them into an `DrmHandler`
pub fn drm_device_bind<B, H>(evlh: &mut EventLoopHandle, device: DrmDevice<B>, handler: H) pub fn drm_device_bind<B, H>(evlh: &mut EventLoopHandle, device: StateToken<DrmDevice<B>>, handler: H)
-> IoResult<FdEventSource<(DrmDevice<B>, H)>> -> IoResult<FdEventSource<(StateToken<DrmDevice<B>>, H)>>
where where
B: Borrow<DrmBackend> + 'static, B: From<DrmBackend> + Borrow<DrmBackend> + 'static,
H: DrmHandler<B> + 'static, H: DrmHandler<B> + 'static,
{ {
let fd = evlh.state().get(&device).as_raw_fd();
evlh.add_fd_event_source( evlh.add_fd_event_source(
device.as_raw_fd(), fd,
fd_event_source_implementation(), fd_event_source_implementation(),
(device, handler), (device, handler),
FdInterest::READ, FdInterest::READ,
) )
} }
fn fd_event_source_implementation<B, H>() -> FdEventSourceImpl<(DrmDevice<B>, H)> fn fd_event_source_implementation<B, H>() -> FdEventSourceImpl<(StateToken<DrmDevice<B>>, H)>
where where
B: Borrow<DrmBackend> + 'static, B: From<DrmBackend> + Borrow<DrmBackend> + 'static,
H: DrmHandler<B> + 'static, H: DrmHandler<B> + 'static,
{ {
FdEventSourceImpl { FdEventSourceImpl {
ready: |evlh, id, _, _| { ready: |evlh, &mut (ref mut dev_token, ref mut handler), _, _| {
let &mut (ref mut dev, ref mut handler) = id; let (events, logger) = {
let dev = evlh.state().get(dev_token);
let events = crtc::receive_events(dev); let events = crtc::receive_events(dev);
let logger = dev.logger.clone();
(events, logger)
};
match events { match events {
Ok(events) => for event in events { Ok(events) => for event in events {
if let crtc::Event::PageFlip(event) = event { if let crtc::Event::PageFlip(event) = event {
let token = dev.backends.get(&event.crtc).cloned(); evlh.state().with_value(dev_token, |state, mut dev| {
if let Some(token) = token { if dev.active {
if let Some(backend_token) = dev.backend_for_crtc(&event.crtc).cloned() {
// we can now unlock the buffer // we can now unlock the buffer
evlh.state().get(&token).borrow().unlock_buffer(); state.get(&backend_token).borrow().unlock_buffer();
trace!(dev.logger, "Handling event for backend {:?}", event.crtc); trace!(logger, "Handling event for backend {:?}", event.crtc);
// and then call the user to render the next frame // and then call the user to render the next frame
handler.ready(evlh, dev, &token, event.frame, event.duration); 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| { error: |evlh, &mut (ref mut dev_token, ref mut handler), _, error| {
warn!(id.0.logger, "DrmDevice errored: {}", error); evlh.state().with_value(dev_token, |state, mut dev| {
id.1.error(evlh, &mut id.0, error.into()); warn!(dev.logger, "DrmDevice errored: {}", error);
handler.error(state, &mut dev, error.into());
})
}, },
} }
} }
#[cfg(feature = "backend_session")]
impl<B: Borrow<DrmBackend> + 'static> SessionObserver for StateToken<DrmDevice<B>> {
fn pause<'a>(&mut self, state: &mut StateProxy<'a>) {
let device: &mut DrmDevice<B> = 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);
}
}
})
}
}

View File

@ -817,7 +817,7 @@ impl<'context, 'surface, T: NativeSurface> EGLSurface<'context, 'surface, T> {
if ret == 0 { if ret == 0 {
match unsafe { self.context.egl.GetError() } as u32 { match unsafe { self.context.egl.GetError() } as u32 {
ffi::egl::CONTEXT_LOST => Err(SwapBuffersError::ContextLost), ffi::egl::CONTEXT_LOST => Err(SwapBuffersError::ContextLost),
err => panic!("eglSwapBuffers failed (eglGetError returned 0x{:x})", err), err => Err(SwapBuffersError::Unknown(err)),
} }
} else { } else {
Ok(()) Ok(())
@ -847,6 +847,12 @@ impl<'context, 'surface, T: NativeSurface> EGLSurface<'context, 'surface, T> {
Ok(()) 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> {} 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 /// This error can be returned when `swap_buffers` has been called multiple times
/// without any modification in between. /// without any modification in between.
AlreadySwapped, AlreadySwapped,
/// Unknown GL error
Unknown(u32),
} }
impl fmt::Display for SwapBuffersError { 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::ContextLost => "The context has been lost, it needs to be recreated",
SwapBuffersError::AlreadySwapped => { SwapBuffersError::AlreadySwapped => {
"Buffers are already swapped, swap_buffers was called too many times" "Buffers are already swapped, swap_buffers was called too many times"
},
SwapBuffersError::Unknown(_) => {
"Unknown Open GL error occurred"
} }
} }
} }

View File

@ -14,6 +14,7 @@ impl From<SwapBuffersError> for GliumSwapBuffersError {
match error { match error {
SwapBuffersError::ContextLost => GliumSwapBuffersError::ContextLost, SwapBuffersError::ContextLost => GliumSwapBuffersError::ContextLost,
SwapBuffersError::AlreadySwapped => GliumSwapBuffersError::AlreadySwapped, SwapBuffersError::AlreadySwapped => GliumSwapBuffersError::AlreadySwapped,
SwapBuffersError::Unknown(_) => GliumSwapBuffersError::ContextLost, // TODO
} }
} }
} }

View File

@ -1,6 +1,5 @@
//! Common traits for input backends to receive input from. //! Common traits for input backends to receive input from.
use backend::{SeatInternal, TouchSlotInternal};
use std::error::Error; use std::error::Error;
/// A seat describes a group of input devices and at least one /// A seat describes a group of input devices and at least one
@ -20,20 +19,18 @@ pub struct Seat {
capabilities: SeatCapabilities, capabilities: SeatCapabilities,
} }
impl SeatInternal for Seat { impl Seat {
fn new(id: u64, capabilities: SeatCapabilities) -> Seat { pub(crate) fn new(id: u64, capabilities: SeatCapabilities) -> Seat {
Seat { Seat {
id: id, id: id,
capabilities: capabilities, capabilities: capabilities,
} }
} }
fn capabilities_mut(&mut self) -> &mut SeatCapabilities { pub(crate) fn capabilities_mut(&mut self) -> &mut SeatCapabilities {
&mut self.capabilities &mut self.capabilities
} }
}
impl Seat {
/// Get the currently capabilities of this `Seat` /// Get the currently capabilities of this `Seat`
pub fn capabilities(&self) -> &SeatCapabilities { pub fn capabilities(&self) -> &SeatCapabilities {
&self.capabilities &self.capabilities
@ -318,8 +315,8 @@ pub struct TouchSlot {
id: u64, id: u64,
} }
impl TouchSlotInternal for TouchSlot { impl TouchSlot {
fn new(id: u64) -> Self { pub(crate) fn new(id: u64) -> Self {
TouchSlot { id: id } TouchSlot { id: id }
} }
} }

View File

@ -1,6 +1,5 @@
//! Implementation of input backend trait for types provided by `libinput` //! Implementation of input backend trait for types provided by `libinput`
use backend::{SeatInternal, TouchSlotInternal};
use backend::input as backend; use backend::input as backend;
use input as libinput; use input as libinput;
use input::event; use input::event;

View File

@ -23,14 +23,7 @@ pub mod winit;
pub mod drm; pub mod drm;
#[cfg(feature = "backend_libinput")] #[cfg(feature = "backend_libinput")]
pub mod libinput; pub mod libinput;
#[cfg(feature = "backend_session")]
// Internal functions that need to be accessible by the different backend implementations pub mod session;
#[cfg(feature = "backend_udev")]
trait SeatInternal { pub mod udev;
fn new(id: u64, capabilities: input::SeatCapabilities) -> Self;
fn capabilities_mut(&mut self) -> &mut input::SeatCapabilities;
}
trait TouchSlotInternal {
fn new(id: u64) -> Self;
}

View File

@ -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<AtomicBool>,
vt: i32,
old_keyboard_mode: i32,
logger: ::slog::Logger,
}
pub struct DirectSessionNotifier {
active: Arc<AtomicBool>,
signals: Vec<Option<Box<SessionObserver>>>,
signal: Signal,
logger: ::slog::Logger,
}
impl DirectSession {
pub fn new<L>(tty: Option<&Path>, logger: L) -> Result<(DirectSession, DirectSessionNotifier)>
where
L: Into<Option<::slog::Logger>>
{
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("<stdin>"))))?;
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<RawFd> {
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<S: SessionObserver + 'static>(&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<L>(notifier: DirectSessionNotifier, evlh: &mut EventLoopHandle, _logger: L)
-> IoResult<SignalEventSource<DirectSessionNotifier>>
where
L: Into<Option<::slog::Logger>>,
{
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),
}
}
}

View File

View File

@ -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<RawFd, Self::Error>;
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<S: SessionObserver + 'static>(&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<RawFd, Self::Error> { 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;

348
src/backend/udev.rs Normal file
View File

@ -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<B: Borrow<DrmBackend> + 'static, H: DrmHandler<B> + 'static, S: Session + 'static, T: UdevHandler<B, H> + 'static> {
devices: HashMap<dev_t, (StateToken<DrmDevice<B>>, FdEventSource<(StateToken<DrmDevice<B>>, H)>)>,
monitor: MonitorSocket,
session: StateToken<S>,
handler: T,
logger: ::slog::Logger,
}
impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static, H: DrmHandler<B> + 'static, S: Session + 'static, T: UdevHandler<B, H> + 'static> UdevBackend<B, H, S, T> {
pub fn new<'a, L>(mut evlh: &mut EventLoopHandle,
context: &Context,
session_token: &StateToken<S>,
mut handler: T,
logger: L)
-> Result<UdevBackend<B, H, S, T>>
where
L: Into<Option<::slog::Logger>>
{
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::<HashMap<dev_t, (StateToken<DrmDevice<B>>, FdEventSource<(StateToken<DrmDevice<B>>, 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<StateProxy<'a>>>(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<B: Borrow<DrmBackend> + 'static, H: DrmHandler<B> + 'static, S: Session + 'static, T: UdevHandler<B, H> + 'static> SessionObserver for StateToken<UdevBackend<B, H, S, T>> {
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<B, S, H, T>(evlh: &mut EventLoopHandle, udev: StateToken<UdevBackend<B, H, S, T>>)
-> IoResult<FdEventSource<StateToken<UdevBackend<B, H, S, T>>>>
where
B: From<DrmBackend> + Borrow<DrmBackend> + 'static,
H: DrmHandler<B> + 'static,
T: UdevHandler<B, H> + '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<B, S, H, T>()
-> FdEventSourceImpl<StateToken<UdevBackend<B, H, S, T>>>
where
B: From<DrmBackend> + Borrow<DrmBackend> + 'static,
H: DrmHandler<B> + 'static,
T: UdevHandler<B, H> + 'static,
S: Session + 'static,
{
FdEventSourceImpl {
ready: |mut evlh, token, _, _| {
let events = evlh.state().get(token).monitor.clone().collect::<Vec<Event>>();
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<B: Borrow<DrmBackend> + 'static, H: DrmHandler<B> + 'static> {
fn device_added<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &mut DrmDevice<B>) -> Option<H>;
fn device_changed<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &StateToken<DrmDevice<B>>);
fn device_removed<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &StateToken<DrmDevice<B>>);
fn error<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, error: IoError);
}
pub fn primary_gpu<S: AsRef<str>>(context: &Context, seat: S) -> UdevResult<Option<PathBuf>> {
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<S: AsRef<str>>(context: &Context, seat: S) -> UdevResult<Vec<PathBuf>> {
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"),
}
}
}

View File

@ -1,6 +1,5 @@
//! Implementation of backend traits for types provided by `winit` //! Implementation of backend traits for types provided by `winit`
use backend::{SeatInternal, TouchSlotInternal};
use backend::graphics::GraphicsBackend; use backend::graphics::GraphicsBackend;
use backend::graphics::egl::{self, EGLContext, EGLGraphicsBackend, GlAttributes, PixelFormat, use backend::graphics::egl::{self, EGLContext, EGLGraphicsBackend, GlAttributes, PixelFormat,
PixelFormatRequirements, SwapBuffersError}; PixelFormatRequirements, SwapBuffersError};
@ -229,7 +228,7 @@ impl EGLGraphicsBackend for WinitGraphicsBackend {
} }
fn is_current(&self) -> bool { 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> { unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {

View File

@ -8,6 +8,7 @@
#![recursion_limit = "1024"] #![recursion_limit = "1024"]
extern crate image; extern crate image;
#[macro_use]
extern crate nix; extern crate nix;
#[macro_use] #[macro_use]
extern crate rental; extern crate rental;
@ -22,6 +23,8 @@ extern crate drm;
extern crate gbm; extern crate gbm;
#[cfg(feature = "backend_libinput")] #[cfg(feature = "backend_libinput")]
extern crate input; extern crate input;
#[cfg(feature = "backend_udev")]
extern crate libudev;
#[cfg(feature = "backend_winit")] #[cfg(feature = "backend_winit")]
extern crate wayland_client; extern crate wayland_client;
#[cfg(feature = "backend_winit")] #[cfg(feature = "backend_winit")]