diff --git a/Cargo.toml b/Cargo.toml index 8f006dd..cab35f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,8 @@ gbm = { version = "^0.4.0", optional = true, default-features = false, features glium = { version = "0.19.0", optional = true, default-features = false } input = { version = "0.4.0", optional = true } udev = { version = "0.2.0", optional = true } +dbus = { version = "0.6.1", optional = true } +systemd = { git = "https://github.com/Drakulix/rust-systemd", branch = "feature/login_functions", optional = true } wayland-protocols = { version = "0.12.5", features = ["unstable_protocols", "server"] } image = "0.17.0" error-chain = "0.11.0" @@ -37,11 +39,12 @@ rand = "0.3" ctrlc = { version = "3.0", features = ["termination"] } [features] -default = ["backend_winit", "backend_drm", "backend_libinput", "backend_udev", "renderer_glium"] +default = ["backend_winit", "backend_drm", "backend_libinput", "backend_udev", "renderer_glium", "backend_session_logind"] backend_winit = ["winit", "wayland-server/dlopen", "wayland-client/dlopen"] backend_drm = ["drm", "gbm"] backend_libinput = ["input"] backend_session = [] backend_session_udev = ["udev", "backend_session"] +backend_session_logind = ["dbus", "systemd", "backend_session"] backend_udev = ["udev", "backend_drm", "backend_session_udev"] renderer_glium = ["glium"] diff --git a/src/backend/drm/mod.rs b/src/backend/drm/mod.rs index 57817da..d2a270f 100644 --- a/src/backend/drm/mod.rs +++ b/src/backend/drm/mod.rs @@ -221,7 +221,7 @@ use drm::control::framebuffer; use drm::result::Error as DrmError; use gbm::Device as GbmDevice; use nix; -use nix::sys::stat::{dev_t, fstat}; +use nix::sys::stat::{self, dev_t, fstat}; use std::collections::HashMap; use std::hash::{Hash, Hasher}; use std::io::Result as IoResult; @@ -597,8 +597,14 @@ where #[cfg(feature = "backend_session")] impl SessionObserver for StateToken> { - fn pause<'a>(&mut self, state: &mut StateProxy<'a>) { + fn pause<'a>(&mut self, state: &mut StateProxy<'a>, devnum: Option<(u32, u32)>) { let device = state.get_mut(self); + if let Some((major, minor)) = devnum { + if major as u64 != stat::major(device.device_id) || + minor as u64 != stat::minor(device.device_id) { + return; + } + } device.active = false; if let Err(err) = device.drop_master() { error!( @@ -608,8 +614,17 @@ impl SessionObserver for StateToken> { } } - fn activate<'a>(&mut self, state: &mut StateProxy<'a>) { + fn activate<'a>(&mut self, state: &mut StateProxy<'a>, devnum: Option<(u32, u32, Option)>) { let device = state.get_mut(self); + if let Some((major, minor, fd)) = devnum { + if major as u64 != stat::major(device.device_id) || + minor as u64 != stat::minor(device.device_id) + { + return; + } else if let Some(fd) = fd { + nix::unistd::dup2(device.as_raw_fd(), fd).expect("Failed to replace file descriptor of drm device"); + } + } device.active = true; if let Err(err) = device.set_master() { crit!( diff --git a/src/backend/libinput.rs b/src/backend/libinput.rs index e81a41d..5628e2b 100644 --- a/src/backend/libinput.rs +++ b/src/backend/libinput.rs @@ -14,6 +14,12 @@ use std::rc::Rc; use wayland_server::{EventLoopHandle, StateProxy}; use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest}; +// No idea if this is the same across unix platforms +// Lets make this linux exclusive for now, once someone tries to build it for +// any BSD-like system, they can verify if this is right and make a PR to change this. +#[cfg(any(target_os = "linux", target_os = "android"))] +const INPUT_MAJOR: u32 = 13; + /// Libinput based `InputBackend`. /// /// Tracks input of all devices given manually or via a udev seat to a provided libinput @@ -568,12 +574,19 @@ impl From for backend::MouseButtonState { #[cfg(feature = "backend_session")] impl SessionObserver for libinput::Libinput { - fn pause<'a>(&mut self, _state: &mut StateProxy<'a>) { + fn pause<'a>(&mut self, _state: &mut StateProxy<'a>, device: Option<(u32, u32)>) { + if let Some((major, _)) = device { + if major != INPUT_MAJOR { + return; + } + } + // lets hope multiple suspends are okay in case of logind? self.suspend() } - fn activate<'a>(&mut self, _state: &mut StateProxy<'a>) { - // TODO Is this the best way to handle this failure? + fn activate<'a>(&mut self, _state: &mut StateProxy<'a>, _device: Option<(u32, u32, Option)>) { + // libinput closes the devices on suspend, so we should not get any INPUT_MAJOR calls + // also lets hope multiple resumes are okay in case of logind self.resume().expect("Unable to resume libinput context"); } } diff --git a/src/backend/session/dbus/logind.rs b/src/backend/session/dbus/logind.rs new file mode 100644 index 0000000..17ce24c --- /dev/null +++ b/src/backend/session/dbus/logind.rs @@ -0,0 +1,502 @@ +use ::backend::session::{AsErrno, Session, SessionNotifier, SessionObserver}; +use nix::fcntl::OFlag; +use nix::sys::stat::{stat, fstat, major, minor}; +use std::cell::RefCell; +use std::io::Result as IoResult; +use std::os::unix::io::RawFd; +use std::rc::{Rc, Weak}; +use std::path::Path; +use std::sync::atomic::{AtomicBool, Ordering}; +use systemd::login; +use dbus::{BusType, Connection, ConnectionItems, ConnectionItem, Message, BusName, Interface, Member, Path as DbusPath, OwnedFd, MessageItem, Watch, WatchEvent}; +use wayland_server::EventLoopHandle; +use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest}; + +struct LogindSessionImpl { + conn: RefCell, + session_path: DbusPath<'static>, + active: AtomicBool, + signals: RefCell>>>, + seat: String, + logger: ::slog::Logger, +} + +#[derive(Clone)] +pub struct LogindSession { + internal: Weak, + seat: String, +} + +pub struct LogindSessionNotifier { + internal: Rc +} + +impl LogindSession { + pub fn new(logger: L) -> Result<(LogindSession, LogindSessionNotifier)> + where + L: Into> + { + let logger = ::slog_or_stdlog(logger) + .new(o!("smithay_module" => "backend_session", "session_type" => "logind")); + + let session_id = login::get_session(None).chain_err(|| ErrorKind::FailedToGetSession)?; + let seat = login::get_seat(session_id.clone()).chain_err(|| ErrorKind::FailedToGetSeat)?; + /*let vt = if seat == "seat0" { + Some(login::get_vt(session_id.clone()).chain_err(|| ErrorKind::FailedToGetVT)?) + } else { + None + };*/ + let vt = login::get_vt(session_id.clone()).ok(); + + let conn = Connection::get_private(BusType::System).chain_err(|| ErrorKind::FailedDbusConnection)?; + + let session_path = LogindSessionImpl::blocking_call( + &conn, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "GetSession", + Some(vec![session_id.clone().into()]) + )?.get1::>() + .chain_err(|| ErrorKind::UnexpectedMethodReturn)?; + + let match1 = String::from("type='signal',\ + sender='org.freedesktop.login1',\ + interface='org.freedesktop.login1.Manager',\ + member='SessionRemoved',\ + path='/org/freedesktop/login1'"); + conn.add_match(&match1).chain_err(|| ErrorKind::DbusMatchFailed(match1))?; + let match2 = format!("type='signal',\ + sender='org.freedesktop.login1',\ + interface='org.freedesktop.login1.Session',\ + member='PauseDevice',\ + path='{}'", &session_path); + conn.add_match(&match2).chain_err(|| ErrorKind::DbusMatchFailed(match2))?; + let match3 = format!("type='signal',\ + sender='org.freedesktop.login1',\ + interface='org.freedesktop.login1.Session',\ + member='ResumeDevice',\ + path='{}'", &session_path); + conn.add_match(&match3).chain_err(|| ErrorKind::DbusMatchFailed(match3))?; + let match4 = format!("type='signal',\ + sender='org.freedesktop.login1',\ + interface='org.freedesktop.DBus.Properties',\ + member='PropertiesChanged',\ + path='{}'", &session_path); + conn.add_match(&match4).chain_err(|| ErrorKind::DbusMatchFailed(match4))?; + + LogindSessionImpl::blocking_call( + &conn, + "org.freedesktop.login1", + session_path.clone(), + "org.freedesktop.login1.Session", + "Activate", + None, + )?; + + LogindSessionImpl::blocking_call( + &conn, + "org.freedesktop.login1", + session_path.clone(), + "org.freedesktop.login1.Session", + "TakeControl", + Some(vec![false.into()]), + )?; + + let signals = RefCell::new(Vec::new()); + let conn = RefCell::new(conn); + + let internal = Rc::new(LogindSessionImpl { + conn, + session_path, + active: AtomicBool::new(true), + signals, + seat: seat.clone(), + logger: logger.new(o!("id" => session_id, "seat" => seat.clone(), "vt" => format!("{:?}", &vt))), + }); + + Ok(( + LogindSession { + internal: Rc::downgrade(&internal), + seat, + }, + LogindSessionNotifier { + internal, + } + )) + } +} + +impl LogindSessionNotifier { + pub fn session(&self) -> LogindSession { + LogindSession { + internal: Rc::downgrade(&self.internal), + seat: self.internal.seat.clone(), + } + } +} + +impl LogindSessionImpl { + fn blocking_call<'d, 'p, 'i, 'm, D, P, I, M> + ( + conn: &Connection, + destination: D, + path: P, + interface: I, + method: M, + arguments: Option> + ) -> Result + where + D: Into>, + P: Into>, + I: Into>, + M: Into>, + { + let destination = destination.into().into_static(); + let path = path.into().into_static(); + let interface = interface.into().into_static(); + let method = method.into().into_static(); + + let mut message = Message::method_call(&destination, &path, &interface, &method); + + if let Some(arguments) = arguments { + message.append_items(&arguments) + }; + + let mut message = conn.send_with_reply_and_block(message, 1000) + .chain_err(|| ErrorKind::FailedToSendDbusCall( + destination.clone(), + path.clone(), + interface.clone(), + method.clone() + ))?; + + match message.as_result() { + Ok(_) => Ok(message), + Err(err) => Err(Error::with_chain(err, ErrorKind::DbusCallFailed( + destination.clone(), + path.clone(), + interface.clone(), + method.clone() + ))) + } + } + + fn handle_signals(&self, evlh: &mut EventLoopHandle, signals: ConnectionItems) -> Result<()> { + for item in signals { + let message = if let ConnectionItem::Signal(ref s) = item { + s + } else { + continue + }; + if &*message.interface().unwrap() == "org.freedesktop.login1.Manager" + && &*message.member().unwrap() == "SessionRemoved" + { + error!(self.logger, "Session got closed by logind"); + //Ok... now what? + //This session will never live again, but the user maybe has other sessions open + //So lets just put it to sleep.. forever + for signal in &mut *self.signals.borrow_mut() { + if let &mut Some(ref mut signal) = signal { + signal.pause(&mut evlh.state().as_proxy(), None); + } + } + self.active.store(false, Ordering::SeqCst); + warn!(self.logger, "Session is now considered inactive"); + } else if &*message.interface().unwrap() == "org.freedesktop.login1.Session" { + if &*message.member().unwrap() == "PauseDevice" { + let (major, minor, pause_type) = message.get3::(); + let major = major.chain_err(|| ErrorKind::UnexpectedMethodReturn)?; + let minor = minor.chain_err(|| ErrorKind::UnexpectedMethodReturn)?; + let pause_type = pause_type.chain_err(|| ErrorKind::UnexpectedMethodReturn)?; + debug!(self.logger, "Request of type \"{}\" to close device ({},{})", pause_type, major, minor); + for signal in &mut *self.signals.borrow_mut() { + if let &mut Some(ref mut signal) = signal { + signal.pause(&mut evlh.state().as_proxy(), Some((major, minor))); + } + } + // the other possible types are "force" or "gone" (unplugged), + // both expect no acknoledgement (note even this is not *really* necessary, + // logind would just timeout and send a "force" event. There is no way to + // keep the device.) + if &*pause_type == "pause" { + LogindSessionImpl::blocking_call( + &*self.conn.borrow(), + "org.freedesktop.login1", + self.session_path.clone(), + "org.freedesktop.login1.Session", + "PauseDeviceComplete", + Some(vec![major.into(), minor.into()]) + )?; + } + } else if &*message.member().unwrap() == "ResumeDevice" { + let (major, minor, fd) = message.get3::(); + let major = major.chain_err(|| ErrorKind::UnexpectedMethodReturn)?; + let minor = minor.chain_err(|| ErrorKind::UnexpectedMethodReturn)?; + let fd = fd.chain_err(|| ErrorKind::UnexpectedMethodReturn)?.into_fd(); + debug!(self.logger, "Reactivating device ({},{})", major, minor); + for signal in &mut *self.signals.borrow_mut() { + if let &mut Some(ref mut signal) = signal { + signal.activate(&mut evlh.state().as_proxy(), Some((major, minor, Some(fd)))); + } + } + } + } else if &*message.interface().unwrap() == "org.freedesktop.DBus.Properties" + && &*message.member().unwrap() == "PropertiesChanged" + { + use dbus::arg::{Array, Dict, Iter, Variant, Get}; + + let (_, changed, _) = message.get3::, Iter>, Array>(); + let mut changed = changed.chain_err(|| ErrorKind::UnexpectedMethodReturn)?; + if let Some((_, mut value)) = changed.find(|&(ref key, _)| &*key == "Active") { + if let Some(active) = Get::get(&mut value.0) { + self.active.store(active, Ordering::SeqCst); + } + } + } + } + Ok(()) + } +} + +impl Session for LogindSession { + type Error = Error; + + fn open(&mut self, path: &Path, _flags: OFlag) -> Result { + if let Some(session) = self.internal.upgrade() { + let stat = stat(path).chain_err(|| ErrorKind::FailedToStatDevice)?; + // TODO handle paused + let (fd, _paused) = LogindSessionImpl::blocking_call( + &*session.conn.borrow(), + "org.freedesktop.login1", + session.session_path.clone(), + "org.freedesktop.login1.Session", + "TakeDevice", + Some(vec![ + (major(stat.st_rdev) as u32).into(), + (minor(stat.st_rdev) as u32).into(), + ]) + )?.get2::(); + let fd = fd.chain_err(|| ErrorKind::UnexpectedMethodReturn)?.into_fd(); + Ok(fd) + } else { + bail!(ErrorKind::SessionLost) + } + } + + fn close(&mut self, fd: RawFd) -> Result<()> { + if let Some(session) = self.internal.upgrade() { + let stat = fstat(fd).chain_err(|| ErrorKind::FailedToStatDevice)?; + LogindSessionImpl::blocking_call( + &*session.conn.borrow(), + "org.freedesktop.login1", + session.session_path.clone(), + "org.freedesktop.login1.Session", + "ReleaseDevice", + Some(vec![ + (major(stat.st_rdev) as u32).into(), + (minor(stat.st_rdev) as u32).into(), + ]) + ).map(|_| ()) + } else { + bail!(ErrorKind::SessionLost) + } + } + + fn is_active(&self) -> bool { + if let Some(internal) = self.internal.upgrade() { + internal.active.load(Ordering::SeqCst) + } else { + false + } + } + + fn seat(&self) -> String { + self.seat.clone() + } + + fn change_vt(&mut self, vt_num: i32) -> Result<()> { + if let Some(session) = self.internal.upgrade() { + LogindSessionImpl::blocking_call( + &*session.conn.borrow_mut(), + "org.freedesktop.login1", + "/org/freedesktop/login1/seat/self", + "org.freedesktop.login1.Seat", + "SwitchTo", + Some(vec![ + (vt_num as u32).into(), + ]) + ).map(|_| ()) + } else { + bail!(ErrorKind::SessionLost) + } + } +} + +/// Ids of registered `SessionObserver`s of the `LogindSessionNotifier` +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct Id(usize); + +impl SessionNotifier for LogindSessionNotifier { + type Id = Id; + + fn register(&mut self, signal: S) -> Id { + self.internal.signals.borrow_mut().push(Some(Box::new(signal))); + Id(self.internal.signals.borrow().len() - 1) + } + fn unregister(&mut self, signal: Id) { + self.internal.signals.borrow_mut()[signal.0] = None; + } + + fn is_active(&self) -> bool { + self.internal.active.load(Ordering::SeqCst) + } + + fn seat(&self) -> &str { + &self.internal.seat + } +} + +pub struct BoundLogindSession { + notifier: LogindSessionNotifier, + watches: Vec, + sources: Vec>>, +} + +pub fn logind_session_bind( + notifier: LogindSessionNotifier, evlh: &mut EventLoopHandle +) -> IoResult +{ + let watches = notifier.internal.conn.borrow().watch_fds(); + let sources = watches.clone().into_iter().map(|watch| { + let mut interest = FdInterest::empty(); + interest.set(FdInterest::READ, watch.readable()); + interest.set(FdInterest::WRITE, watch.writable()); + evlh.add_fd_event_source( + watch.fd(), + fd_event_source_implementation(), + notifier.internal.clone(), + interest + ) + }).collect::>>>>()?; + + Ok(BoundLogindSession { + notifier, + watches, + sources, + }) +} + +impl BoundLogindSession { + pub fn close(self) -> LogindSessionNotifier { + for source in self.sources { + source.remove(); + } + self.notifier + } +} + +impl Drop for LogindSessionNotifier { + fn drop(&mut self) { + info!(self.internal.logger, "Closing logind session"); + let _ = LogindSessionImpl::blocking_call( + &*self.internal.conn.borrow(), + "org.freedesktop.login1", + self.internal.session_path.clone(), + "org.freedesktop.login1.Session", + "ReleaseControl", + None, + ); + } +} + +fn fd_event_source_implementation() -> FdEventSourceImpl> { + FdEventSourceImpl { + ready: |evlh, session, fd, interest| { + let conn = session.conn.borrow(); + let items = conn.watch_handle(fd, match interest { + x if x.contains(FdInterest::READ) && x.contains(FdInterest::WRITE) => + WatchEvent::Readable as u32 | WatchEvent::Writable as u32, + x if x.contains(FdInterest::READ) => WatchEvent::Readable as u32, + x if x.contains(FdInterest::WRITE) => WatchEvent::Writable as u32, + _ => return, + }); + if let Err(err) = session.handle_signals(evlh, items) { + error!(session.logger, "Error handling dbus signals: {}", err); + } + }, + error: |evlh, session, fd, error| { + warn!(session.logger, "Error on dbus connection: {:?}", error); + // handle the remaining messages, they might contain the SessionRemoved event + // in case the server did close the connection. + let conn = session.conn.borrow(); + let items = conn.watch_handle(fd, WatchEvent::Error as u32); + if let Err(err) = session.handle_signals(evlh, items) { + error!(session.logger, "Error handling dbus signals: {}", err); + } + }, + } +} + +error_chain! { + errors { + #[doc = "Failed to connect to dbus system socket"] + FailedDbusConnection { + description("Failed to connect to dbus system socket"), + } + + #[doc = "Failed to get session from logind"] + FailedToGetSession { + description("Failed to get session from logind") + } + + #[doc = "Failed to get seat from logind"] + FailedToGetSeat { + description("Failed to get seat from logind") + } + + #[doc = "Failed to get vt from logind"] + FailedToGetVT { + description("Failed to get vt from logind") + } + + #[doc = "Failed to call dbus method"] + FailedToSendDbusCall(bus: BusName<'static>, path: DbusPath<'static>, interface: Interface<'static>, member: Member<'static>) { + description("Failed to call dbus method") + display("Failed to call dbus method for service: {:?}, path: {:?}, interface: {:?}, member: {:?}", bus, path, interface, member), + } + + #[doc = "Dbus method call failed"] + DbusCallFailed(bus: BusName<'static>, path: DbusPath<'static>, interface: Interface<'static>, member: Member<'static>) { + description("Dbus method call failed") + display("Dbus message call failed for service: {:?}, path: {:?}, interface: {:?}, member: {:?}", bus, path, interface, member), + } + + #[doc = "Dbus method return had unexpected format"] + UnexpectedMethodReturn { + description("Dbus method return returned unexpected format") + } + + #[doc = "Failed to setup dbus match rule"] + DbusMatchFailed(rule: String) { + description("Failed to setup dbus match rule"), + display("Failed to setup dbus match rule {}", rule), + } + + #[doc = "Failed to stat device"] + FailedToStatDevice { + description("Failed to stat device") + } + + #[doc = "Session is already closed"] + SessionLost { + description("Session is already closed") + } + } +} + +impl AsErrno for Error { + fn as_errno(&self) -> Option { + None + } +} diff --git a/src/backend/session/dbus/mod.rs b/src/backend/session/dbus/mod.rs new file mode 100644 index 0000000..cc0676f --- /dev/null +++ b/src/backend/session/dbus/mod.rs @@ -0,0 +1,2 @@ +#[cfg(feature = "backend_session_logind")] +pub mod logind; diff --git a/src/backend/session/direct.rs b/src/backend/session/direct.rs index 27659aa..3a61b71 100644 --- a/src/backend/session/direct.rs +++ b/src/backend/session/direct.rs @@ -376,7 +376,7 @@ pub fn direct_session_bind( info!(notifier.logger, "Session shall become inactive"); for signal in &mut notifier.signals { if let &mut Some(ref mut signal) = signal { - signal.pause(&mut evlh.state().as_proxy()); + signal.pause(&mut evlh.state().as_proxy(), None); } } notifier.active.store(false, Ordering::SeqCst); @@ -391,7 +391,7 @@ pub fn direct_session_bind( } for signal in &mut notifier.signals { if let &mut Some(ref mut signal) = signal { - signal.activate(&mut evlh.state().as_proxy()); + signal.activate(&mut evlh.state().as_proxy(), None); } } notifier.active.store(true, Ordering::SeqCst); diff --git a/src/backend/session/mod.rs b/src/backend/session/mod.rs index 0d16259..3fd8ff8 100644 --- a/src/backend/session/mod.rs +++ b/src/backend/session/mod.rs @@ -68,16 +68,26 @@ pub trait SessionNotifier { /// It might be impossible to interact with devices while the session is disabled. /// This interface provides callbacks for when that happens. pub trait SessionObserver { - /// Session is about to be paused. + /// Session/Device is about to be paused. /// /// In case the implementor is a `StateToken` the state of the `EventLoop` /// is provided via a `StateProxy`. - fn pause<'a>(&mut self, state: &mut StateProxy<'a>); - /// Session got active again + /// + /// If only a specific device shall be closed a device number in the form of + /// (major, minor) is provided. All observers not using the specified device should + /// ignore the signal in that case. + fn pause<'a>(&mut self, state: &mut StateProxy<'a>, device: Option<(u32, u32)>); + /// Session/Device got active again /// /// In case the implementor is a `StateToken` the state of the `EventLoop` /// is provided via a `StateProxy`. - fn activate<'a>(&mut self, state: &mut StateProxy<'a>); + /// + /// If only a specific device shall be activated again a device number in the form of + /// (major, major, Option) is provided. Optionally the session may decide to replace + /// the currently open file descriptor of the device with a new one. In that case the old one + /// should not be used anymore and be closed. All observers not using the specified device should + /// ignore the signal in that case. + fn activate<'a>(&mut self, state: &mut StateProxy<'a>, device: Option<(u32, u32, Option)>); } impl Session for () { @@ -163,3 +173,5 @@ impl AsErrno for () { } pub mod direct; +mod dbus; +pub use self::dbus::*; diff --git a/src/backend/udev.rs b/src/backend/udev.rs index b85b4b2..c90936f 100644 --- a/src/backend/udev.rs +++ b/src/backend/udev.rs @@ -178,20 +178,19 @@ impl< H: DrmHandler + 'static, S: Session + 'static, T: UdevHandler + 'static, -> SessionObserver for StateToken> -{ - fn pause<'a>(&mut self, state: &mut StateProxy<'a>) { +> SessionObserver for StateToken> { + fn pause<'a>(&mut self, state: &mut StateProxy<'a>, devnum: Option<(u32, u32)>) { state.with_value(self, |state, udev| { for &mut (ref mut device, _) in udev.devices.values_mut() { - device.pause(state); + device.pause(state, devnum); } }); } - fn activate<'a>(&mut self, state: &mut StateProxy<'a>) { + fn activate<'a>(&mut self, state: &mut StateProxy<'a>, devnum: Option<(u32, u32, Option)>) { state.with_value(self, |state, udev| { for &mut (ref mut device, _) in udev.devices.values_mut() { - device.activate(state); + device.activate(state, devnum); } }); } diff --git a/src/lib.rs b/src/lib.rs index 928e1a8..b5f4b4d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,12 +24,10 @@ extern crate gbm; extern crate input; #[cfg(feature = "udev")] extern crate udev; -/* -#[cfg(feature = "backend_session_logind")] +#[cfg(feature = "dbus")] extern crate dbus; #[cfg(feature = "backend_session_logind")] extern crate systemd; -*/ #[cfg(feature = "backend_winit")] extern crate wayland_client; #[cfg(feature = "backend_winit")]