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 }
|
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"]
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
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> {
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -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`
|
//! 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> {
|
||||||
|
|
|
@ -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")]
|
||||||
|
|
Loading…
Reference in New Issue