From 96bb3570bad015446e4978535496bdd27b1933a1 Mon Sep 17 00:00:00 2001 From: Drakulix Date: Tue, 7 Nov 2017 01:18:52 +0100 Subject: [PATCH] Get input working --- Cargo.toml | 2 +- examples/resources/cursor.rgba | Bin 0 -> 2816 bytes examples/resources/cursor2.rgba | Bin 0 -> 16384 bytes examples/udev.rs | 227 ++++++++++++++++++++++++++++++-- src/backend/drm/backend.rs | 4 +- src/backend/graphics/mod.rs | 2 +- src/backend/libinput.rs | 66 +++++++++- src/backend/session/direct.rs | 37 ++++-- src/backend/session/mod.rs | 46 ++++++- src/backend/udev.rs | 81 +++++------- src/backend/winit.rs | 4 +- 11 files changed, 393 insertions(+), 76 deletions(-) create mode 100644 examples/resources/cursor.rgba create mode 100644 examples/resources/cursor2.rgba diff --git a/Cargo.toml b/Cargo.toml index 3efcc8e..06ba3f0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ winit = { version = "0.8.2", optional = true } drm = { version = "^0.3.0", optional = true } gbm = { version = "^0.3.0", optional = true } glium = { version = "0.17.1", optional = true, default-features = false } -input = { version = "0.3.0", optional = true } +input = { version = "0.4.0", git = "https://github.com/Smithay/input.rs.git", branch = "feature/udev", optional = true } libudev = { git = "https://github.com/Drakulix/libudev-rs.git", branch = "feature/raw_ffi_access", optional = true } rental = "0.4.11" wayland-protocols = { version = "0.12.0", features = ["unstable_protocols", "server"] } diff --git a/examples/resources/cursor.rgba b/examples/resources/cursor.rgba new file mode 100644 index 0000000000000000000000000000000000000000..1bd35092c060a6ac4ff37db714bf26e264ace56e GIT binary patch literal 2816 zcmb`JOGso#6o&1-{qog1Dr%x8!Y~sVa1n)I5EQ`?fgqQHmX?;@7hCiBeBX*laAs!41GF8w54JWt zJL@_w;6{~{l-!kVL7a*k2m}ze1o58l!PfHg^Y6k|T<`+T&CP;%$(Fgf zxhAk&r%fiA! zJABsE)YO}@4Q^dso#K+6n3zcR=^48fK4)@r@=e$wxnw6LCCTLEB+)0x#ZLHaWo2dI zvM=S5jdc^R*Gsl6E-rS%7X^jGzLHCJLPCN}OiZXbLEJyX7nhx#eI5H+F4+!;L&nC& zRNP?qz-Q0R&Ao~p7ME<3$t0trql&$}y!zu`e~0 z7wtw|bXK`IG&H2xD=RC%!50-DAAd3KTXD%B92`_@xxT*s2gZ>E-}$)1;$n@Ysi{dC z8yl4x8XBa%yrHbEm$(UiIfsZEdZ=*3YA(qoaS(?^7rh;=EFcP#omIShvQ}M6|;st z<3?`(ot~cl1=?gl>aMq1troM{{1MK_0YXr3#GICvCRjg?Zv^e5L!9|qPfyPi)IA30 z1C)IC`EkW`b#;{ogTejr@o~j1EGQJj!B?p@#8{r9j}JEbuI5b Dtabf1 literal 0 HcmV?d00001 diff --git a/examples/resources/cursor2.rgba b/examples/resources/cursor2.rgba new file mode 100644 index 0000000000000000000000000000000000000000..729c1cc466b85bb2b1e9923d4a0bb7db559df4be GIT binary patch literal 16384 zcmeI1O-NKx6vxx~)X~vYbjq=Ws3dTaLKuP~m=FrJuvI9bL!v{}AX>ICKS>b^7eP@g zaT_j75OiUpE)+i&I*Qt0f{G}SV4*Xkr~kS51{afAtKOS?;mn;m^9KC=-nk#|y0NiQ zw6wHz{|s7TVPP2eq14=N_X8sGPfkv{z->Z=l!K|MDaYaAp%>vFA0K}N{>XxYf{<~5 z`!4vsUN6dk{P6J<4s_Ys*&*S8`N8e2udgTEe|A9A)6;cu5T(=UH19y+uc)Y?q@*O8 znVDe)e)P}az*JmZ9FAVIP2qPsodig-SS&O#F~JUKc6PQIIfKn+)2su9zpAQA@Uw&X z_;_}3c;uiFIcs8Kq9z>#<7Wp62?;bdHpU7Lutv5bXDliz(wu|e@v{TWe>5^O!VYL| zZmu13GZPyFn_Y&O&2;Gn1jIOssml$n{S z2?wX)X9rARH_#EWAc87NL3vr{!k{R;!iz`}>6hJTrA7XT?5IhhDYqZ}gwvKwzgJ8 zO-&6oH#gJn?yjhVm6eqb;J2owrRmYDss-ccxz1!V1@?n_ygsC2$uE_al>rCJbJ70({!cJoN7xWN?m6NSF$gniM^$s-aemxa!M?q{ zy}7fq^KEZ$kD{Za0}e_{O9|&hG(SJj<2?$&tQ>q!pakBkrz!mW+>h~0o12?ov$C=r zot>R6ETg@>J;2Y;17Ihg&-cBlsVNtfM)i;e|4W&l&;OWpZEfuf*0MXe@-iZIYirBB zzP?T=DJcO5d3kw!DSLR2b)V~A)n`OU_n%f*SKS#I8TY_*5n)C|mzS5{W!Bc#R>9Bf z0`fcua{7nE;kb#S7%3o?{NUifh#Tf0L8eE&eVNXJI#Z-Q$Lu z_ux#}h5ca;zDq?Y>mc50uNjR-u7S9|zP=aT-QBP7J{xffYa#Dqc>6-dj literal 0 HcmV?d00001 diff --git a/examples/udev.rs b/examples/udev.rs index 8d48c95..60c9e4e 100644 --- a/examples/udev.rs +++ b/examples/udev.rs @@ -2,6 +2,8 @@ extern crate drm; #[macro_use] extern crate glium; extern crate rand; +extern crate input as libinput; +extern crate image; extern crate libudev; #[macro_use(define_roles)] extern crate smithay; @@ -22,15 +24,24 @@ use drm::control::encoder::Info as EncoderInfo; use drm::control::crtc; use drm::result::Error as DrmError; use glium::Surface; +use image::{ImageBuffer, Rgba}; +use libinput::{Libinput, Device as LibinputDevice, event}; +use libinput::event::keyboard::KeyboardEventTrait; use helpers::{init_shell, GliumDrawer, MyWindowMap, Roles, SurfaceData}; use slog::{Drain, Logger}; use smithay::backend::drm::{DrmBackend, DrmDevice, DrmHandler}; +use smithay::backend::graphics::GraphicsBackend; use smithay::backend::graphics::egl::EGLGraphicsBackend; +use smithay::backend::input::{self, Event, InputBackend, InputHandler, KeyboardKeyEvent, KeyState, PointerButtonEvent, + PointerAxisEvent}; +use smithay::backend::libinput::{LibinputInputBackend, libinput_bind, PointerAxisEvent as LibinputPointerAxisEvent, LibinputSessionInterface}; use smithay::backend::udev::{UdevBackend, UdevHandler, udev_backend_bind}; -use smithay::backend::session::SessionNotifier; +use smithay::backend::session::{Session, SessionNotifier}; use smithay::backend::session::direct::{direct_session_bind, DirectSession}; use smithay::wayland::compositor::{CompositorToken, SubsurfaceRole, TraversalAction}; use smithay::wayland::compositor::roles::Role; +use smithay::wayland::output::{Mode, Output, PhysicalProperties}; +use smithay::wayland::seat::{KeyboardHandle, PointerHandle, Seat}; use smithay::wayland::shell::ShellState; use smithay::wayland::shm::init_shm_global; use std::cell::RefCell; @@ -38,9 +49,121 @@ use std::collections::HashSet; use std::io::Error as IoError; use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use std::time::Duration; use wayland_server::{StateToken, StateProxy}; +use wayland_server::protocol::{wl_output, wl_pointer}; + +struct LibinputInputHandler { + log: Logger, + pointer: PointerHandle, + keyboard: KeyboardHandle, + window_map: Rc>, + pointer_location: Arc>, + screen_size: (u32, u32), + serial: u32, + running: Arc, +} + +impl LibinputInputHandler { + fn next_serial(&mut self) -> u32 { + self.serial += 1; + self.serial + } +} + +impl InputHandler for LibinputInputHandler { + fn on_seat_created(&mut self, _: &input::Seat) { + /* we just create a single static one */ + } + fn on_seat_destroyed(&mut self, _: &input::Seat) { + /* we just create a single static one */ + } + fn on_seat_changed(&mut self, _: &input::Seat) { + /* we just create a single static one */ + } + fn on_keyboard_key(&mut self, _: &input::Seat, evt: event::keyboard::KeyboardKeyEvent) { + let keycode = evt.key(); + let state = evt.state(); + debug!(self.log, "key"; "keycode" => keycode, "state" => format!("{:?}", state)); + + match (keycode, state) { + (1 /*ESC*/, KeyState::Pressed) => { + self.running.store(false, Ordering::SeqCst); + }, + (keycode, state) => { + let serial = self.next_serial(); + self.keyboard.input(keycode, state, serial, |_, _| true); + } + } + } + fn on_pointer_move(&mut self, _: &input::Seat, evt: event::pointer::PointerMotionEvent) { + let (x, y) = (evt.dx(), evt.dy()); + let serial = self.next_serial(); + let mut location = self.pointer_location.lock().unwrap(); + location.0 += x; + location.1 += y; + let under = self.window_map.borrow().get_surface_under((location.0, location.1)); + self.pointer.motion( + under.as_ref().map(|&(ref s, (x, y))| (s, x, y)), + serial, + evt.time(), + ); + } + fn on_pointer_move_absolute(&mut self, _: &input::Seat, evt: event::pointer::PointerMotionAbsoluteEvent) { + let (x, y) = (evt.absolute_x_transformed(self.screen_size.0), evt.absolute_y_transformed(self.screen_size.1)); + *self.pointer_location.lock().unwrap() = (x, y); + let serial = self.next_serial(); + let under = self.window_map.borrow().get_surface_under((x, y)); + self.pointer.motion( + under.as_ref().map(|&(ref s, (x, y))| (s, x, y)), + serial, + evt.time(), + ); + } + fn on_pointer_button(&mut self, _: &input::Seat, evt: event::pointer::PointerButtonEvent) { + let serial = self.next_serial(); + let button = evt.button(); + let state = match evt.state() { + input::MouseButtonState::Pressed => { + // change the keyboard focus + let under = self.window_map + .borrow_mut() + .get_surface_and_bring_to_top(*self.pointer_location.lock().unwrap()); + self.keyboard + .set_focus(under.as_ref().map(|&(ref s, _)| s), serial); + wl_pointer::ButtonState::Pressed + } + input::MouseButtonState::Released => wl_pointer::ButtonState::Released, + }; + self.pointer.button(button, state, serial, evt.time()); + } + fn on_pointer_axis(&mut self, _: &input::Seat, evt: LibinputPointerAxisEvent) { + let axis = match evt.axis() { + input::Axis::Vertical => wayland_server::protocol::wl_pointer::Axis::VerticalScroll, + input::Axis::Horizontal => wayland_server::protocol::wl_pointer::Axis::HorizontalScroll, + }; + self.pointer.axis(axis, evt.amount(), evt.time()); + } + fn on_touch_down(&mut self, _: &input::Seat, _: event::touch::TouchDownEvent) { + /* not done in this example */ + } + fn on_touch_motion(&mut self, _: &input::Seat, _: event::touch::TouchMotionEvent) { + /* not done in this example */ + } + fn on_touch_up(&mut self, _: &input::Seat, _: event::touch::TouchUpEvent) { + /* not done in this example */ + } + fn on_touch_cancel(&mut self, _: &input::Seat, _: event::touch::TouchCancelEvent) { + /* not done in this example */ + } + fn on_touch_frame(&mut self, _: &input::Seat, _: event::touch::TouchFrameEvent) { + /* not done in this example */ + } + fn on_input_config_changed(&mut self, _: &mut [LibinputDevice]) { + /* not done in this example */ + } +} fn main() { // A logger facility, here we use the terminal for this example @@ -63,7 +186,7 @@ fn main() { * Initialize session on the current tty */ let (session, mut notifier) = DirectSession::new(None, log.clone()).unwrap(); - let session_token = event_loop.state().insert(session); + let session = Arc::new(Mutex::new(session)); let running = Arc::new(AtomicBool::new(true)); let r = running.clone(); @@ -71,48 +194,124 @@ fn main() { r.store(false, Ordering::SeqCst); }).expect("Error setting Ctrl-C handler"); + let pointer_location = Arc::new(Mutex::new((0.0, 0.0))); + /* * Initialize the udev backend */ let context = libudev::Context::new().unwrap(); + let bytes = include_bytes!("resources/cursor2.rgba"); let udev - = UdevBackend::new(&mut event_loop, &context, &session_token, UdevHandlerImpl { + = UdevBackend::new(&mut event_loop, &context, session.clone(), UdevHandlerImpl { shell_state_token, compositor_token, window_map: window_map.clone(), + pointer_location: pointer_location.clone(), + pointer_image: ImageBuffer::from_raw(64, 64, bytes.to_vec()).unwrap(), logger: log.clone(), }, log.clone()).unwrap(); let udev_token = event_loop.state().insert(udev); let udev_session_id = notifier.register(udev_token.clone()); + + let (seat_token, _) = Seat::new(&mut event_loop, session.seat().into(), log.clone()); + + let pointer = event_loop.state().get_mut(&seat_token).add_pointer(); + let keyboard = event_loop + .state() + .get_mut(&seat_token) + .add_keyboard("", "", "", None, 1000, 500) + .expect("Failed to initialize the keyboard"); + + let (output_token, _output_global) = Output::new( + &mut event_loop, + "Drm".into(), + PhysicalProperties { + width: 0, + height: 0, + subpixel: wl_output::Subpixel::Unknown, + maker: "Smithay".into(), + model: "Generic DRM".into(), + }, + log.clone(), + ); + + let (w, h) = (1920, 1080); // Hardcode full-hd res + event_loop + .state() + .get_mut(&output_token) + .change_current_state( + Some(Mode { + width: w as i32, + height: h as i32, + refresh: 60_000, + }), + None, + None, + ); + event_loop + .state() + .get_mut(&output_token) + .set_preferred(Mode { + width: w as i32, + height: h as i32, + refresh: 60_000, + }); + + /* + * Initialize libinput backend + */ + let seat = session.seat(); + let mut libinput_context = Libinput::new_from_udev::>>>(session.into(), &context); + let libinput_session_id = notifier.register(libinput_context.clone()); + libinput_context.udev_assign_seat(&seat); + let mut libinput_backend = LibinputInputBackend::new(libinput_context, log.clone()); + libinput_backend.set_handler(LibinputInputHandler { + log: log.clone(), + pointer, + keyboard, + window_map: window_map.clone(), + pointer_location, + screen_size: (w, h), + serial: 0, + running: running.clone(), + }); + let libinput_event_source = libinput_bind(libinput_backend, &mut event_loop).unwrap(); + let session_event_source = direct_session_bind(notifier, &mut event_loop, log.clone()).unwrap(); let udev_event_source = udev_backend_bind(&mut event_loop, udev_token).unwrap(); + /* - * Add a listening socket: + * Add a listening socket */ let name = display.add_socket_auto().unwrap().into_string().unwrap(); println!("Listening on socket: {}", name); while running.load(Ordering::SeqCst) { - event_loop.dispatch(Some(16)).unwrap(); + event_loop.dispatch(Some(16)); display.flush_clients(); window_map.borrow_mut().refresh(); } + println!("Bye Bye"); + let mut notifier = session_event_source.remove(); notifier.unregister(udev_session_id); + notifier.unregister(libinput_session_id); + + libinput_event_source.remove(); let udev_token = udev_event_source.remove(); let udev = event_loop.state().remove(udev_token); udev.close(event_loop.state()); - - event_loop.state().remove(session_token); } struct UdevHandlerImpl { shell_state_token: StateToken>, compositor_token: CompositorToken, window_map: Rc>, + pointer_location: Arc>, + pointer_image: ImageBuffer, Vec>, logger: ::slog::Logger, } @@ -146,6 +345,12 @@ impl UdevHandlerImpl { // create a backend let renderer_token = device.create_backend(&mut state, crtc, mode, vec![connector_info.handle()]).unwrap(); + // create cursor + { + let renderer = state.get_mut(renderer_token); + renderer.set_cursor_representation(&self.pointer_image, (2, 2)).unwrap(); + } + // render first frame { let renderer = state.get_mut(renderer_token); @@ -172,6 +377,7 @@ impl UdevHandler, DrmHandlerImpl> for UdevHandlerImpl { shell_state_token: self.shell_state_token.clone(), compositor_token: self.compositor_token.clone(), window_map: self.window_map.clone(), + pointer_location: self.pointer_location.clone(), logger: self.logger.clone(), }) } @@ -202,6 +408,7 @@ pub struct DrmHandlerImpl { shell_state_token: StateToken>, compositor_token: CompositorToken, window_map: Rc>, + pointer_location: Arc>, logger: ::slog::Logger, } @@ -210,6 +417,10 @@ impl DrmHandler> for DrmHandlerImpl { backend: &StateToken>, _crtc: crtc::Handle, _frame: u32, _duration: Duration) { let state = state.into(); let drawer = state.get(backend); + { + let (x, y) = *self.pointer_location.lock().unwrap(); + let _ = (**drawer).set_cursor_position(x.trunc().abs() as u32, y.trunc().abs() as u32); + } let mut frame = drawer.draw(); frame.clear_color(0.8, 0.8, 0.9, 1.0); // redraw the frame, in a simple but inneficient way diff --git a/src/backend/drm/backend.rs b/src/backend/drm/backend.rs index d2197bc..e784321 100644 --- a/src/backend/drm/backend.rs +++ b/src/backend/drm/backend.rs @@ -424,7 +424,7 @@ impl GraphicsBackend for DrmBackend { }) } - fn set_cursor_representation(&self, buffer: ImageBuffer, Vec>, hotspot: (u32, u32)) + fn set_cursor_representation(&self, buffer: &ImageBuffer, Vec>, hotspot: (u32, u32)) -> Result<()> { let (w, h) = buffer.dimensions(); @@ -445,7 +445,7 @@ impl GraphicsBackend for DrmBackend { ) .chain_err(|| ErrorKind::GbmInitFailed)?; cursor - .write(&*buffer.into_raw()) + .write(&**buffer) .chain_err(|| ErrorKind::GbmInitFailed)?; trace!(self.logger, "Set the new imported cursor"); diff --git a/src/backend/graphics/mod.rs b/src/backend/graphics/mod.rs index 0a082d7..2f0917e 100644 --- a/src/backend/graphics/mod.rs +++ b/src/backend/graphics/mod.rs @@ -29,7 +29,7 @@ pub trait GraphicsBackend { /// The format is entirely dictated by the concrete implementation and might range /// from raw image buffers over a fixed list of possible cursor types to simply the /// void type () to represent no possible customization of the cursor itself. - fn set_cursor_representation(&self, cursor: Self::CursorFormat, hotspot: (u32, u32)) + fn set_cursor_representation(&self, cursor: &Self::CursorFormat, hotspot: (u32, u32)) -> Result<(), Self::Error>; } diff --git a/src/backend/libinput.rs b/src/backend/libinput.rs index 4f7cd06..ca7051b 100644 --- a/src/backend/libinput.rs +++ b/src/backend/libinput.rs @@ -1,12 +1,18 @@ //! Implementation of input backend trait for types provided by `libinput` +#[cfg(feature = "backend_session")] +use backend::session::{AsErrno, Session, SessionObserver}; use backend::input as backend; use input as libinput; use input::event; use std::collections::hash_map::{DefaultHasher, Entry, HashMap}; use std::hash::{Hash, Hasher}; -use std::io::Error as IoError; +use std::io::{Error as IoError, Result as IoResult}; use std::rc::Rc; +use std::path::Path; +use std::os::unix::io::RawFd; +use wayland_server::{EventLoopHandle, StateProxy}; +use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest}; /// Libinput based `InputBackend`. /// @@ -260,7 +266,7 @@ impl backend::InputBackend for LibinputInputBackend { if self.handler.is_some() { self.clear_handler(); } - info!(self.logger, "New input handler set."); + info!(self.logger, "New input handler set"); for seat in self.seats.values() { trace!(self.logger, "Calling on_seat_created with {:?}", seat); handler.on_seat_created(seat); @@ -548,3 +554,59 @@ impl From for backend::MouseButtonState { } } } + +impl SessionObserver for libinput::Libinput { + fn pause<'a>(&mut self, _state: &mut StateProxy<'a>) { + self.suspend() + } + + fn activate<'a>(&mut self, _state: &mut StateProxy<'a>) { + // TODO Is this the best way to handle this failure? + self.resume().expect("Unable to resume libinput context"); + } +} + +pub struct LibinputSessionInterface(S); + +impl From for LibinputSessionInterface { + fn from(session: S) -> LibinputSessionInterface { + LibinputSessionInterface(session) + } +} + +impl libinput::LibinputInterface for LibinputSessionInterface { + fn open_restricted(&mut self, path: &Path, flags: i32) -> Result { + use nix::fcntl::OFlag; + self.0.open(path, OFlag::from_bits_truncate(flags)).map_err(|err| err.as_errno().unwrap_or(1 /*Use EPERM by default*/)) + } + + fn close_restricted(&mut self, fd: RawFd) { + let _ = self.0.close(fd); + } +} + +pub fn libinput_bind(backend: LibinputInputBackend, evlh: &mut EventLoopHandle) + -> IoResult> +{ + let fd = unsafe { backend.context.fd() }; + evlh.add_fd_event_source( + fd, + fd_event_source_implementation(), + backend, + FdInterest::READ, + ) +} + +fn fd_event_source_implementation() -> FdEventSourceImpl { + FdEventSourceImpl { + ready: |_evlh, ref mut backend, _, _| { + use ::backend::input::InputBackend; + if let Err(error) = backend.dispatch_new_events() { + warn!(backend.logger, "Libinput errored: {}", error); + } + }, + error: |_evlh, ref backend, _, error| { + warn!(backend.logger, "Libinput fd errored: {}", error); + } + } +} diff --git a/src/backend/session/direct.rs b/src/backend/session/direct.rs index a74def2..741096d 100644 --- a/src/backend/session/direct.rs +++ b/src/backend/session/direct.rs @@ -4,7 +4,7 @@ use std::os::unix::io::RawFd; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; use nix::{Error as NixError, Result as NixResult}; -use nix::fcntl::{self, open}; +use nix::fcntl::{self, open, OFlag}; use nix::libc::c_int; use nix::sys::signal::{self, Signal}; use nix::sys::stat::{dev_t, major, minor, Mode, fstat}; @@ -15,7 +15,7 @@ use wayland_server::sources::SignalEventSource; #[cfg(feature = "backend_session_udev")] use libudev::Context; -use super::{Session, SessionNotifier, SessionObserver}; +use super::{AsErrno, Session, SessionNotifier, SessionObserver}; mod tty { ioctl!(bad read kd_get_mode with 0x4B3B; i16); @@ -34,6 +34,7 @@ mod tty { ioctl!(bad write_int vt_activate with 0x5606); ioctl!(bad write_int vt_wait_active with 0x5607); ioctl!(bad write_ptr vt_set_mode with 0x5602; VtMode); + ioctl!(bad write_int vt_rel_disp with 0x5605); #[repr(C)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub struct VtMode { @@ -50,7 +51,7 @@ mod tty { } pub const VT_AUTO: i8 = 0x00; pub const VT_PROCESS: i8 = 0x01; - pub const VT_ACKACQ: i8 = 0x02; + pub const VT_ACKACQ: i32 = 0x02; extern { pub fn __libc_current_sigrtmin() -> i8; @@ -146,6 +147,7 @@ pub struct DirectSession { } pub struct DirectSessionNotifier { + tty: RawFd, active: Arc, signals: Vec>>, signal: Signal, @@ -175,6 +177,7 @@ impl DirectSession { old_keyboard_mode, logger: logger.new(o!("vt" => format!("{}", vt), "component" => "session")), }, DirectSessionNotifier { + tty: fd, active, signals: Vec::new(), signal, @@ -194,7 +197,7 @@ impl DirectSession { bail!(ErrorKind::NotRunningFromTTY); } - let vt_num = minor(stat.st_dev) as i32 - 1; + let vt_num = minor(stat.st_rdev) as i32; info!(logger, "Running from tty: {}", vt_num); let mut mode = 0; @@ -246,8 +249,9 @@ impl DirectSession { impl Session for DirectSession { type Error = NixError; - fn open(&mut self, path: &Path) -> NixResult { - open(path, fcntl::O_RDWR | fcntl::O_CLOEXEC | fcntl::O_NOCTTY | fcntl::O_NONBLOCK, Mode::empty()) + fn open(&mut self, path: &Path, flags: OFlag) -> NixResult { + info!(self.logger, "Opening device: {:?}", path); + open(path, flags, Mode::empty()) } fn close(&mut self, fd: RawFd) -> NixResult<()> { @@ -258,9 +262,9 @@ impl Session for DirectSession { self.active.load(Ordering::SeqCst) } - fn seat(&self) -> &str { + fn seat(&self) -> String { // The VT api can only be used on seat0 - return "seat0" + String::from("seat0") } fn change_vt(&mut self, vt_num: i32) -> NixResult<()> { @@ -268,8 +272,19 @@ impl Session for DirectSession { } } +impl AsErrno for NixError { + fn as_errno(&self) -> Option { + match *self { + NixError::Sys(errno) => Some(errno as i32), + _ => None, + } + } +} + impl Drop for DirectSession { fn drop(&mut self) { + info!(self.logger, "Deallocating tty {}", self.tty); + if let Err(err) = unsafe { tty::kd_set_kb_mode(self.tty, self.old_keyboard_mode) } { warn!(self.logger, "Unable to restore vt keyboard mode. Error: {}", err); } @@ -318,7 +333,13 @@ where if let &mut Some(ref mut signal) = signal {signal.pause(&mut evlh.state().as_proxy()); } } notifier.active.store(false, Ordering::SeqCst); + unsafe { + tty::vt_rel_disp(notifier.tty, 1).expect("Unable to release tty lock"); + } } else { + unsafe { + tty::vt_rel_disp(notifier.tty, tty::VT_ACKACQ).expect("Unable to acquire tty lock"); + } for signal in &mut notifier.signals { if let &mut Some(ref mut signal) = signal { signal.activate(&mut evlh.state().as_proxy()); } } diff --git a/src/backend/session/mod.rs b/src/backend/session/mod.rs index 235fc66..507e986 100644 --- a/src/backend/session/mod.rs +++ b/src/backend/session/mod.rs @@ -1,17 +1,19 @@ use std::path::Path; +use std::sync::{Arc, Mutex}; use std::os::unix::io::RawFd; +use nix::fcntl::OFlag; use wayland_server::StateProxy; pub trait Session { - type Error: ::std::fmt::Debug; + type Error: AsErrno; - fn open(&mut self, path: &Path) -> Result; + fn open(&mut self, path: &Path, flags: OFlag) -> Result; fn close(&mut self, fd: RawFd) -> Result<(), Self::Error>; fn change_vt(&mut self, vt: i32) -> Result<(), Self::Error>; fn is_active(&self) -> bool; - fn seat(&self) -> &str; + fn seat(&self) -> String; } pub trait SessionNotifier { @@ -30,13 +32,47 @@ pub trait SessionObserver { impl Session for () { type Error = (); - fn open(&mut self, _path: &Path) -> Result { Err(()) } + fn open(&mut self, _path: &Path, _flags: OFlag) -> Result { Err(()) } fn close(&mut self, _fd: RawFd) -> Result<(), Self::Error> { Err(()) } fn change_vt(&mut self, _vt: i32) -> Result<(), Self::Error> { Err(()) } fn is_active(&self) -> bool { false } - fn seat(&self) -> &str { "seat0" } + fn seat(&self) -> String { String::from("seat0") } +} + +impl Session for Arc> { + type Error = S::Error; + + fn open(&mut self, path: &Path, flags: OFlag) -> Result { + self.lock().unwrap().open(path, flags) + } + + fn close(&mut self, fd: RawFd) -> Result<(), Self::Error> { + self.lock().unwrap().close(fd) + } + + fn change_vt(&mut self, vt: i32) -> Result<(), Self::Error> { + self.lock().unwrap().change_vt(vt) + } + + fn is_active(&self) -> bool { + self.lock().unwrap().is_active() + } + + fn seat(&self) -> String { + self.lock().unwrap().seat() + } +} + +pub trait AsErrno: ::std::fmt::Debug { + fn as_errno(&self) -> Option; +} + +impl AsErrno for () { + fn as_errno(&self) -> Option { + None + } } pub mod direct; diff --git a/src/backend/udev.rs b/src/backend/udev.rs index 2ddef60..794cb76 100644 --- a/src/backend/udev.rs +++ b/src/backend/udev.rs @@ -1,4 +1,5 @@ use libudev::{Context, MonitorBuilder, MonitorSocket, Event, EventType, Enumerator, Result as UdevResult}; +use nix::fcntl; use nix::sys::stat::{dev_t, fstat}; use std::borrow::Borrow; use std::collections::HashMap; @@ -16,7 +17,7 @@ use ::backend::session::{Session, SessionObserver}; pub struct UdevBackend + 'static, H: DrmHandler + 'static, S: Session + 'static, T: UdevHandler + 'static> { devices: HashMap>, FdEventSource<(StateToken>, H)>)>, monitor: MonitorSocket, - session: StateToken, + session: S, handler: T, logger: ::slog::Logger, } @@ -24,7 +25,7 @@ pub struct UdevBackend + 'static, H: DrmHandler + 'stat impl + Borrow + 'static, H: DrmHandler + 'static, S: Session + 'static, T: UdevHandler + 'static> UdevBackend { pub fn new<'a, L>(mut evlh: &mut EventLoopHandle, context: &Context, - session_token: &StateToken, + mut session: S, mut handler: T, logger: L) -> Result> @@ -32,7 +33,7 @@ impl + Borrow + 'static, H: DrmHandler + 'sta L: Into> { let logger = ::slog_or_stdlog(logger).new(o!("smithay_module" => "backend_udev")); - let seat = String::from(evlh.state().get(&session_token).seat()); + let seat = session.seat(); let devices = all_gpus(context, seat) .chain_err(|| ErrorKind::FailedToScan)? .into_iter() @@ -40,8 +41,7 @@ impl + Borrow + 'static, H: DrmHandler + 'sta .flat_map(|path| { match unsafe { DrmDevice::new_from_fd( { - let session = evlh.state().get_mut(session_token); - match session.open(&path) { + match session.open(&path, fcntl::O_RDWR | fcntl::O_CLOEXEC | fcntl::O_NOCTTY | fcntl::O_NONBLOCK) { Ok(fd) => fd, Err(err) => { warn!(logger, "Unable to open drm device {:?}, Error: {:?}. Skipping", path, err); @@ -63,7 +63,6 @@ impl + Borrow + 'static, H: DrmHandler + 'sta let device = evlh.state().remove(token); let fd = device.as_raw_fd(); drop(device); - let session = evlh.state().get_mut(session_token); if let Err(err) = session.close(fd) { warn!(logger, "Failed to close dropped device. Error: {:?}. Ignoring", err); }; @@ -78,7 +77,6 @@ impl + Borrow + 'static, H: DrmHandler + 'sta let device = evlh.state().remove(token); let fd = device.as_raw_fd(); drop(device); - let session = evlh.state().get_mut(session_token); if let Err(err) = session.close(fd) { warn!(logger, "Failed to close dropped device. Error: {:?}. Ignoring", err); }; @@ -88,7 +86,6 @@ impl + Borrow + 'static, H: DrmHandler + 'sta None => { let fd = device.as_raw_fd(); drop(device); //drops master - let session = evlh.state().get_mut(session_token); if let Err(err) = session.close(fd) { warn!(logger, "Failed to close device. Error: {:?}. Ignoring", err); } @@ -110,7 +107,7 @@ impl + Borrow + 'static, H: DrmHandler + 'sta Ok(UdevBackend { devices, monitor, - session: session_token.clone(), + session, handler, logger, }) @@ -124,11 +121,11 @@ impl + Borrow + 'static, H: DrmHandler + 'sta let device = state.remove(device); let fd = device.as_raw_fd(); drop(device); - let session = state.get_mut(&self.session); - if let Err(err) = session.close(fd) { + if let Err(err) = self.session.close(fd) { warn!(self.logger, "Failed to close device. Error: {:?}. Ignoring", err); }; } + info!(self.logger, "All devices closed"); } } @@ -187,10 +184,8 @@ where let mut device = { match unsafe { DrmDevice::new_from_fd( { - let session_token = evlh.state().get(token).session.clone(); let logger = evlh.state().get(token).logger.clone(); - let session = evlh.state().get_mut(&session_token); - match session.open(path) { + match evlh.state().get_mut(token).session.open(path, fcntl::O_RDWR | fcntl::O_CLOEXEC | fcntl::O_NOCTTY | fcntl::O_NONBLOCK) { Ok(fd) => fd, Err(err) => { warn!(logger, "Unable to open drm device {:?}, Error: {:?}. Skipping", path, err); @@ -213,29 +208,24 @@ where evlh.state().get_mut(token).devices.insert(devnum, (dev_token, fd_event_source)); } else { evlh.state().with_value(token, |state, udev| { - let session_token = udev.session.clone(); - state.with_value(&session_token, |state, session| { - let mut state: StateProxy = state.into(); - udev.handler.device_removed(&mut state, &dev_token); - let device = state.remove(dev_token); - let fd = device.as_raw_fd(); - drop(device); - if let Err(err) = session.close(fd) { - warn!(udev.logger, "Failed to close dropped device. Error: {:?}. Ignoring", err); - }; - }) + let mut state: StateProxy = state.into(); + udev.handler.device_removed(&mut state, &dev_token); + let device = state.remove(dev_token); + let fd = device.as_raw_fd(); + drop(device); + if let Err(err) = udev.session.close(fd) { + warn!(udev.logger, "Failed to close dropped device. Error: {:?}. Ignoring", err); + }; }) } }, None => { let fd = device.as_raw_fd(); drop(device); - evlh.state().with_value(token, |state, udev| { - state.with_value(&udev.session, |_, session| { - if let Err(err) = session.close(fd) { - warn!(udev.logger, "Failed to close unused device. Error: {:?}", err); - } - }) + evlh.state().with_value(token, |_state, udev| { + if let Err(err) = udev.session.close(fd) { + warn!(udev.logger, "Failed to close unused device. Error: {:?}", err); + } }) }, }; @@ -244,23 +234,20 @@ where // Device removed EventType::Remove => { evlh.state().with_value(token, |state, udev| { - let session_token = udev.session.clone(); - state.with_value(&session_token, |state, session| { - info!(udev.logger, "Device Remove"); - if let Some(devnum) = event.devnum() { - if let Some((device, fd_event_source)) = udev.devices.remove(&devnum) { - fd_event_source.remove(); - let mut state: StateProxy = state.into(); - udev.handler.device_removed(&mut state, &device); - let device = state.remove(device); - let fd = device.as_raw_fd(); - drop(device); - if let Err(err) = session.close(fd) { - warn!(udev.logger, "Failed to close device {:?}. Error: {:?}. Ignoring", event.sysname(), err); - }; - } + info!(udev.logger, "Device Remove"); + if let Some(devnum) = event.devnum() { + if let Some((device, fd_event_source)) = udev.devices.remove(&devnum) { + fd_event_source.remove(); + let mut state: StateProxy = state.into(); + udev.handler.device_removed(&mut state, &device); + let device = state.remove(device); + let fd = device.as_raw_fd(); + drop(device); + if let Err(err) = udev.session.close(fd) { + warn!(udev.logger, "Failed to close device {:?}. Error: {:?}. Ignoring", event.sysname(), err); + }; } - }) + } }) }, // New connector diff --git a/src/backend/winit.rs b/src/backend/winit.rs index 50c8e8b..177897a 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -190,11 +190,11 @@ impl GraphicsBackend for WinitGraphicsBackend { self.window.head().set_cursor_position(x as i32, y as i32) } - fn set_cursor_representation(&self, cursor: Self::CursorFormat, _hotspot: (u32, u32)) + fn set_cursor_representation(&self, cursor: &Self::CursorFormat, _hotspot: (u32, u32)) -> ::std::result::Result<(), ()> { // Cannot log this one, as `CursorFormat` is not `Debug` and should not be debug!(self.logger, "Changing cursor representation"); - self.window.head().set_cursor(cursor); + self.window.head().set_cursor(*cursor); Ok(()) } }