Udev backend
This commit is contained in:
parent
c31d966fd4
commit
c7682e77de
15
Cargo.toml
15
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"]
|
||||
|
|
|
@ -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,11 +81,20 @@ 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()])
|
||||
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<GliumDrawer<DrmBackend>> for DrmHandlerImpl {
|
||||
fn ready(&mut self, evlh: &mut EventLoopHandle, _device: &mut DrmDevice<GliumDrawer<DrmBackend>>,
|
||||
backend: &StateToken<GliumDrawer<DrmBackend>>, _frame: u32, _duration: Duration) {
|
||||
fn ready(&mut self, evlh: &mut EventLoopHandle, device: &StateToken<DrmDevice<GliumDrawer<DrmBackend>>>,
|
||||
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<GliumDrawer<DrmBackend>> for DrmHandlerImpl {
|
|||
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) {
|
||||
panic!("{:?}", error);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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> {
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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<B: Borrow<DrmBackend> + 'static> {
|
||||
context: Rc<Context>,
|
||||
backends: HashMap<crtc::Handle, StateToken<B>>,
|
||||
active: bool,
|
||||
logger: ::slog::Logger,
|
||||
}
|
||||
|
||||
|
@ -351,6 +358,9 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static> DrmDevice<B> {
|
|||
|
||||
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<B: From<DrmBackend> + Borrow<DrmBackend> + 'static> DrmDevice<B> {
|
|||
},
|
||||
)?),
|
||||
backends: HashMap::new(),
|
||||
active: true,
|
||||
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
|
||||
/// connectors.
|
||||
pub fn create_backend<I>(&mut self, evlh: &mut EventLoopHandle, crtc: crtc::Handle, mode: Mode,
|
||||
connectors: I)
|
||||
-> Result<StateToken<B>>
|
||||
pub fn create_backend<'a, I, S>(&mut self, state: S, crtc: crtc::Handle, mode: Mode, connectors: I)
|
||||
-> Result<&StateToken<B>>
|
||||
where
|
||||
I: Into<Vec<connector::Handle>>,
|
||||
S: Into<StateProxy<'a>>,
|
||||
{
|
||||
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<B: From<DrmBackend> + Borrow<DrmBackend> + 'static> DrmDevice<B> {
|
|||
|
||||
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<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()
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Borrow<DrmBackend> + 'static> BasicDevice 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
|
||||
///
|
||||
/// 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,
|
||||
/// check using `DrmBackend::is`.
|
||||
fn ready(&mut self, evlh: &mut EventLoopHandle, device: &mut DrmDevice<B>, backend: &StateToken<B>,
|
||||
frame: u32, duration: Duration);
|
||||
fn ready<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &mut DrmDevice<B>, backend: &StateToken<B>,
|
||||
crtc: crtc::Handle, frame: u32, duration: Duration);
|
||||
/// The `DrmDevice` has thrown an error.
|
||||
///
|
||||
/// The 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<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`,
|
||||
///
|
||||
/// 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)
|
||||
-> IoResult<FdEventSource<(DrmDevice<B>, H)>>
|
||||
pub fn drm_device_bind<B, H>(evlh: &mut EventLoopHandle, device: StateToken<DrmDevice<B>>, handler: H)
|
||||
-> IoResult<FdEventSource<(StateToken<DrmDevice<B>>, H)>>
|
||||
where
|
||||
B: Borrow<DrmBackend> + 'static,
|
||||
B: From<DrmBackend> + Borrow<DrmBackend> + 'static,
|
||||
H: DrmHandler<B> + '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<B, H>() -> FdEventSourceImpl<(DrmDevice<B>, H)>
|
||||
fn fd_event_source_implementation<B, H>() -> FdEventSourceImpl<(StateToken<DrmDevice<B>>, H)>
|
||||
where
|
||||
B: Borrow<DrmBackend> + 'static,
|
||||
B: From<DrmBackend> + Borrow<DrmBackend> + 'static,
|
||||
H: DrmHandler<B> + '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)
|
||||
};
|
||||
|
||||
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 {
|
||||
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
|
||||
evlh.state().get(&token).borrow().unlock_buffer();
|
||||
trace!(dev.logger, "Handling event for backend {:?}", event.crtc);
|
||||
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(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| {
|
||||
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<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);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ impl From<SwapBuffersError> for GliumSwapBuffersError {
|
|||
match error {
|
||||
SwapBuffersError::ContextLost => GliumSwapBuffersError::ContextLost,
|
||||
SwapBuffersError::AlreadySwapped => GliumSwapBuffersError::AlreadySwapped,
|
||||
SwapBuffersError::Unknown(_) => GliumSwapBuffersError::ContextLost, // TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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"),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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> {
|
||||
|
|
|
@ -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")]
|
||||
|
|
Loading…
Reference in New Issue