Get input working

This commit is contained in:
Drakulix 2017-11-07 01:18:52 +01:00
parent c7682e77de
commit 96bb3570ba
11 changed files with 393 additions and 76 deletions

View File

@ -19,7 +19,7 @@ winit = { version = "0.8.2", optional = true }
drm = { version = "^0.3.0", optional = true } drm = { version = "^0.3.0", optional = true }
gbm = { version = "^0.3.0", 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.3.0", optional = true } input = { version = "0.4.0", git = "https://github.com/Smithay/input.rs.git", branch = "feature/udev", optional = true }
libudev = { git = "https://github.com/Drakulix/libudev-rs.git", branch = "feature/raw_ffi_access", optional = true } 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"] }

Binary file not shown.

Binary file not shown.

View File

@ -2,6 +2,8 @@ extern crate drm;
#[macro_use] #[macro_use]
extern crate glium; extern crate glium;
extern crate rand; extern crate rand;
extern crate input as libinput;
extern crate image;
extern crate libudev; extern crate libudev;
#[macro_use(define_roles)] #[macro_use(define_roles)]
extern crate smithay; extern crate smithay;
@ -22,15 +24,24 @@ use drm::control::encoder::Info as EncoderInfo;
use drm::control::crtc; use drm::control::crtc;
use drm::result::Error as DrmError; use drm::result::Error as DrmError;
use glium::Surface; use glium::Surface;
use image::{ImageBuffer, Rgba};
use libinput::{Libinput, Device as LibinputDevice, event};
use libinput::event::keyboard::KeyboardEventTrait;
use helpers::{init_shell, GliumDrawer, MyWindowMap, Roles, SurfaceData}; use helpers::{init_shell, GliumDrawer, MyWindowMap, Roles, SurfaceData};
use slog::{Drain, Logger}; use slog::{Drain, Logger};
use smithay::backend::drm::{DrmBackend, DrmDevice, DrmHandler}; use smithay::backend::drm::{DrmBackend, DrmDevice, DrmHandler};
use smithay::backend::graphics::GraphicsBackend;
use smithay::backend::graphics::egl::EGLGraphicsBackend; use smithay::backend::graphics::egl::EGLGraphicsBackend;
use smithay::backend::input::{self, Event, InputBackend, InputHandler, KeyboardKeyEvent, KeyState, PointerButtonEvent,
PointerAxisEvent};
use smithay::backend::libinput::{LibinputInputBackend, libinput_bind, PointerAxisEvent as LibinputPointerAxisEvent, LibinputSessionInterface};
use smithay::backend::udev::{UdevBackend, UdevHandler, udev_backend_bind}; use smithay::backend::udev::{UdevBackend, UdevHandler, udev_backend_bind};
use smithay::backend::session::SessionNotifier; use smithay::backend::session::{Session, SessionNotifier};
use smithay::backend::session::direct::{direct_session_bind, DirectSession}; use smithay::backend::session::direct::{direct_session_bind, DirectSession};
use smithay::wayland::compositor::{CompositorToken, SubsurfaceRole, TraversalAction}; use smithay::wayland::compositor::{CompositorToken, SubsurfaceRole, TraversalAction};
use smithay::wayland::compositor::roles::Role; use smithay::wayland::compositor::roles::Role;
use smithay::wayland::output::{Mode, Output, PhysicalProperties};
use smithay::wayland::seat::{KeyboardHandle, PointerHandle, Seat};
use smithay::wayland::shell::ShellState; use smithay::wayland::shell::ShellState;
use smithay::wayland::shm::init_shm_global; use smithay::wayland::shm::init_shm_global;
use std::cell::RefCell; use std::cell::RefCell;
@ -38,9 +49,121 @@ use std::collections::HashSet;
use std::io::Error as IoError; use std::io::Error as IoError;
use std::rc::Rc; use std::rc::Rc;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc; use std::sync::{Arc, Mutex};
use std::time::Duration; use std::time::Duration;
use wayland_server::{StateToken, StateProxy}; use wayland_server::{StateToken, StateProxy};
use wayland_server::protocol::{wl_output, wl_pointer};
struct LibinputInputHandler {
log: Logger,
pointer: PointerHandle,
keyboard: KeyboardHandle,
window_map: Rc<RefCell<MyWindowMap>>,
pointer_location: Arc<Mutex<(f64, f64)>>,
screen_size: (u32, u32),
serial: u32,
running: Arc<AtomicBool>,
}
impl LibinputInputHandler {
fn next_serial(&mut self) -> u32 {
self.serial += 1;
self.serial
}
}
impl InputHandler<LibinputInputBackend> for LibinputInputHandler {
fn on_seat_created(&mut self, _: &input::Seat) {
/* we just create a single static one */
}
fn on_seat_destroyed(&mut self, _: &input::Seat) {
/* we just create a single static one */
}
fn on_seat_changed(&mut self, _: &input::Seat) {
/* we just create a single static one */
}
fn on_keyboard_key(&mut self, _: &input::Seat, evt: event::keyboard::KeyboardKeyEvent) {
let keycode = evt.key();
let state = evt.state();
debug!(self.log, "key"; "keycode" => keycode, "state" => format!("{:?}", state));
match (keycode, state) {
(1 /*ESC*/, KeyState::Pressed) => {
self.running.store(false, Ordering::SeqCst);
},
(keycode, state) => {
let serial = self.next_serial();
self.keyboard.input(keycode, state, serial, |_, _| true);
}
}
}
fn on_pointer_move(&mut self, _: &input::Seat, evt: event::pointer::PointerMotionEvent) {
let (x, y) = (evt.dx(), evt.dy());
let serial = self.next_serial();
let mut location = self.pointer_location.lock().unwrap();
location.0 += x;
location.1 += y;
let under = self.window_map.borrow().get_surface_under((location.0, location.1));
self.pointer.motion(
under.as_ref().map(|&(ref s, (x, y))| (s, x, y)),
serial,
evt.time(),
);
}
fn on_pointer_move_absolute(&mut self, _: &input::Seat, evt: event::pointer::PointerMotionAbsoluteEvent) {
let (x, y) = (evt.absolute_x_transformed(self.screen_size.0), evt.absolute_y_transformed(self.screen_size.1));
*self.pointer_location.lock().unwrap() = (x, y);
let serial = self.next_serial();
let under = self.window_map.borrow().get_surface_under((x, y));
self.pointer.motion(
under.as_ref().map(|&(ref s, (x, y))| (s, x, y)),
serial,
evt.time(),
);
}
fn on_pointer_button(&mut self, _: &input::Seat, evt: event::pointer::PointerButtonEvent) {
let serial = self.next_serial();
let button = evt.button();
let state = match evt.state() {
input::MouseButtonState::Pressed => {
// change the keyboard focus
let under = self.window_map
.borrow_mut()
.get_surface_and_bring_to_top(*self.pointer_location.lock().unwrap());
self.keyboard
.set_focus(under.as_ref().map(|&(ref s, _)| s), serial);
wl_pointer::ButtonState::Pressed
}
input::MouseButtonState::Released => wl_pointer::ButtonState::Released,
};
self.pointer.button(button, state, serial, evt.time());
}
fn on_pointer_axis(&mut self, _: &input::Seat, evt: LibinputPointerAxisEvent) {
let axis = match evt.axis() {
input::Axis::Vertical => wayland_server::protocol::wl_pointer::Axis::VerticalScroll,
input::Axis::Horizontal => wayland_server::protocol::wl_pointer::Axis::HorizontalScroll,
};
self.pointer.axis(axis, evt.amount(), evt.time());
}
fn on_touch_down(&mut self, _: &input::Seat, _: event::touch::TouchDownEvent) {
/* not done in this example */
}
fn on_touch_motion(&mut self, _: &input::Seat, _: event::touch::TouchMotionEvent) {
/* not done in this example */
}
fn on_touch_up(&mut self, _: &input::Seat, _: event::touch::TouchUpEvent) {
/* not done in this example */
}
fn on_touch_cancel(&mut self, _: &input::Seat, _: event::touch::TouchCancelEvent) {
/* not done in this example */
}
fn on_touch_frame(&mut self, _: &input::Seat, _: event::touch::TouchFrameEvent) {
/* not done in this example */
}
fn on_input_config_changed(&mut self, _: &mut [LibinputDevice]) {
/* not done in this example */
}
}
fn main() { fn main() {
// A logger facility, here we use the terminal for this example // A logger facility, here we use the terminal for this example
@ -63,7 +186,7 @@ fn main() {
* Initialize session on the current tty * Initialize session on the current tty
*/ */
let (session, mut notifier) = DirectSession::new(None, log.clone()).unwrap(); let (session, mut notifier) = DirectSession::new(None, log.clone()).unwrap();
let session_token = event_loop.state().insert(session); let session = Arc::new(Mutex::new(session));
let running = Arc::new(AtomicBool::new(true)); let running = Arc::new(AtomicBool::new(true));
let r = running.clone(); let r = running.clone();
@ -71,48 +194,124 @@ fn main() {
r.store(false, Ordering::SeqCst); r.store(false, Ordering::SeqCst);
}).expect("Error setting Ctrl-C handler"); }).expect("Error setting Ctrl-C handler");
let pointer_location = Arc::new(Mutex::new((0.0, 0.0)));
/* /*
* Initialize the udev backend * Initialize the udev backend
*/ */
let context = libudev::Context::new().unwrap(); let context = libudev::Context::new().unwrap();
let bytes = include_bytes!("resources/cursor2.rgba");
let udev let udev
= UdevBackend::new(&mut event_loop, &context, &session_token, UdevHandlerImpl { = UdevBackend::new(&mut event_loop, &context, session.clone(), UdevHandlerImpl {
shell_state_token, shell_state_token,
compositor_token, compositor_token,
window_map: window_map.clone(), window_map: window_map.clone(),
pointer_location: pointer_location.clone(),
pointer_image: ImageBuffer::from_raw(64, 64, bytes.to_vec()).unwrap(),
logger: log.clone(), logger: log.clone(),
}, log.clone()).unwrap(); }, log.clone()).unwrap();
let udev_token = event_loop.state().insert(udev); let udev_token = event_loop.state().insert(udev);
let udev_session_id = notifier.register(udev_token.clone()); let udev_session_id = notifier.register(udev_token.clone());
let (seat_token, _) = Seat::new(&mut event_loop, session.seat().into(), log.clone());
let pointer = event_loop.state().get_mut(&seat_token).add_pointer();
let keyboard = event_loop
.state()
.get_mut(&seat_token)
.add_keyboard("", "", "", None, 1000, 500)
.expect("Failed to initialize the keyboard");
let (output_token, _output_global) = Output::new(
&mut event_loop,
"Drm".into(),
PhysicalProperties {
width: 0,
height: 0,
subpixel: wl_output::Subpixel::Unknown,
maker: "Smithay".into(),
model: "Generic DRM".into(),
},
log.clone(),
);
let (w, h) = (1920, 1080); // Hardcode full-hd res
event_loop
.state()
.get_mut(&output_token)
.change_current_state(
Some(Mode {
width: w as i32,
height: h as i32,
refresh: 60_000,
}),
None,
None,
);
event_loop
.state()
.get_mut(&output_token)
.set_preferred(Mode {
width: w as i32,
height: h as i32,
refresh: 60_000,
});
/*
* Initialize libinput backend
*/
let seat = session.seat();
let mut libinput_context = Libinput::new_from_udev::<LibinputSessionInterface<Arc<Mutex<DirectSession>>>>(session.into(), &context);
let libinput_session_id = notifier.register(libinput_context.clone());
libinput_context.udev_assign_seat(&seat);
let mut libinput_backend = LibinputInputBackend::new(libinput_context, log.clone());
libinput_backend.set_handler(LibinputInputHandler {
log: log.clone(),
pointer,
keyboard,
window_map: window_map.clone(),
pointer_location,
screen_size: (w, h),
serial: 0,
running: running.clone(),
});
let libinput_event_source = libinput_bind(libinput_backend, &mut event_loop).unwrap();
let session_event_source = direct_session_bind(notifier, &mut event_loop, log.clone()).unwrap(); let 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(); let udev_event_source = udev_backend_bind(&mut event_loop, udev_token).unwrap();
/* /*
* Add a listening socket: * Add a listening socket
*/ */
let name = display.add_socket_auto().unwrap().into_string().unwrap(); let name = display.add_socket_auto().unwrap().into_string().unwrap();
println!("Listening on socket: {}", name); println!("Listening on socket: {}", name);
while running.load(Ordering::SeqCst) { while running.load(Ordering::SeqCst) {
event_loop.dispatch(Some(16)).unwrap(); event_loop.dispatch(Some(16));
display.flush_clients(); display.flush_clients();
window_map.borrow_mut().refresh(); window_map.borrow_mut().refresh();
} }
println!("Bye Bye");
let mut notifier = session_event_source.remove(); let mut notifier = session_event_source.remove();
notifier.unregister(udev_session_id); notifier.unregister(udev_session_id);
notifier.unregister(libinput_session_id);
libinput_event_source.remove();
let udev_token = udev_event_source.remove(); let udev_token = udev_event_source.remove();
let udev = event_loop.state().remove(udev_token); let udev = event_loop.state().remove(udev_token);
udev.close(event_loop.state()); udev.close(event_loop.state());
event_loop.state().remove(session_token);
} }
struct UdevHandlerImpl { struct UdevHandlerImpl {
shell_state_token: StateToken<ShellState<SurfaceData, Roles, (), ()>>, shell_state_token: StateToken<ShellState<SurfaceData, Roles, (), ()>>,
compositor_token: CompositorToken<SurfaceData, Roles, ()>, compositor_token: CompositorToken<SurfaceData, Roles, ()>,
window_map: Rc<RefCell<MyWindowMap>>, window_map: Rc<RefCell<MyWindowMap>>,
pointer_location: Arc<Mutex<(f64, f64)>>,
pointer_image: ImageBuffer<Rgba<u8>, Vec<u8>>,
logger: ::slog::Logger, logger: ::slog::Logger,
} }
@ -146,6 +345,12 @@ impl UdevHandlerImpl {
// create a backend // create a backend
let renderer_token = device.create_backend(&mut state, crtc, mode, vec![connector_info.handle()]).unwrap(); let renderer_token = device.create_backend(&mut state, crtc, mode, vec![connector_info.handle()]).unwrap();
// create cursor
{
let renderer = state.get_mut(renderer_token);
renderer.set_cursor_representation(&self.pointer_image, (2, 2)).unwrap();
}
// render first frame // render first frame
{ {
let renderer = state.get_mut(renderer_token); let renderer = state.get_mut(renderer_token);
@ -172,6 +377,7 @@ impl UdevHandler<GliumDrawer<DrmBackend>, DrmHandlerImpl> for UdevHandlerImpl {
shell_state_token: self.shell_state_token.clone(), shell_state_token: self.shell_state_token.clone(),
compositor_token: self.compositor_token.clone(), compositor_token: self.compositor_token.clone(),
window_map: self.window_map.clone(), window_map: self.window_map.clone(),
pointer_location: self.pointer_location.clone(),
logger: self.logger.clone(), logger: self.logger.clone(),
}) })
} }
@ -202,6 +408,7 @@ pub struct DrmHandlerImpl {
shell_state_token: StateToken<ShellState<SurfaceData, Roles, (), ()>>, shell_state_token: StateToken<ShellState<SurfaceData, Roles, (), ()>>,
compositor_token: CompositorToken<SurfaceData, Roles, ()>, compositor_token: CompositorToken<SurfaceData, Roles, ()>,
window_map: Rc<RefCell<MyWindowMap>>, window_map: Rc<RefCell<MyWindowMap>>,
pointer_location: Arc<Mutex<(f64, f64)>>,
logger: ::slog::Logger, logger: ::slog::Logger,
} }
@ -210,6 +417,10 @@ impl DrmHandler<GliumDrawer<DrmBackend>> for DrmHandlerImpl {
backend: &StateToken<GliumDrawer<DrmBackend>>, _crtc: crtc::Handle, _frame: u32, _duration: Duration) { backend: &StateToken<GliumDrawer<DrmBackend>>, _crtc: crtc::Handle, _frame: u32, _duration: Duration) {
let state = state.into(); let state = state.into();
let drawer = state.get(backend); let drawer = state.get(backend);
{
let (x, y) = *self.pointer_location.lock().unwrap();
let _ = (**drawer).set_cursor_position(x.trunc().abs() as u32, y.trunc().abs() as u32);
}
let mut frame = drawer.draw(); 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

View File

@ -424,7 +424,7 @@ impl GraphicsBackend for DrmBackend {
}) })
} }
fn set_cursor_representation(&self, buffer: ImageBuffer<Rgba<u8>, Vec<u8>>, hotspot: (u32, u32)) fn set_cursor_representation(&self, buffer: &ImageBuffer<Rgba<u8>, Vec<u8>>, hotspot: (u32, u32))
-> Result<()> { -> Result<()> {
let (w, h) = buffer.dimensions(); let (w, h) = buffer.dimensions();
@ -445,7 +445,7 @@ impl GraphicsBackend for DrmBackend {
) )
.chain_err(|| ErrorKind::GbmInitFailed)?; .chain_err(|| ErrorKind::GbmInitFailed)?;
cursor cursor
.write(&*buffer.into_raw()) .write(&**buffer)
.chain_err(|| ErrorKind::GbmInitFailed)?; .chain_err(|| ErrorKind::GbmInitFailed)?;
trace!(self.logger, "Set the new imported cursor"); trace!(self.logger, "Set the new imported cursor");

View File

@ -29,7 +29,7 @@ pub trait GraphicsBackend {
/// The format is entirely dictated by the concrete implementation and might range /// The format is entirely dictated by the concrete implementation and might range
/// from raw image buffers over a fixed list of possible cursor types to simply the /// from raw image buffers over a fixed list of possible cursor types to simply the
/// void type () to represent no possible customization of the cursor itself. /// void type () to represent no possible customization of the cursor itself.
fn set_cursor_representation(&self, cursor: Self::CursorFormat, hotspot: (u32, u32)) fn set_cursor_representation(&self, cursor: &Self::CursorFormat, hotspot: (u32, u32))
-> Result<(), Self::Error>; -> Result<(), Self::Error>;
} }

View File

@ -1,12 +1,18 @@
//! Implementation of input backend trait for types provided by `libinput` //! Implementation of input backend trait for types provided by `libinput`
#[cfg(feature = "backend_session")]
use backend::session::{AsErrno, Session, SessionObserver};
use backend::input as backend; use backend::input as backend;
use input as libinput; use input as libinput;
use input::event; use input::event;
use std::collections::hash_map::{DefaultHasher, Entry, HashMap}; use std::collections::hash_map::{DefaultHasher, Entry, HashMap};
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::io::Error as IoError; use std::io::{Error as IoError, Result as IoResult};
use std::rc::Rc; use std::rc::Rc;
use std::path::Path;
use std::os::unix::io::RawFd;
use wayland_server::{EventLoopHandle, StateProxy};
use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest};
/// Libinput based `InputBackend`. /// Libinput based `InputBackend`.
/// ///
@ -260,7 +266,7 @@ impl backend::InputBackend for LibinputInputBackend {
if self.handler.is_some() { if self.handler.is_some() {
self.clear_handler(); self.clear_handler();
} }
info!(self.logger, "New input handler set."); info!(self.logger, "New input handler set");
for seat in self.seats.values() { for seat in self.seats.values() {
trace!(self.logger, "Calling on_seat_created with {:?}", seat); trace!(self.logger, "Calling on_seat_created with {:?}", seat);
handler.on_seat_created(seat); handler.on_seat_created(seat);
@ -548,3 +554,59 @@ impl From<event::pointer::ButtonState> for backend::MouseButtonState {
} }
} }
} }
impl SessionObserver for libinput::Libinput {
fn pause<'a>(&mut self, _state: &mut StateProxy<'a>) {
self.suspend()
}
fn activate<'a>(&mut self, _state: &mut StateProxy<'a>) {
// TODO Is this the best way to handle this failure?
self.resume().expect("Unable to resume libinput context");
}
}
pub struct LibinputSessionInterface<S: Session>(S);
impl<S: Session> From<S> for LibinputSessionInterface<S> {
fn from(session: S) -> LibinputSessionInterface<S> {
LibinputSessionInterface(session)
}
}
impl<S: Session> libinput::LibinputInterface for LibinputSessionInterface<S> {
fn open_restricted(&mut self, path: &Path, flags: i32) -> Result<RawFd, i32> {
use nix::fcntl::OFlag;
self.0.open(path, OFlag::from_bits_truncate(flags)).map_err(|err| err.as_errno().unwrap_or(1 /*Use EPERM by default*/))
}
fn close_restricted(&mut self, fd: RawFd) {
let _ = self.0.close(fd);
}
}
pub fn libinput_bind(backend: LibinputInputBackend, evlh: &mut EventLoopHandle)
-> IoResult<FdEventSource<LibinputInputBackend>>
{
let fd = unsafe { backend.context.fd() };
evlh.add_fd_event_source(
fd,
fd_event_source_implementation(),
backend,
FdInterest::READ,
)
}
fn fd_event_source_implementation() -> FdEventSourceImpl<LibinputInputBackend> {
FdEventSourceImpl {
ready: |_evlh, ref mut backend, _, _| {
use ::backend::input::InputBackend;
if let Err(error) = backend.dispatch_new_events() {
warn!(backend.logger, "Libinput errored: {}", error);
}
},
error: |_evlh, ref backend, _, error| {
warn!(backend.logger, "Libinput fd errored: {}", error);
}
}
}

View File

@ -4,7 +4,7 @@ use std::os::unix::io::RawFd;
use std::sync::Arc; use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use nix::{Error as NixError, Result as NixResult}; use nix::{Error as NixError, Result as NixResult};
use nix::fcntl::{self, open}; use nix::fcntl::{self, open, OFlag};
use nix::libc::c_int; use nix::libc::c_int;
use nix::sys::signal::{self, Signal}; use nix::sys::signal::{self, Signal};
use nix::sys::stat::{dev_t, major, minor, Mode, fstat}; use nix::sys::stat::{dev_t, major, minor, Mode, fstat};
@ -15,7 +15,7 @@ use wayland_server::sources::SignalEventSource;
#[cfg(feature = "backend_session_udev")] #[cfg(feature = "backend_session_udev")]
use libudev::Context; use libudev::Context;
use super::{Session, SessionNotifier, SessionObserver}; use super::{AsErrno, Session, SessionNotifier, SessionObserver};
mod tty { mod tty {
ioctl!(bad read kd_get_mode with 0x4B3B; i16); ioctl!(bad read kd_get_mode with 0x4B3B; i16);
@ -34,6 +34,7 @@ mod tty {
ioctl!(bad write_int vt_activate with 0x5606); ioctl!(bad write_int vt_activate with 0x5606);
ioctl!(bad write_int vt_wait_active with 0x5607); ioctl!(bad write_int vt_wait_active with 0x5607);
ioctl!(bad write_ptr vt_set_mode with 0x5602; VtMode); ioctl!(bad write_ptr vt_set_mode with 0x5602; VtMode);
ioctl!(bad write_int vt_rel_disp with 0x5605);
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct VtMode { pub struct VtMode {
@ -50,7 +51,7 @@ mod tty {
} }
pub const VT_AUTO: i8 = 0x00; pub const VT_AUTO: i8 = 0x00;
pub const VT_PROCESS: i8 = 0x01; pub const VT_PROCESS: i8 = 0x01;
pub const VT_ACKACQ: i8 = 0x02; pub const VT_ACKACQ: i32 = 0x02;
extern { extern {
pub fn __libc_current_sigrtmin() -> i8; pub fn __libc_current_sigrtmin() -> i8;
@ -146,6 +147,7 @@ pub struct DirectSession {
} }
pub struct DirectSessionNotifier { pub struct DirectSessionNotifier {
tty: RawFd,
active: Arc<AtomicBool>, active: Arc<AtomicBool>,
signals: Vec<Option<Box<SessionObserver>>>, signals: Vec<Option<Box<SessionObserver>>>,
signal: Signal, signal: Signal,
@ -175,6 +177,7 @@ impl DirectSession {
old_keyboard_mode, old_keyboard_mode,
logger: logger.new(o!("vt" => format!("{}", vt), "component" => "session")), logger: logger.new(o!("vt" => format!("{}", vt), "component" => "session")),
}, DirectSessionNotifier { }, DirectSessionNotifier {
tty: fd,
active, active,
signals: Vec::new(), signals: Vec::new(),
signal, signal,
@ -194,7 +197,7 @@ impl DirectSession {
bail!(ErrorKind::NotRunningFromTTY); bail!(ErrorKind::NotRunningFromTTY);
} }
let vt_num = minor(stat.st_dev) as i32 - 1; let vt_num = minor(stat.st_rdev) as i32;
info!(logger, "Running from tty: {}", vt_num); info!(logger, "Running from tty: {}", vt_num);
let mut mode = 0; let mut mode = 0;
@ -246,8 +249,9 @@ impl DirectSession {
impl Session for DirectSession { impl Session for DirectSession {
type Error = NixError; type Error = NixError;
fn open(&mut self, path: &Path) -> NixResult<RawFd> { fn open(&mut self, path: &Path, flags: OFlag) -> NixResult<RawFd> {
open(path, fcntl::O_RDWR | fcntl::O_CLOEXEC | fcntl::O_NOCTTY | fcntl::O_NONBLOCK, Mode::empty()) info!(self.logger, "Opening device: {:?}", path);
open(path, flags, Mode::empty())
} }
fn close(&mut self, fd: RawFd) -> NixResult<()> { fn close(&mut self, fd: RawFd) -> NixResult<()> {
@ -258,9 +262,9 @@ impl Session for DirectSession {
self.active.load(Ordering::SeqCst) self.active.load(Ordering::SeqCst)
} }
fn seat(&self) -> &str { fn seat(&self) -> String {
// The VT api can only be used on seat0 // The VT api can only be used on seat0
return "seat0" String::from("seat0")
} }
fn change_vt(&mut self, vt_num: i32) -> NixResult<()> { fn change_vt(&mut self, vt_num: i32) -> NixResult<()> {
@ -268,8 +272,19 @@ impl Session for DirectSession {
} }
} }
impl AsErrno for NixError {
fn as_errno(&self) -> Option<i32> {
match *self {
NixError::Sys(errno) => Some(errno as i32),
_ => None,
}
}
}
impl Drop for DirectSession { impl Drop for DirectSession {
fn drop(&mut self) { fn drop(&mut self) {
info!(self.logger, "Deallocating tty {}", self.tty);
if let Err(err) = unsafe { tty::kd_set_kb_mode(self.tty, self.old_keyboard_mode) } { if let Err(err) = unsafe { tty::kd_set_kb_mode(self.tty, self.old_keyboard_mode) } {
warn!(self.logger, "Unable to restore vt keyboard mode. Error: {}", err); warn!(self.logger, "Unable to restore vt keyboard mode. Error: {}", err);
} }
@ -318,7 +333,13 @@ where
if let &mut Some(ref mut signal) = signal {signal.pause(&mut evlh.state().as_proxy()); } if let &mut Some(ref mut signal) = signal {signal.pause(&mut evlh.state().as_proxy()); }
} }
notifier.active.store(false, Ordering::SeqCst); notifier.active.store(false, Ordering::SeqCst);
unsafe {
tty::vt_rel_disp(notifier.tty, 1).expect("Unable to release tty lock");
}
} else { } else {
unsafe {
tty::vt_rel_disp(notifier.tty, tty::VT_ACKACQ).expect("Unable to acquire tty lock");
}
for signal in &mut notifier.signals { for signal in &mut notifier.signals {
if let &mut Some(ref mut signal) = signal { signal.activate(&mut evlh.state().as_proxy()); } if let &mut Some(ref mut signal) = signal { signal.activate(&mut evlh.state().as_proxy()); }
} }

View File

@ -1,17 +1,19 @@
use std::path::Path; use std::path::Path;
use std::sync::{Arc, Mutex};
use std::os::unix::io::RawFd; use std::os::unix::io::RawFd;
use nix::fcntl::OFlag;
use wayland_server::StateProxy; use wayland_server::StateProxy;
pub trait Session { pub trait Session {
type Error: ::std::fmt::Debug; type Error: AsErrno;
fn open(&mut self, path: &Path) -> Result<RawFd, Self::Error>; fn open(&mut self, path: &Path, flags: OFlag) -> Result<RawFd, Self::Error>;
fn close(&mut self, fd: RawFd) -> Result<(), Self::Error>; fn close(&mut self, fd: RawFd) -> Result<(), Self::Error>;
fn change_vt(&mut self, vt: i32) -> Result<(), Self::Error>; fn change_vt(&mut self, vt: i32) -> Result<(), Self::Error>;
fn is_active(&self) -> bool; fn is_active(&self) -> bool;
fn seat(&self) -> &str; fn seat(&self) -> String;
} }
pub trait SessionNotifier { pub trait SessionNotifier {
@ -30,13 +32,47 @@ pub trait SessionObserver {
impl Session for () { impl Session for () {
type Error = (); type Error = ();
fn open(&mut self, _path: &Path) -> Result<RawFd, Self::Error> { Err(()) } fn open(&mut self, _path: &Path, _flags: OFlag) -> Result<RawFd, Self::Error> { Err(()) }
fn close(&mut self, _fd: RawFd) -> Result<(), 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 change_vt(&mut self, _vt: i32) -> Result<(), Self::Error> { Err(()) }
fn is_active(&self) -> bool { false } fn is_active(&self) -> bool { false }
fn seat(&self) -> &str { "seat0" } fn seat(&self) -> String { String::from("seat0") }
}
impl<S: Session> Session for Arc<Mutex<S>> {
type Error = S::Error;
fn open(&mut self, path: &Path, flags: OFlag) -> Result<RawFd, Self::Error> {
self.lock().unwrap().open(path, flags)
}
fn close(&mut self, fd: RawFd) -> Result<(), Self::Error> {
self.lock().unwrap().close(fd)
}
fn change_vt(&mut self, vt: i32) -> Result<(), Self::Error> {
self.lock().unwrap().change_vt(vt)
}
fn is_active(&self) -> bool {
self.lock().unwrap().is_active()
}
fn seat(&self) -> String {
self.lock().unwrap().seat()
}
}
pub trait AsErrno: ::std::fmt::Debug {
fn as_errno(&self) -> Option<i32>;
}
impl AsErrno for () {
fn as_errno(&self) -> Option<i32> {
None
}
} }
pub mod direct; pub mod direct;

View File

@ -1,4 +1,5 @@
use libudev::{Context, MonitorBuilder, MonitorSocket, Event, EventType, Enumerator, Result as UdevResult}; use libudev::{Context, MonitorBuilder, MonitorSocket, Event, EventType, Enumerator, Result as UdevResult};
use nix::fcntl;
use nix::sys::stat::{dev_t, fstat}; use nix::sys::stat::{dev_t, fstat};
use std::borrow::Borrow; use std::borrow::Borrow;
use std::collections::HashMap; use std::collections::HashMap;
@ -16,7 +17,7 @@ use ::backend::session::{Session, SessionObserver};
pub struct UdevBackend<B: Borrow<DrmBackend> + 'static, H: DrmHandler<B> + 'static, S: Session + 'static, T: UdevHandler<B, H> + 'static> { 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)>)>, devices: HashMap<dev_t, (StateToken<DrmDevice<B>>, FdEventSource<(StateToken<DrmDevice<B>>, H)>)>,
monitor: MonitorSocket, monitor: MonitorSocket,
session: StateToken<S>, session: S,
handler: T, handler: T,
logger: ::slog::Logger, logger: ::slog::Logger,
} }
@ -24,7 +25,7 @@ pub struct UdevBackend<B: Borrow<DrmBackend> + 'static, H: DrmHandler<B> + 'stat
impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static, H: DrmHandler<B> + 'static, S: Session + 'static, T: UdevHandler<B, H> + 'static> UdevBackend<B, H, S, T> { 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, pub fn new<'a, L>(mut evlh: &mut EventLoopHandle,
context: &Context, context: &Context,
session_token: &StateToken<S>, mut session: S,
mut handler: T, mut handler: T,
logger: L) logger: L)
-> Result<UdevBackend<B, H, S, T>> -> Result<UdevBackend<B, H, S, T>>
@ -32,7 +33,7 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static, H: DrmHandler<B> + 'sta
L: Into<Option<::slog::Logger>> L: Into<Option<::slog::Logger>>
{ {
let logger = ::slog_or_stdlog(logger).new(o!("smithay_module" => "backend_udev")); let logger = ::slog_or_stdlog(logger).new(o!("smithay_module" => "backend_udev"));
let seat = String::from(evlh.state().get(&session_token).seat()); let seat = session.seat();
let devices = all_gpus(context, seat) let devices = all_gpus(context, seat)
.chain_err(|| ErrorKind::FailedToScan)? .chain_err(|| ErrorKind::FailedToScan)?
.into_iter() .into_iter()
@ -40,8 +41,7 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static, H: DrmHandler<B> + 'sta
.flat_map(|path| { .flat_map(|path| {
match unsafe { DrmDevice::new_from_fd( match unsafe { DrmDevice::new_from_fd(
{ {
let session = evlh.state().get_mut(session_token); match session.open(&path, fcntl::O_RDWR | fcntl::O_CLOEXEC | fcntl::O_NOCTTY | fcntl::O_NONBLOCK) {
match session.open(&path) {
Ok(fd) => fd, Ok(fd) => fd,
Err(err) => { Err(err) => {
warn!(logger, "Unable to open drm device {:?}, Error: {:?}. Skipping", path, err); warn!(logger, "Unable to open drm device {:?}, Error: {:?}. Skipping", path, err);
@ -63,7 +63,6 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static, H: DrmHandler<B> + 'sta
let device = evlh.state().remove(token); let device = evlh.state().remove(token);
let fd = device.as_raw_fd(); let fd = device.as_raw_fd();
drop(device); drop(device);
let session = evlh.state().get_mut(session_token);
if let Err(err) = session.close(fd) { if let Err(err) = session.close(fd) {
warn!(logger, "Failed to close dropped device. Error: {:?}. Ignoring", err); warn!(logger, "Failed to close dropped device. Error: {:?}. Ignoring", err);
}; };
@ -78,7 +77,6 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static, H: DrmHandler<B> + 'sta
let device = evlh.state().remove(token); let device = evlh.state().remove(token);
let fd = device.as_raw_fd(); let fd = device.as_raw_fd();
drop(device); drop(device);
let session = evlh.state().get_mut(session_token);
if let Err(err) = session.close(fd) { if let Err(err) = session.close(fd) {
warn!(logger, "Failed to close dropped device. Error: {:?}. Ignoring", err); warn!(logger, "Failed to close dropped device. Error: {:?}. Ignoring", err);
}; };
@ -88,7 +86,6 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static, H: DrmHandler<B> + 'sta
None => { None => {
let fd = device.as_raw_fd(); let fd = device.as_raw_fd();
drop(device); //drops master drop(device); //drops master
let session = evlh.state().get_mut(session_token);
if let Err(err) = session.close(fd) { if let Err(err) = session.close(fd) {
warn!(logger, "Failed to close device. Error: {:?}. Ignoring", err); warn!(logger, "Failed to close device. Error: {:?}. Ignoring", err);
} }
@ -110,7 +107,7 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static, H: DrmHandler<B> + 'sta
Ok(UdevBackend { Ok(UdevBackend {
devices, devices,
monitor, monitor,
session: session_token.clone(), session,
handler, handler,
logger, logger,
}) })
@ -124,11 +121,11 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static, H: DrmHandler<B> + 'sta
let device = state.remove(device); let device = state.remove(device);
let fd = device.as_raw_fd(); let fd = device.as_raw_fd();
drop(device); drop(device);
let session = state.get_mut(&self.session); if let Err(err) = self.session.close(fd) {
if let Err(err) = session.close(fd) {
warn!(self.logger, "Failed to close device. Error: {:?}. Ignoring", err); warn!(self.logger, "Failed to close device. Error: {:?}. Ignoring", err);
}; };
} }
info!(self.logger, "All devices closed");
} }
} }
@ -187,10 +184,8 @@ where
let mut device = { let mut device = {
match unsafe { DrmDevice::new_from_fd( match unsafe { DrmDevice::new_from_fd(
{ {
let session_token = evlh.state().get(token).session.clone();
let logger = evlh.state().get(token).logger.clone(); let logger = evlh.state().get(token).logger.clone();
let session = evlh.state().get_mut(&session_token); match evlh.state().get_mut(token).session.open(path, fcntl::O_RDWR | fcntl::O_CLOEXEC | fcntl::O_NOCTTY | fcntl::O_NONBLOCK) {
match session.open(path) {
Ok(fd) => fd, Ok(fd) => fd,
Err(err) => { Err(err) => {
warn!(logger, "Unable to open drm device {:?}, Error: {:?}. Skipping", path, err); warn!(logger, "Unable to open drm device {:?}, Error: {:?}. Skipping", path, err);
@ -213,30 +208,25 @@ where
evlh.state().get_mut(token).devices.insert(devnum, (dev_token, fd_event_source)); evlh.state().get_mut(token).devices.insert(devnum, (dev_token, fd_event_source));
} else { } else {
evlh.state().with_value(token, |state, udev| { 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(); let mut state: StateProxy = state.into();
udev.handler.device_removed(&mut state, &dev_token); udev.handler.device_removed(&mut state, &dev_token);
let device = state.remove(dev_token); let device = state.remove(dev_token);
let fd = device.as_raw_fd(); let fd = device.as_raw_fd();
drop(device); drop(device);
if let Err(err) = session.close(fd) { if let Err(err) = udev.session.close(fd) {
warn!(udev.logger, "Failed to close dropped device. Error: {:?}. Ignoring", err); warn!(udev.logger, "Failed to close dropped device. Error: {:?}. Ignoring", err);
}; };
}) })
})
} }
}, },
None => { None => {
let fd = device.as_raw_fd(); let fd = device.as_raw_fd();
drop(device); drop(device);
evlh.state().with_value(token, |state, udev| { evlh.state().with_value(token, |_state, udev| {
state.with_value(&udev.session, |_, session| { if let Err(err) = udev.session.close(fd) {
if let Err(err) = session.close(fd) {
warn!(udev.logger, "Failed to close unused device. Error: {:?}", err); warn!(udev.logger, "Failed to close unused device. Error: {:?}", err);
} }
}) })
})
}, },
}; };
} }
@ -244,8 +234,6 @@ where
// Device removed // Device removed
EventType::Remove => { EventType::Remove => {
evlh.state().with_value(token, |state, udev| { 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"); info!(udev.logger, "Device Remove");
if let Some(devnum) = event.devnum() { if let Some(devnum) = event.devnum() {
if let Some((device, fd_event_source)) = udev.devices.remove(&devnum) { if let Some((device, fd_event_source)) = udev.devices.remove(&devnum) {
@ -255,13 +243,12 @@ where
let device = state.remove(device); let device = state.remove(device);
let fd = device.as_raw_fd(); let fd = device.as_raw_fd();
drop(device); drop(device);
if let Err(err) = session.close(fd) { if let Err(err) = udev.session.close(fd) {
warn!(udev.logger, "Failed to close device {:?}. Error: {:?}. Ignoring", event.sysname(), err); warn!(udev.logger, "Failed to close device {:?}. Error: {:?}. Ignoring", event.sysname(), err);
}; };
} }
} }
}) })
})
}, },
// New connector // New connector
EventType::Change => evlh.state().with_value(token, |state, udev| { EventType::Change => evlh.state().with_value(token, |state, udev| {

View File

@ -190,11 +190,11 @@ impl GraphicsBackend for WinitGraphicsBackend {
self.window.head().set_cursor_position(x as i32, y as i32) self.window.head().set_cursor_position(x as i32, y as i32)
} }
fn set_cursor_representation(&self, cursor: Self::CursorFormat, _hotspot: (u32, u32)) fn set_cursor_representation(&self, cursor: &Self::CursorFormat, _hotspot: (u32, u32))
-> ::std::result::Result<(), ()> { -> ::std::result::Result<(), ()> {
// Cannot log this one, as `CursorFormat` is not `Debug` and should not be // Cannot log this one, as `CursorFormat` is not `Debug` and should not be
debug!(self.logger, "Changing cursor representation"); debug!(self.logger, "Changing cursor representation");
self.window.head().set_cursor(cursor); self.window.head().set_cursor(*cursor);
Ok(()) Ok(())
} }
} }