commit
9058f37a6a
|
@ -36,6 +36,7 @@ env:
|
||||||
- FEATURES="backend_udev"
|
- FEATURES="backend_udev"
|
||||||
- FEATURES="backend_session"
|
- FEATURES="backend_session"
|
||||||
- FEATURES="backend_session_udev"
|
- FEATURES="backend_session_udev"
|
||||||
|
- FEATURES="backend_session_logind"
|
||||||
- FEATURES="renderer_glium"
|
- FEATURES="renderer_glium"
|
||||||
# test default features
|
# test default features
|
||||||
- FEATURES="default"
|
- FEATURES="default"
|
||||||
|
|
|
@ -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 }
|
glium = { version = "0.19.0", optional = true, default-features = false }
|
||||||
input = { version = "0.4.0", optional = true }
|
input = { version = "0.4.0", optional = true }
|
||||||
udev = { version = "0.2.0", optional = true }
|
udev = { version = "0.2.0", optional = true }
|
||||||
|
dbus = { version = "0.6.1", optional = true }
|
||||||
|
systemd = { version = "^0.2.0", optional = true }
|
||||||
wayland-protocols = { version = "0.12.5", features = ["unstable_protocols", "server"] }
|
wayland-protocols = { version = "0.12.5", features = ["unstable_protocols", "server"] }
|
||||||
image = "0.17.0"
|
image = "0.17.0"
|
||||||
error-chain = "0.11.0"
|
error-chain = "0.11.0"
|
||||||
|
@ -34,14 +36,14 @@ gl_generator = "0.7"
|
||||||
slog-term = "2.3"
|
slog-term = "2.3"
|
||||||
slog-async = "2.2"
|
slog-async = "2.2"
|
||||||
rand = "0.3"
|
rand = "0.3"
|
||||||
ctrlc = { version = "3.0", features = ["termination"] }
|
|
||||||
|
|
||||||
[features]
|
[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_winit = ["winit", "wayland-server/dlopen", "wayland-client/dlopen"]
|
||||||
backend_drm = ["drm", "gbm"]
|
backend_drm = ["drm", "gbm"]
|
||||||
backend_libinput = ["input"]
|
backend_libinput = ["input"]
|
||||||
backend_session = []
|
backend_session = []
|
||||||
backend_session_udev = ["udev", "backend_session"]
|
backend_session_udev = ["udev", "backend_session"]
|
||||||
|
backend_session_logind = ["dbus", "systemd", "backend_session"]
|
||||||
backend_udev = ["udev", "backend_drm", "backend_session_udev"]
|
backend_udev = ["udev", "backend_drm", "backend_session_udev"]
|
||||||
renderer_glium = ["glium"]
|
renderer_glium = ["glium"]
|
||||||
|
|
|
@ -15,8 +15,6 @@ extern crate slog;
|
||||||
extern crate slog_async;
|
extern crate slog_async;
|
||||||
extern crate slog_term;
|
extern crate slog_term;
|
||||||
|
|
||||||
extern crate ctrlc;
|
|
||||||
|
|
||||||
mod helpers;
|
mod helpers;
|
||||||
|
|
||||||
use drm::control::{Device as ControlDevice, ResourceInfo};
|
use drm::control::{Device as ControlDevice, ResourceInfo};
|
||||||
|
@ -39,7 +37,7 @@ use smithay::backend::input::{self, Event, InputBackend, InputHandler, KeyState,
|
||||||
use smithay::backend::libinput::{libinput_bind, LibinputInputBackend, LibinputSessionInterface,
|
use smithay::backend::libinput::{libinput_bind, LibinputInputBackend, LibinputSessionInterface,
|
||||||
PointerAxisEvent as LibinputPointerAxisEvent};
|
PointerAxisEvent as LibinputPointerAxisEvent};
|
||||||
use smithay::backend::session::{Session, SessionNotifier};
|
use smithay::backend::session::{Session, SessionNotifier};
|
||||||
use smithay::backend::session::direct::{direct_session_bind, DirectSession};
|
use smithay::backend::session::auto::{auto_session_bind, AutoSession};
|
||||||
use smithay::backend::udev::{primary_gpu, udev_backend_bind, SessionFdDrmDevice, UdevBackend, UdevHandler};
|
use smithay::backend::udev::{primary_gpu, udev_backend_bind, SessionFdDrmDevice, UdevBackend, UdevHandler};
|
||||||
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;
|
||||||
|
@ -48,6 +46,7 @@ use smithay::wayland::seat::{KeyboardHandle, PointerHandle, Seat};
|
||||||
use smithay::wayland::shm::init_shm_global;
|
use smithay::wayland::shm::init_shm_global;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::env;
|
||||||
use std::io::Error as IoError;
|
use std::io::Error as IoError;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
@ -67,6 +66,7 @@ struct LibinputInputHandler {
|
||||||
pointer_location: Rc<RefCell<(f64, f64)>>,
|
pointer_location: Rc<RefCell<(f64, f64)>>,
|
||||||
screen_size: (u32, u32),
|
screen_size: (u32, u32),
|
||||||
serial: u32,
|
serial: u32,
|
||||||
|
session: AutoSession,
|
||||||
running: Arc<AtomicBool>,
|
running: Arc<AtomicBool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,14 +93,36 @@ impl InputHandler<LibinputInputBackend> for LibinputInputHandler {
|
||||||
let keycode = evt.key();
|
let keycode = evt.key();
|
||||||
let state = evt.state();
|
let state = evt.state();
|
||||||
debug!(self.log, "key"; "keycode" => keycode, "state" => format!("{:?}", state));
|
debug!(self.log, "key"; "keycode" => keycode, "state" => format!("{:?}", state));
|
||||||
|
|
||||||
let serial = self.next_serial();
|
let serial = self.next_serial();
|
||||||
|
|
||||||
|
// we cannot borrow `self` into the closure, because we need self.keyboard.
|
||||||
|
// but rust does not borrow all fields separately, so we need to do that manually...
|
||||||
|
let running = &self.running;
|
||||||
|
let mut session = &mut self.session;
|
||||||
|
let log = &self.log;
|
||||||
self.keyboard
|
self.keyboard
|
||||||
.input(keycode, state, serial, |modifiers, keysym| {
|
.input(keycode, state, serial, move |modifiers, keysym| {
|
||||||
if modifiers.ctrl && modifiers.alt && keysym == xkb::KEY_BackSpace {
|
debug!(log, "keysym"; "state" => format!("{:?}", state), "mods" => format!("{:?}", modifiers), "keysym" => xkbcommon::xkb::keysym_get_name(keysym));
|
||||||
self.running.store(false, Ordering::SeqCst);
|
if modifiers.ctrl && modifiers.alt && keysym == xkb::KEY_BackSpace
|
||||||
|
&& state == KeyState::Pressed
|
||||||
|
{
|
||||||
|
info!(log, "Stopping example using Ctrl+Alt+Backspace");
|
||||||
|
running.store(false, Ordering::SeqCst);
|
||||||
|
false
|
||||||
|
} else if modifiers.logo && keysym == xkb::KEY_q {
|
||||||
|
info!(log, "Stopping example using Logo+Q");
|
||||||
|
running.store(false, Ordering::SeqCst);
|
||||||
|
false
|
||||||
|
} else if modifiers.ctrl && modifiers.alt && keysym == xkb::KEY_XF86Switch_VT_1
|
||||||
|
&& state == KeyState::Pressed
|
||||||
|
{
|
||||||
|
info!(log, "Trying to switch to vt 1");
|
||||||
|
if let Err(err) = session.change_vt(1) {
|
||||||
|
error!(log, "Error switching to vt 1: {}", err);
|
||||||
|
}
|
||||||
false
|
false
|
||||||
} else if modifiers.logo && keysym == xkb::KEY_Return && state == KeyState::Pressed {
|
} else if modifiers.logo && keysym == xkb::KEY_Return && state == KeyState::Pressed {
|
||||||
|
info!(log, "Launching terminal");
|
||||||
let _ = Command::new("weston-terminal").spawn();
|
let _ = Command::new("weston-terminal").spawn();
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
|
@ -217,6 +239,7 @@ fn main() {
|
||||||
*/
|
*/
|
||||||
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);
|
||||||
|
env::set_var("WAYLAND_DISPLAY", name);
|
||||||
let display = Rc::new(display);
|
let display = Rc::new(display);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -228,16 +251,11 @@ fn main() {
|
||||||
init_shell(&mut event_loop, log.clone(), active_egl_context.clone());
|
init_shell(&mut event_loop, log.clone(), active_egl_context.clone());
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize session on the current tty
|
* Initialize session
|
||||||
*/
|
*/
|
||||||
let (session, mut notifier) = DirectSession::new(None, log.clone()).unwrap();
|
let (session, mut notifier) = AutoSession::new(log.clone()).unwrap();
|
||||||
let session = Rc::new(RefCell::new(session));
|
|
||||||
|
|
||||||
let running = Arc::new(AtomicBool::new(true));
|
let running = Arc::new(AtomicBool::new(true));
|
||||||
let r = running.clone();
|
|
||||||
ctrlc::set_handler(move || {
|
|
||||||
r.store(false, Ordering::SeqCst);
|
|
||||||
}).expect("Error setting Ctrl-C handler");
|
|
||||||
|
|
||||||
let pointer_location = Rc::new(RefCell::new((0.0, 0.0)));
|
let pointer_location = Rc::new(RefCell::new((0.0, 0.0)));
|
||||||
|
|
||||||
|
@ -317,9 +335,8 @@ fn main() {
|
||||||
/*
|
/*
|
||||||
* Initialize libinput backend
|
* Initialize libinput backend
|
||||||
*/
|
*/
|
||||||
let mut libinput_context = Libinput::new_from_udev::<
|
let mut libinput_context =
|
||||||
LibinputSessionInterface<Rc<RefCell<DirectSession>>>,
|
Libinput::new_from_udev::<LibinputSessionInterface<AutoSession>>(session.clone().into(), &context);
|
||||||
>(session.into(), &context);
|
|
||||||
let libinput_session_id = notifier.register(libinput_context.clone());
|
let libinput_session_id = notifier.register(libinput_context.clone());
|
||||||
libinput_context.udev_assign_seat(&seat).unwrap();
|
libinput_context.udev_assign_seat(&seat).unwrap();
|
||||||
let mut libinput_backend = LibinputInputBackend::new(libinput_context, log.clone());
|
let mut libinput_backend = LibinputInputBackend::new(libinput_context, log.clone());
|
||||||
|
@ -333,12 +350,13 @@ fn main() {
|
||||||
pointer_location,
|
pointer_location,
|
||||||
screen_size: (w, h),
|
screen_size: (w, h),
|
||||||
serial: 0,
|
serial: 0,
|
||||||
|
session: session,
|
||||||
running: running.clone(),
|
running: running.clone(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
let libinput_event_source = libinput_bind(libinput_backend, &mut event_loop).unwrap();
|
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 = auto_session_bind(notifier, &mut event_loop).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();
|
||||||
|
|
||||||
while running.load(Ordering::SeqCst) {
|
while running.load(Ordering::SeqCst) {
|
||||||
|
@ -352,7 +370,7 @@ fn main() {
|
||||||
|
|
||||||
println!("Bye Bye");
|
println!("Bye Bye");
|
||||||
|
|
||||||
let mut notifier = session_event_source.remove();
|
let mut notifier = session_event_source.unbind();
|
||||||
notifier.unregister(udev_session_id);
|
notifier.unregister(udev_session_id);
|
||||||
notifier.unregister(libinput_session_id);
|
notifier.unregister(libinput_session_id);
|
||||||
|
|
||||||
|
|
|
@ -24,8 +24,8 @@ pub struct DrmBackend<A: Device + 'static> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct DrmBackendInternal<A: Device + 'static> {
|
pub(crate) struct DrmBackendInternal<A: Device + 'static> {
|
||||||
context: Rc<EGLContext<Gbm<framebuffer::Info>, GbmDevice<A>>>,
|
pub(crate) context: Rc<EGLContext<Gbm<framebuffer::Info>, GbmDevice<A>>>,
|
||||||
cursor: Cell<BufferObject<()>>,
|
pub(crate) cursor: Cell<(BufferObject<()>, (u32, u32))>,
|
||||||
current_frame_buffer: Cell<framebuffer::Info>,
|
current_frame_buffer: Cell<framebuffer::Info>,
|
||||||
front_buffer: Cell<SurfaceBufferHandle<framebuffer::Info>>,
|
front_buffer: Cell<SurfaceBufferHandle<framebuffer::Info>>,
|
||||||
next_buffer: Cell<Option<SurfaceBufferHandle<framebuffer::Info>>>,
|
next_buffer: Cell<Option<SurfaceBufferHandle<framebuffer::Info>>>,
|
||||||
|
@ -96,14 +96,17 @@ impl<A: Device + 'static> DrmBackend<A> {
|
||||||
})?;
|
})?;
|
||||||
front_bo.set_userdata(fb).unwrap();
|
front_bo.set_userdata(fb).unwrap();
|
||||||
|
|
||||||
let cursor = Cell::new(context
|
let cursor = Cell::new((
|
||||||
.create_buffer_object(
|
context
|
||||||
1,
|
.create_buffer_object(
|
||||||
1,
|
1,
|
||||||
GbmFormat::ARGB8888,
|
1,
|
||||||
BufferObjectFlags::CURSOR | BufferObjectFlags::WRITE,
|
GbmFormat::ARGB8888,
|
||||||
)
|
BufferObjectFlags::CURSOR | BufferObjectFlags::WRITE,
|
||||||
.chain_err(|| ErrorKind::GbmInitFailed)?);
|
)
|
||||||
|
.chain_err(|| ErrorKind::GbmInitFailed)?,
|
||||||
|
(0, 0),
|
||||||
|
));
|
||||||
|
|
||||||
Ok(DrmBackend {
|
Ok(DrmBackend {
|
||||||
backend: Rc::new(DrmBackendInternal {
|
backend: Rc::new(DrmBackendInternal {
|
||||||
|
@ -455,7 +458,7 @@ impl<A: Device + 'static> GraphicsBackend for DrmBackend<A> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// and store it
|
// and store it
|
||||||
self.backend.cursor.set(cursor);
|
self.backend.cursor.set((cursor, hotspot));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -219,9 +219,9 @@ use drm::control::{connector, crtc, encoder, Mode, ResourceInfo};
|
||||||
use drm::control::Device as ControlDevice;
|
use drm::control::Device as ControlDevice;
|
||||||
use drm::control::framebuffer;
|
use drm::control::framebuffer;
|
||||||
use drm::result::Error as DrmError;
|
use drm::result::Error as DrmError;
|
||||||
use gbm::Device as GbmDevice;
|
use gbm::{BufferObject, Device as GbmDevice};
|
||||||
use nix;
|
use nix;
|
||||||
use nix::sys::stat::{dev_t, fstat};
|
use nix::sys::stat::{self, dev_t, fstat};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::io::Result as IoResult;
|
use std::io::Result as IoResult;
|
||||||
|
@ -251,6 +251,7 @@ pub struct DrmDevice<A: ControlDevice + 'static> {
|
||||||
device_id: dev_t,
|
device_id: dev_t,
|
||||||
backends: HashMap<crtc::Handle, Weak<DrmBackendInternal<A>>>,
|
backends: HashMap<crtc::Handle, Weak<DrmBackendInternal<A>>>,
|
||||||
active: bool,
|
active: bool,
|
||||||
|
priviledged: bool,
|
||||||
logger: ::slog::Logger,
|
logger: ::slog::Logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -319,13 +320,20 @@ impl<A: ControlDevice + 'static> DrmDevice<A> {
|
||||||
device_id,
|
device_id,
|
||||||
old_state: HashMap::new(),
|
old_state: HashMap::new(),
|
||||||
active: true,
|
active: true,
|
||||||
|
priviledged: true,
|
||||||
logger: log.clone(),
|
logger: log.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
info!(log, "DrmDevice initializing");
|
info!(log, "DrmDevice initializing");
|
||||||
|
|
||||||
// we want to mode-set, so we better be the master
|
// we want to mode-set, so we better be the master, if we run via a tty session
|
||||||
drm.set_master().chain_err(|| ErrorKind::DrmMasterFailed)?;
|
if let Err(_) = drm.set_master() {
|
||||||
|
warn!(
|
||||||
|
log,
|
||||||
|
"Unable to become drm master, assuming unpriviledged mode"
|
||||||
|
);
|
||||||
|
drm.priviledged = false;
|
||||||
|
};
|
||||||
|
|
||||||
let res_handles = drm.resource_handles().chain_err(|| {
|
let res_handles = drm.resource_handles().chain_err(|| {
|
||||||
ErrorKind::DrmDev(format!(
|
ErrorKind::DrmDev(format!(
|
||||||
|
@ -506,11 +514,13 @@ impl<A: ControlDevice + 'static> Drop for DrmDevice<A> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Err(err) = self.drop_master() {
|
if self.priviledged {
|
||||||
error!(
|
if let Err(err) = self.drop_master() {
|
||||||
self.logger,
|
error!(
|
||||||
"Failed to drop drm master state. Error: {}", err
|
self.logger,
|
||||||
);
|
"Failed to drop drm master state. Error: {}", err
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -597,26 +607,61 @@ where
|
||||||
|
|
||||||
#[cfg(feature = "backend_session")]
|
#[cfg(feature = "backend_session")]
|
||||||
impl<A: ControlDevice + 'static> SessionObserver for StateToken<DrmDevice<A>> {
|
impl<A: ControlDevice + 'static> SessionObserver for StateToken<DrmDevice<A>> {
|
||||||
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);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (handle, &(ref info, ref connectors)) in device.old_state.iter() {
|
||||||
|
if let Err(err) = crtc::set(
|
||||||
|
&*device.context,
|
||||||
|
*handle,
|
||||||
|
info.fb(),
|
||||||
|
connectors,
|
||||||
|
info.position(),
|
||||||
|
info.mode(),
|
||||||
|
) {
|
||||||
|
error!(
|
||||||
|
device.logger,
|
||||||
|
"Failed to reset crtc ({:?}). Error: {}", handle, err
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
device.active = false;
|
device.active = false;
|
||||||
if let Err(err) = device.drop_master() {
|
if device.priviledged {
|
||||||
error!(
|
if let Err(err) = device.drop_master() {
|
||||||
device.logger,
|
error!(
|
||||||
"Failed to drop drm master state. Error: {}", err
|
device.logger,
|
||||||
);
|
"Failed to drop drm master state. Error: {}", err
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn activate<'a>(&mut self, state: &mut StateProxy<'a>) {
|
fn activate<'a>(&mut self, state: &mut StateProxy<'a>, devnum: Option<(u32, u32, Option<RawFd>)>) {
|
||||||
let device = state.get_mut(self);
|
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 {
|
||||||
|
info!(device.logger, "Replacing fd");
|
||||||
|
nix::unistd::dup2(device.as_raw_fd(), fd)
|
||||||
|
.expect("Failed to replace file descriptor of drm device");
|
||||||
|
}
|
||||||
|
}
|
||||||
device.active = true;
|
device.active = true;
|
||||||
if let Err(err) = device.set_master() {
|
if device.priviledged {
|
||||||
crit!(
|
if let Err(err) = device.set_master() {
|
||||||
device.logger,
|
crit!(
|
||||||
"Failed to acquire drm master again. Error: {}",
|
device.logger,
|
||||||
err
|
"Failed to acquire drm master again. Error: {}",
|
||||||
);
|
err
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let mut crtcs = Vec::new();
|
let mut crtcs = Vec::new();
|
||||||
for (crtc, backend) in device.backends.iter() {
|
for (crtc, backend) in device.backends.iter() {
|
||||||
|
@ -628,6 +673,22 @@ impl<A: ControlDevice + 'static> SessionObserver for StateToken<DrmDevice<A>> {
|
||||||
"Failed to activate crtc ({:?}) again. Error: {}", crtc, err
|
"Failed to activate crtc ({:?}) again. Error: {}", crtc, err
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
// reset cursor
|
||||||
|
{
|
||||||
|
let &(ref cursor, ref hotspot): &(BufferObject<()>, (u32, u32)) =
|
||||||
|
unsafe { &*backend.cursor.as_ptr() };
|
||||||
|
if crtc::set_cursor2(
|
||||||
|
&*backend.context,
|
||||||
|
*crtc,
|
||||||
|
cursor,
|
||||||
|
((*hotspot).0 as i32, (*hotspot).1 as i32),
|
||||||
|
).is_err()
|
||||||
|
{
|
||||||
|
if let Err(err) = crtc::set_cursor(&*backend.context, *crtc, cursor) {
|
||||||
|
error!(device.logger, "Failed to reset cursor. Error: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
crtcs.push(*crtc);
|
crtcs.push(*crtc);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,12 @@ use std::rc::Rc;
|
||||||
use wayland_server::{EventLoopHandle, StateProxy};
|
use wayland_server::{EventLoopHandle, StateProxy};
|
||||||
use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest};
|
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`.
|
/// Libinput based `InputBackend`.
|
||||||
///
|
///
|
||||||
/// Tracks input of all devices given manually or via a udev seat to a provided libinput
|
/// Tracks input of all devices given manually or via a udev seat to a provided libinput
|
||||||
|
@ -375,7 +381,8 @@ impl backend::InputBackend for LibinputInputBackend {
|
||||||
.filter(|x| x.seat() == device_seat)
|
.filter(|x| x.seat() == device_seat)
|
||||||
.any(|x| x.has_capability(libinput::DeviceCapability::Touch));
|
.any(|x| x.has_capability(libinput::DeviceCapability::Touch));
|
||||||
} else {
|
} else {
|
||||||
panic!("Seat changed that was never created")
|
warn!(self.logger, "Seat changed that was never created");
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if the seat has any other devices
|
// check if the seat has any other devices
|
||||||
|
@ -387,13 +394,18 @@ impl backend::InputBackend for LibinputInputBackend {
|
||||||
handler.on_seat_destroyed(evlh, &seat);
|
handler.on_seat_destroyed(evlh, &seat);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
panic!("Seat destroyed that was never created");
|
warn!(self.logger, "Seat destroyed that was never created");
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
// it has, notify about updates
|
// it has, notify about updates
|
||||||
} else if let Some(ref mut handler) = self.handler {
|
} else if let Some(ref mut handler) = self.handler {
|
||||||
let seat = &self.seats[&device_seat];
|
if let Some(seat) = self.seats.get(&device_seat) {
|
||||||
trace!(self.logger, "Calling on_seat_changed with {:?}", seat);
|
trace!(self.logger, "Calling on_seat_changed with {:?}", seat);
|
||||||
handler.on_seat_changed(evlh, seat);
|
handler.on_seat_changed(evlh, &seat);
|
||||||
|
} else {
|
||||||
|
warn!(self.logger, "Seat changed that was never created");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -405,38 +417,40 @@ impl backend::InputBackend for LibinputInputBackend {
|
||||||
use input::event::touch::*;
|
use input::event::touch::*;
|
||||||
if let Some(ref mut handler) = self.handler {
|
if let Some(ref mut handler) = self.handler {
|
||||||
let device_seat = touch_event.device().seat();
|
let device_seat = touch_event.device().seat();
|
||||||
let seat = &self.seats
|
if let &Some(ref seat) = &self.seats.get(&device_seat) {
|
||||||
.get(&device_seat)
|
match touch_event {
|
||||||
.expect("Recieved touch event of non existing Seat");
|
TouchEvent::Down(down_event) => {
|
||||||
match touch_event {
|
trace!(self.logger, "Calling on_touch_down with {:?}", down_event);
|
||||||
TouchEvent::Down(down_event) => {
|
handler.on_touch_down(evlh, seat, down_event)
|
||||||
trace!(self.logger, "Calling on_touch_down with {:?}", down_event);
|
}
|
||||||
handler.on_touch_down(evlh, seat, down_event)
|
TouchEvent::Motion(motion_event) => {
|
||||||
}
|
trace!(
|
||||||
TouchEvent::Motion(motion_event) => {
|
self.logger,
|
||||||
trace!(
|
"Calling on_touch_motion with {:?}",
|
||||||
self.logger,
|
motion_event
|
||||||
"Calling on_touch_motion with {:?}",
|
);
|
||||||
motion_event
|
handler.on_touch_motion(evlh, seat, motion_event)
|
||||||
);
|
}
|
||||||
handler.on_touch_motion(evlh, seat, motion_event)
|
TouchEvent::Up(up_event) => {
|
||||||
}
|
trace!(self.logger, "Calling on_touch_up with {:?}", up_event);
|
||||||
TouchEvent::Up(up_event) => {
|
handler.on_touch_up(evlh, seat, up_event)
|
||||||
trace!(self.logger, "Calling on_touch_up with {:?}", up_event);
|
}
|
||||||
handler.on_touch_up(evlh, seat, up_event)
|
TouchEvent::Cancel(cancel_event) => {
|
||||||
}
|
trace!(
|
||||||
TouchEvent::Cancel(cancel_event) => {
|
self.logger,
|
||||||
trace!(
|
"Calling on_touch_cancel with {:?}",
|
||||||
self.logger,
|
cancel_event
|
||||||
"Calling on_touch_cancel with {:?}",
|
);
|
||||||
cancel_event
|
handler.on_touch_cancel(evlh, seat, cancel_event)
|
||||||
);
|
}
|
||||||
handler.on_touch_cancel(evlh, seat, cancel_event)
|
TouchEvent::Frame(frame_event) => {
|
||||||
}
|
trace!(self.logger, "Calling on_touch_frame with {:?}", frame_event);
|
||||||
TouchEvent::Frame(frame_event) => {
|
handler.on_touch_frame(evlh, seat, frame_event)
|
||||||
trace!(self.logger, "Calling on_touch_frame with {:?}", frame_event);
|
}
|
||||||
handler.on_touch_frame(evlh, seat, frame_event)
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
warn!(self.logger, "Recieved touch event of non existing Seat");
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -445,11 +459,13 @@ impl backend::InputBackend for LibinputInputBackend {
|
||||||
match keyboard_event {
|
match keyboard_event {
|
||||||
KeyboardEvent::Key(key_event) => if let Some(ref mut handler) = self.handler {
|
KeyboardEvent::Key(key_event) => if let Some(ref mut handler) = self.handler {
|
||||||
let device_seat = key_event.device().seat();
|
let device_seat = key_event.device().seat();
|
||||||
let seat = &self.seats
|
if let &Some(ref seat) = &self.seats.get(&device_seat) {
|
||||||
.get(&device_seat)
|
trace!(self.logger, "Calling on_keyboard_key with {:?}", key_event);
|
||||||
.expect("Recieved key event of non existing Seat");
|
handler.on_keyboard_key(evlh, seat, key_event);
|
||||||
trace!(self.logger, "Calling on_keyboard_key with {:?}", key_event);
|
} else {
|
||||||
handler.on_keyboard_key(evlh, seat, key_event);
|
warn!(self.logger, "Recieved key event of non existing Seat");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -457,67 +473,69 @@ impl backend::InputBackend for LibinputInputBackend {
|
||||||
use input::event::pointer::*;
|
use input::event::pointer::*;
|
||||||
if let Some(ref mut handler) = self.handler {
|
if let Some(ref mut handler) = self.handler {
|
||||||
let device_seat = pointer_event.device().seat();
|
let device_seat = pointer_event.device().seat();
|
||||||
let seat = &self.seats
|
if let &Some(ref seat) = &self.seats.get(&device_seat) {
|
||||||
.get(&device_seat)
|
match pointer_event {
|
||||||
.expect("Recieved pointer event of non existing Seat");
|
PointerEvent::Motion(motion_event) => {
|
||||||
match pointer_event {
|
|
||||||
PointerEvent::Motion(motion_event) => {
|
|
||||||
trace!(
|
|
||||||
self.logger,
|
|
||||||
"Calling on_pointer_move with {:?}",
|
|
||||||
motion_event
|
|
||||||
);
|
|
||||||
handler.on_pointer_move(evlh, seat, motion_event);
|
|
||||||
}
|
|
||||||
PointerEvent::MotionAbsolute(motion_abs_event) => {
|
|
||||||
trace!(
|
|
||||||
self.logger,
|
|
||||||
"Calling on_pointer_move_absolute with {:?}",
|
|
||||||
motion_abs_event
|
|
||||||
);
|
|
||||||
handler.on_pointer_move_absolute(evlh, seat, motion_abs_event);
|
|
||||||
}
|
|
||||||
PointerEvent::Axis(axis_event) => {
|
|
||||||
let rc_axis_event = Rc::new(axis_event);
|
|
||||||
if rc_axis_event.has_axis(Axis::Vertical) {
|
|
||||||
trace!(
|
trace!(
|
||||||
self.logger,
|
self.logger,
|
||||||
"Calling on_pointer_axis for Axis::Vertical with {:?}",
|
"Calling on_pointer_move with {:?}",
|
||||||
*rc_axis_event
|
motion_event
|
||||||
);
|
|
||||||
handler.on_pointer_axis(
|
|
||||||
evlh,
|
|
||||||
seat,
|
|
||||||
self::PointerAxisEvent {
|
|
||||||
axis: Axis::Vertical,
|
|
||||||
event: rc_axis_event.clone(),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
handler.on_pointer_move(evlh, seat, motion_event);
|
||||||
}
|
}
|
||||||
if rc_axis_event.has_axis(Axis::Horizontal) {
|
PointerEvent::MotionAbsolute(motion_abs_event) => {
|
||||||
trace!(
|
trace!(
|
||||||
self.logger,
|
self.logger,
|
||||||
"Calling on_pointer_axis for Axis::Horizontal with {:?}",
|
"Calling on_pointer_move_absolute with {:?}",
|
||||||
*rc_axis_event
|
motion_abs_event
|
||||||
);
|
);
|
||||||
handler.on_pointer_axis(
|
handler.on_pointer_move_absolute(evlh, seat, motion_abs_event);
|
||||||
evlh,
|
}
|
||||||
seat,
|
PointerEvent::Axis(axis_event) => {
|
||||||
self::PointerAxisEvent {
|
let rc_axis_event = Rc::new(axis_event);
|
||||||
axis: Axis::Horizontal,
|
if rc_axis_event.has_axis(Axis::Vertical) {
|
||||||
event: rc_axis_event.clone(),
|
trace!(
|
||||||
},
|
self.logger,
|
||||||
|
"Calling on_pointer_axis for Axis::Vertical with {:?}",
|
||||||
|
*rc_axis_event
|
||||||
|
);
|
||||||
|
handler.on_pointer_axis(
|
||||||
|
evlh,
|
||||||
|
seat,
|
||||||
|
self::PointerAxisEvent {
|
||||||
|
axis: Axis::Vertical,
|
||||||
|
event: rc_axis_event.clone(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if rc_axis_event.has_axis(Axis::Horizontal) {
|
||||||
|
trace!(
|
||||||
|
self.logger,
|
||||||
|
"Calling on_pointer_axis for Axis::Horizontal with {:?}",
|
||||||
|
*rc_axis_event
|
||||||
|
);
|
||||||
|
handler.on_pointer_axis(
|
||||||
|
evlh,
|
||||||
|
seat,
|
||||||
|
self::PointerAxisEvent {
|
||||||
|
axis: Axis::Horizontal,
|
||||||
|
event: rc_axis_event.clone(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PointerEvent::Button(button_event) => {
|
||||||
|
trace!(
|
||||||
|
self.logger,
|
||||||
|
"Calling on_pointer_button with {:?}",
|
||||||
|
button_event
|
||||||
);
|
);
|
||||||
|
handler.on_pointer_button(evlh, seat, button_event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PointerEvent::Button(button_event) => {
|
} else {
|
||||||
trace!(
|
warn!(self.logger, "Recieved pointer event of non existing Seat");
|
||||||
self.logger,
|
continue;
|
||||||
"Calling on_pointer_button with {:?}",
|
|
||||||
button_event
|
|
||||||
);
|
|
||||||
handler.on_pointer_button(evlh, seat, button_event);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -568,12 +586,19 @@ impl From<event::pointer::ButtonState> for backend::MouseButtonState {
|
||||||
|
|
||||||
#[cfg(feature = "backend_session")]
|
#[cfg(feature = "backend_session")]
|
||||||
impl SessionObserver for libinput::Libinput {
|
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()
|
self.suspend()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn activate<'a>(&mut self, _state: &mut StateProxy<'a>) {
|
fn activate<'a>(&mut self, _state: &mut StateProxy<'a>, _device: Option<(u32, u32, Option<RawFd>)>) {
|
||||||
// TODO Is this the best way to handle this failure?
|
// 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");
|
self.resume().expect("Unable to resume libinput context");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,276 @@
|
||||||
|
//!
|
||||||
|
//! Implementation of the `Session` trait through various implementations
|
||||||
|
//! automatically choosing the best available interface.
|
||||||
|
//!
|
||||||
|
//! ## How to use it
|
||||||
|
//!
|
||||||
|
//! ### Initialization
|
||||||
|
//!
|
||||||
|
//! To initialize a session just call `AutoSession::new`. A new session will be opened, if the
|
||||||
|
//! any available interface is successful and will be closed once the `AutoSessionNotifier` is dropped.
|
||||||
|
//!
|
||||||
|
//! ### Usage of the session
|
||||||
|
//!
|
||||||
|
//! The session may be used to open devices manually through the `Session` interface
|
||||||
|
//! or be passed to other objects that need it to open devices themselves.
|
||||||
|
//! The `AutoSession` is clonable and may be passed to multiple devices easily.
|
||||||
|
//!
|
||||||
|
//! Examples for those are e.g. the `LibinputInputBackend` (its context might be initialized through a
|
||||||
|
//! `Session` via the `LibinputSessionInterface`) or the `UdevBackend`.
|
||||||
|
//!
|
||||||
|
//! ### Usage of the session notifier
|
||||||
|
//!
|
||||||
|
//! The notifier might be used to pause device access, when the session gets paused (e.g. by
|
||||||
|
//! switching the tty via `AutoSession::change_vt`) and to automatically enable it again,
|
||||||
|
//! when the session becomes active again.
|
||||||
|
//!
|
||||||
|
//! It is crutial to avoid errors during that state. Examples for object that might be registered
|
||||||
|
//! for notifications are the `Libinput` context, the `UdevBackend` or a `DrmDevice` (handled
|
||||||
|
//! automatically by the `UdevBackend`, if not done manually).
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
use super::{AsErrno, Session, SessionNotifier, SessionObserver};
|
||||||
|
use super::direct::{self, direct_session_bind, DirectSession, DirectSessionNotifier};
|
||||||
|
#[cfg(feature = "backend_session_logind")]
|
||||||
|
use super::logind::{self, logind_session_bind, BoundLogindSession, LogindSession, LogindSessionNotifier};
|
||||||
|
use nix::fcntl::OFlag;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::io::Result as IoResult;
|
||||||
|
use std::os::unix::io::RawFd;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use wayland_server::EventLoopHandle;
|
||||||
|
use wayland_server::sources::SignalEventSource;
|
||||||
|
|
||||||
|
/// `Session` using the best available inteface
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum AutoSession {
|
||||||
|
/// Logind session
|
||||||
|
#[cfg(feature = "backend_session_logind")]
|
||||||
|
Logind(LogindSession),
|
||||||
|
/// Direct / tty session
|
||||||
|
Direct(Rc<RefCell<DirectSession>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `SessionNotifier` using the best available inteface
|
||||||
|
pub enum AutoSessionNotifier {
|
||||||
|
/// Logind session nofifier
|
||||||
|
#[cfg(feature = "backend_session_logind")]
|
||||||
|
Logind(LogindSessionNotifier),
|
||||||
|
/// Direct / tty session notifier
|
||||||
|
Direct(DirectSessionNotifier),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bound session that is driven by the `wayland_server::EventLoop`.
|
||||||
|
///
|
||||||
|
/// See `auto_session_bind` for details.
|
||||||
|
///
|
||||||
|
/// Dropping this object will close the session just like the `AutoSessionNotifier`.
|
||||||
|
pub enum BoundAutoSession {
|
||||||
|
/// Bound logind session
|
||||||
|
#[cfg(feature = "backend_session_logind")]
|
||||||
|
Logind(BoundLogindSession),
|
||||||
|
/// Bound direct / tty session
|
||||||
|
Direct(SignalEventSource<DirectSessionNotifier>),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Id's used by the `AutoSessionNotifier` internally.
|
||||||
|
#[derive(PartialEq, Eq)]
|
||||||
|
pub struct AutoId(AutoIdInternal);
|
||||||
|
#[derive(PartialEq, Eq)]
|
||||||
|
enum AutoIdInternal {
|
||||||
|
#[cfg(feature = "backend_session_logind")]
|
||||||
|
Logind(logind::Id),
|
||||||
|
Direct(direct::Id),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AutoSession {
|
||||||
|
/// Tries to create a new session via the best available interface.
|
||||||
|
#[cfg(feature = "backend_session_logind")]
|
||||||
|
pub fn new<L>(logger: L) -> Option<(AutoSession, AutoSessionNotifier)>
|
||||||
|
where
|
||||||
|
L: Into<Option<::slog::Logger>>,
|
||||||
|
{
|
||||||
|
let logger = ::slog_or_stdlog(logger)
|
||||||
|
.new(o!("smithay_module" => "backend_session_auto", "session_type" => "auto"));
|
||||||
|
|
||||||
|
info!(logger, "Trying to create logind session");
|
||||||
|
match LogindSession::new(logger.clone()) {
|
||||||
|
Ok((session, notifier)) => Some((
|
||||||
|
AutoSession::Logind(session),
|
||||||
|
AutoSessionNotifier::Logind(notifier),
|
||||||
|
)),
|
||||||
|
Err(err) => {
|
||||||
|
warn!(logger, "Failed to create logind session: {}", err);
|
||||||
|
info!(logger, "Falling back to create tty session");
|
||||||
|
match DirectSession::new(None, logger.clone()) {
|
||||||
|
Ok((session, notifier)) => Some((
|
||||||
|
AutoSession::Direct(Rc::new(RefCell::new(session))),
|
||||||
|
AutoSessionNotifier::Direct(notifier),
|
||||||
|
)),
|
||||||
|
Err(err) => {
|
||||||
|
warn!(logger, "Failed to create direct session: {}", err);
|
||||||
|
error!(
|
||||||
|
logger,
|
||||||
|
"Could not create any session, possibilities exhausted"
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "backend_session_logind"))]
|
||||||
|
pub fn new<L>(logger: L) -> Option<(AutoSession, AutoSessionNotifier)>
|
||||||
|
where
|
||||||
|
L: Into<Option<::slog::Logger>>,
|
||||||
|
{
|
||||||
|
let logger = ::slog_or_stdlog(logger)
|
||||||
|
.new(o!("smithay_module" => "backend_session_auto", "session_type" => "auto"));
|
||||||
|
|
||||||
|
info!(logger, "Trying to create tty session");
|
||||||
|
match DirectSession::new(None, logger.clone()) {
|
||||||
|
Ok((session, notifier)) => Some((
|
||||||
|
AutoSession::Direct(Rc::new(RefCell::new(session))),
|
||||||
|
AutoSessionNotifier::Direct(notifier),
|
||||||
|
)),
|
||||||
|
Err(err) => {
|
||||||
|
warn!(logger, "Failed to create direct session: {}", err);
|
||||||
|
error!(
|
||||||
|
logger,
|
||||||
|
"Could not create any session, possibilities exhausted"
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bind an `AutoSessionNotifier` to an `EventLoop`.
|
||||||
|
///
|
||||||
|
/// Allows the `AutoSessionNotifier` to listen for incoming signals signalling the session state.
|
||||||
|
/// If you don't use this function `AutoSessionNotifier` will not correctly tell you the
|
||||||
|
/// session state and call it's `SessionObservers`.
|
||||||
|
pub fn auto_session_bind(
|
||||||
|
notifier: AutoSessionNotifier, evlh: &mut EventLoopHandle
|
||||||
|
) -> IoResult<BoundAutoSession> {
|
||||||
|
Ok(match notifier {
|
||||||
|
#[cfg(feature = "backend_session_logind")]
|
||||||
|
AutoSessionNotifier::Logind(logind) => BoundAutoSession::Logind(logind_session_bind(logind, evlh)?),
|
||||||
|
AutoSessionNotifier::Direct(direct) => BoundAutoSession::Direct(direct_session_bind(direct, evlh)?),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Session for AutoSession {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn open(&mut self, path: &Path, flags: OFlag) -> Result<RawFd> {
|
||||||
|
match self {
|
||||||
|
#[cfg(feature = "backend_session_logind")]
|
||||||
|
&mut AutoSession::Logind(ref mut logind) => logind.open(path, flags).map_err(|e| e.into()),
|
||||||
|
&mut AutoSession::Direct(ref mut direct) => direct.open(path, flags).map_err(|e| e.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn close(&mut self, fd: RawFd) -> Result<()> {
|
||||||
|
match self {
|
||||||
|
#[cfg(feature = "backend_session_logind")]
|
||||||
|
&mut AutoSession::Logind(ref mut logind) => logind.close(fd).map_err(|e| e.into()),
|
||||||
|
&mut AutoSession::Direct(ref mut direct) => direct.close(fd).map_err(|e| e.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change_vt(&mut self, vt: i32) -> Result<()> {
|
||||||
|
match self {
|
||||||
|
#[cfg(feature = "backend_session_logind")]
|
||||||
|
&mut AutoSession::Logind(ref mut logind) => logind.change_vt(vt).map_err(|e| e.into()),
|
||||||
|
&mut AutoSession::Direct(ref mut direct) => direct.change_vt(vt).map_err(|e| e.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_active(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
#[cfg(feature = "backend_session_logind")]
|
||||||
|
&AutoSession::Logind(ref logind) => logind.is_active(),
|
||||||
|
&AutoSession::Direct(ref direct) => direct.is_active(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn seat(&self) -> String {
|
||||||
|
match self {
|
||||||
|
#[cfg(feature = "backend_session_logind")]
|
||||||
|
&AutoSession::Logind(ref logind) => logind.seat(),
|
||||||
|
&AutoSession::Direct(ref direct) => direct.seat(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SessionNotifier for AutoSessionNotifier {
|
||||||
|
type Id = AutoId;
|
||||||
|
|
||||||
|
fn register<S: SessionObserver + 'static>(&mut self, signal: S) -> Self::Id {
|
||||||
|
match self {
|
||||||
|
#[cfg(feature = "backend_session_logind")]
|
||||||
|
&mut AutoSessionNotifier::Logind(ref mut logind) => {
|
||||||
|
AutoId(AutoIdInternal::Logind(logind.register(signal)))
|
||||||
|
}
|
||||||
|
&mut AutoSessionNotifier::Direct(ref mut direct) => {
|
||||||
|
AutoId(AutoIdInternal::Direct(direct.register(signal)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn unregister(&mut self, signal: Self::Id) {
|
||||||
|
match (self, signal) {
|
||||||
|
#[cfg(feature = "backend_session_logind")]
|
||||||
|
(&mut AutoSessionNotifier::Logind(ref mut logind), AutoId(AutoIdInternal::Logind(signal))) => {
|
||||||
|
logind.unregister(signal)
|
||||||
|
}
|
||||||
|
(&mut AutoSessionNotifier::Direct(ref mut direct), AutoId(AutoIdInternal::Direct(signal))) => {
|
||||||
|
direct.unregister(signal)
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_active(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
#[cfg(feature = "backend_session_logind")]
|
||||||
|
&AutoSessionNotifier::Logind(ref logind) => logind.is_active(),
|
||||||
|
&AutoSessionNotifier::Direct(ref direct) => direct.is_active(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn seat(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
#[cfg(feature = "backend_session_logind")]
|
||||||
|
&AutoSessionNotifier::Logind(ref logind) => logind.seat(),
|
||||||
|
&AutoSessionNotifier::Direct(ref direct) => direct.seat(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BoundAutoSession {
|
||||||
|
/// Unbind the session from the `EventLoop` again
|
||||||
|
pub fn unbind(self) -> AutoSessionNotifier {
|
||||||
|
match self {
|
||||||
|
#[cfg(feature = "backend_session_logind")]
|
||||||
|
BoundAutoSession::Logind(logind) => AutoSessionNotifier::Logind(logind.unbind()),
|
||||||
|
BoundAutoSession::Direct(source) => AutoSessionNotifier::Direct(source.remove()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
error_chain! {
|
||||||
|
links {
|
||||||
|
Logind(logind::Error, logind::ErrorKind) #[cfg(feature = "backend_session_logind")] #[doc = "Underlying logind session error"];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreign_links {
|
||||||
|
Direct(::nix::Error) #[doc = "Underlying direct tty session error"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsErrno for Error {
|
||||||
|
fn as_errno(&self) -> Option<i32> {
|
||||||
|
//TODO figure this out, I don't see a way..
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,576 @@
|
||||||
|
//!
|
||||||
|
//! Implementation of the `Session` trait through the logind dbus interface.
|
||||||
|
//!
|
||||||
|
//! This requires systemd and dbus to be available and started on the system.
|
||||||
|
//!
|
||||||
|
//! ## How to use it
|
||||||
|
//!
|
||||||
|
//! ### Initialization
|
||||||
|
//!
|
||||||
|
//! To initialize a session just call `LogindSession::new`. A new session will be opened, if the
|
||||||
|
//! call is successful and will be closed once the `LogindSessionNotifier` is dropped.
|
||||||
|
//!
|
||||||
|
//! ### Usage of the session
|
||||||
|
//!
|
||||||
|
//! The session may be used to open devices manually through the `Session` interface
|
||||||
|
//! or be passed to other objects that need it to open devices themselves.
|
||||||
|
//! The `LogindSession` is clonable and may be passed to multiple devices easily.
|
||||||
|
//!
|
||||||
|
//! Examples for those are e.g. the `LibinputInputBackend` (its context might be initialized through a
|
||||||
|
//! `Session` via the `LibinputSessionInterface`) or the `UdevBackend`.
|
||||||
|
//!
|
||||||
|
//! ### Usage of the session notifier
|
||||||
|
//!
|
||||||
|
//! The notifier might be used to pause device access, when the session gets paused (e.g. by
|
||||||
|
//! switching the tty via `LogindSession::change_vt`) and to automatically enable it again,
|
||||||
|
//! when the session becomes active again.
|
||||||
|
//!
|
||||||
|
//! It is crutial to avoid errors during that state. Examples for object that might be registered
|
||||||
|
//! for notifications are the `Libinput` context, the `UdevBackend` or a `DrmDevice` (handled
|
||||||
|
//! automatically by the `UdevBackend`, if not done manually).
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
use backend::session::{AsErrno, Session, SessionNotifier, SessionObserver};
|
||||||
|
use dbus::{BusName, BusType, Connection, ConnectionItem, ConnectionItems, Interface, Member, Message,
|
||||||
|
MessageItem, OwnedFd, Path as DbusPath, Watch, WatchEvent};
|
||||||
|
use nix::fcntl::OFlag;
|
||||||
|
use nix::sys::stat::{fstat, major, minor, stat};
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::io::Result as IoResult;
|
||||||
|
use std::os::unix::io::RawFd;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::rc::{Rc, Weak};
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use systemd::login;
|
||||||
|
use wayland_server::EventLoopHandle;
|
||||||
|
use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest};
|
||||||
|
|
||||||
|
struct LogindSessionImpl {
|
||||||
|
conn: RefCell<Connection>,
|
||||||
|
session_path: DbusPath<'static>,
|
||||||
|
active: AtomicBool,
|
||||||
|
signals: RefCell<Vec<Option<Box<SessionObserver>>>>,
|
||||||
|
seat: String,
|
||||||
|
logger: ::slog::Logger,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Session` via the logind dbus interface
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct LogindSession {
|
||||||
|
internal: Weak<LogindSessionImpl>,
|
||||||
|
seat: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `SessionNotifier` via the logind dbus interface
|
||||||
|
pub struct LogindSessionNotifier {
|
||||||
|
internal: Rc<LogindSessionImpl>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LogindSession {
|
||||||
|
/// Tries to create a new session via the logind dbus interface.
|
||||||
|
pub fn new<L>(logger: L) -> Result<(LogindSession, LogindSessionNotifier)>
|
||||||
|
where
|
||||||
|
L: Into<Option<::slog::Logger>>,
|
||||||
|
{
|
||||||
|
let logger = ::slog_or_stdlog(logger)
|
||||||
|
.new(o!("smithay_module" => "backend_session", "session_type" => "logind"));
|
||||||
|
|
||||||
|
// Acquire session_id, seat and vt (if any) via libsystemd
|
||||||
|
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 = login::get_vt(session_id.clone()).ok();
|
||||||
|
|
||||||
|
// Create dbus connection
|
||||||
|
let conn = Connection::get_private(BusType::System).chain_err(|| ErrorKind::FailedDbusConnection)?;
|
||||||
|
// and get the session path
|
||||||
|
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::<DbusPath<'static>>()
|
||||||
|
.chain_err(|| ErrorKind::UnexpectedMethodReturn)?;
|
||||||
|
|
||||||
|
// Match all signals that we want to receive and handle
|
||||||
|
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))?;
|
||||||
|
|
||||||
|
// Activate (switch to) the session and take control
|
||||||
|
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 {
|
||||||
|
/// Creates a new session object beloging to this notifier.
|
||||||
|
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<Vec<MessageItem>>,
|
||||||
|
) -> Result<Message>
|
||||||
|
where
|
||||||
|
D: Into<BusName<'d>>,
|
||||||
|
P: Into<DbusPath<'p>>,
|
||||||
|
I: Into<Interface<'i>>,
|
||||||
|
M: Into<Member<'m>>,
|
||||||
|
{
|
||||||
|
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::<u32, u32, String>();
|
||||||
|
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::<u32, u32, OwnedFd>();
|
||||||
|
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, Get, Iter, Variant};
|
||||||
|
|
||||||
|
let (_, changed, _) =
|
||||||
|
message.get3::<String, Dict<String, Variant<Iter>, Iter>, Array<String, Iter>>();
|
||||||
|
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<RawFd> {
|
||||||
|
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::<OwnedFd, bool>();
|
||||||
|
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<S: SessionObserver + 'static>(&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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bound logind session that is driven by the `wayland_server::EventLoop`.
|
||||||
|
///
|
||||||
|
/// See `logind_session_bind` for details.
|
||||||
|
///
|
||||||
|
/// Dropping this object will close the logind session just like the `LogindSessionNotifier`.
|
||||||
|
pub struct BoundLogindSession {
|
||||||
|
notifier: LogindSessionNotifier,
|
||||||
|
_watches: Vec<Watch>,
|
||||||
|
sources: Vec<FdEventSource<Rc<LogindSessionImpl>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bind a `LogindSessionNotifier` to an `EventLoop`.
|
||||||
|
///
|
||||||
|
/// Allows the `LogindSessionNotifier` to listen for incoming signals signalling the session state.
|
||||||
|
/// If you don't use this function `LogindSessionNotifier` will not correctly tell you the logind
|
||||||
|
/// session state and call it's `SessionObservers`.
|
||||||
|
pub fn logind_session_bind(
|
||||||
|
notifier: LogindSessionNotifier, evlh: &mut EventLoopHandle
|
||||||
|
) -> IoResult<BoundLogindSession> {
|
||||||
|
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::<IoResult<Vec<FdEventSource<Rc<LogindSessionImpl>>>>>()?;
|
||||||
|
|
||||||
|
Ok(BoundLogindSession {
|
||||||
|
notifier,
|
||||||
|
_watches: watches,
|
||||||
|
sources,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BoundLogindSession {
|
||||||
|
/// Unbind the logind session from the `EventLoop`
|
||||||
|
pub fn unbind(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");
|
||||||
|
// Release control again and drop everything closing the connection
|
||||||
|
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<Rc<LogindSessionImpl>> {
|
||||||
|
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<i32> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
#[cfg(feature = "backend_session_logind")]
|
||||||
|
pub mod logind;
|
|
@ -27,7 +27,7 @@
|
||||||
//! ### Usage of the session
|
//! ### Usage of the session
|
||||||
//!
|
//!
|
||||||
//! The session may be used to open devices manually through the `Session` interface
|
//! The session may be used to open devices manually through the `Session` interface
|
||||||
//! or be passed to other object that need to open devices themselves.
|
//! or be passed to other objects that need it to open devices themselves.
|
||||||
//!
|
//!
|
||||||
//! Examples for those are e.g. the `LibinputInputBackend` (its context might be initialized through a
|
//! Examples for those are e.g. the `LibinputInputBackend` (its context might be initialized through a
|
||||||
//! `Session` via the `LibinputSessionInterface`) or the `UdevBackend`.
|
//! `Session` via the `LibinputSessionInterface`) or the `UdevBackend`.
|
||||||
|
@ -160,7 +160,7 @@ pub struct DirectSessionNotifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DirectSession {
|
impl DirectSession {
|
||||||
/// Tries to creates a new session via the legacy virtual terminal interface.
|
/// Tries to create a new session via the legacy virtual terminal interface.
|
||||||
///
|
///
|
||||||
/// If you do not provide a tty device path, it will try to open the currently active tty if any.
|
/// If you do not provide a tty device path, it will try to open the currently active tty if any.
|
||||||
pub fn new<L>(tty: Option<&Path>, logger: L) -> Result<(DirectSession, DirectSessionNotifier)>
|
pub fn new<L>(tty: Option<&Path>, logger: L) -> Result<(DirectSession, DirectSessionNotifier)>
|
||||||
|
@ -362,15 +362,12 @@ impl SessionNotifier for DirectSessionNotifier {
|
||||||
|
|
||||||
/// Bind a `DirectSessionNotifier` to an `EventLoop`.
|
/// Bind a `DirectSessionNotifier` to an `EventLoop`.
|
||||||
///
|
///
|
||||||
/// Allows the `DirectSessionNotifier` to listen for the incoming signals signalling the session state.
|
/// Allows the `DirectSessionNotifier` to listen for incoming signals signalling the session state.
|
||||||
/// If you don't use this function `DirectSessionNotifier` will not correctly tell you the current
|
/// If you don't use this function `DirectSessionNotifier` will not correctly tell you the current
|
||||||
/// session state.
|
/// session state and call it's `SessionObservers`.
|
||||||
pub fn direct_session_bind<L>(
|
pub fn direct_session_bind(
|
||||||
notifier: DirectSessionNotifier, evlh: &mut EventLoopHandle, _logger: L
|
notifier: DirectSessionNotifier, evlh: &mut EventLoopHandle
|
||||||
) -> IoResult<SignalEventSource<DirectSessionNotifier>>
|
) -> IoResult<SignalEventSource<DirectSessionNotifier>> {
|
||||||
where
|
|
||||||
L: Into<Option<::slog::Logger>>,
|
|
||||||
{
|
|
||||||
let signal = notifier.signal;
|
let signal = notifier.signal;
|
||||||
|
|
||||||
evlh.add_signal_event_source(
|
evlh.add_signal_event_source(
|
||||||
|
@ -379,7 +376,7 @@ where
|
||||||
info!(notifier.logger, "Session shall become inactive");
|
info!(notifier.logger, "Session shall become inactive");
|
||||||
for signal in &mut notifier.signals {
|
for signal in &mut notifier.signals {
|
||||||
if let &mut Some(ref mut signal) = signal {
|
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);
|
notifier.active.store(false, Ordering::SeqCst);
|
||||||
|
@ -394,7 +391,7 @@ where
|
||||||
}
|
}
|
||||||
for signal in &mut notifier.signals {
|
for signal in &mut notifier.signals {
|
||||||
if let &mut Some(ref mut signal) = signal {
|
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);
|
notifier.active.store(true, Ordering::SeqCst);
|
||||||
|
|
|
@ -68,16 +68,26 @@ pub trait SessionNotifier {
|
||||||
/// It might be impossible to interact with devices while the session is disabled.
|
/// It might be impossible to interact with devices while the session is disabled.
|
||||||
/// This interface provides callbacks for when that happens.
|
/// This interface provides callbacks for when that happens.
|
||||||
pub trait SessionObserver {
|
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`
|
/// In case the implementor is a `StateToken` the state of the `EventLoop`
|
||||||
/// is provided via a `StateProxy`.
|
/// 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`
|
/// In case the implementor is a `StateToken` the state of the `EventLoop`
|
||||||
/// is provided via a `StateProxy`.
|
/// 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<RawFd>) 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<RawFd>)>);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Session for () {
|
impl Session for () {
|
||||||
|
@ -162,4 +172,7 @@ impl AsErrno for () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod auto;
|
||||||
pub mod direct;
|
pub mod direct;
|
||||||
|
mod dbus;
|
||||||
|
pub use self::dbus::*;
|
||||||
|
|
|
@ -180,18 +180,18 @@ impl<
|
||||||
T: UdevHandler<H> + 'static,
|
T: UdevHandler<H> + 'static,
|
||||||
> SessionObserver for StateToken<UdevBackend<H, S, T>>
|
> SessionObserver for StateToken<UdevBackend<H, S, T>>
|
||||||
{
|
{
|
||||||
fn pause<'a>(&mut self, state: &mut StateProxy<'a>) {
|
fn pause<'a>(&mut self, state: &mut StateProxy<'a>, devnum: Option<(u32, u32)>) {
|
||||||
state.with_value(self, |state, udev| {
|
state.with_value(self, |state, udev| {
|
||||||
for &mut (ref mut device, _) in udev.devices.values_mut() {
|
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<RawFd>)>) {
|
||||||
state.with_value(self, |state, udev| {
|
state.with_value(self, |state, udev| {
|
||||||
for &mut (ref mut device, _) in udev.devices.values_mut() {
|
for &mut (ref mut device, _) in udev.devices.values_mut() {
|
||||||
device.activate(state);
|
device.activate(state, devnum);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
10
src/lib.rs
10
src/lib.rs
|
@ -16,20 +16,18 @@ extern crate wayland_server;
|
||||||
extern crate wayland_sys;
|
extern crate wayland_sys;
|
||||||
extern crate xkbcommon;
|
extern crate xkbcommon;
|
||||||
|
|
||||||
|
#[cfg(feature = "dbus")]
|
||||||
|
extern crate dbus;
|
||||||
#[cfg(feature = "backend_drm")]
|
#[cfg(feature = "backend_drm")]
|
||||||
extern crate drm;
|
extern crate drm;
|
||||||
#[cfg(feature = "backend_drm")]
|
#[cfg(feature = "backend_drm")]
|
||||||
extern crate gbm;
|
extern crate gbm;
|
||||||
#[cfg(feature = "backend_libinput")]
|
#[cfg(feature = "backend_libinput")]
|
||||||
extern crate input;
|
extern crate input;
|
||||||
#[cfg(feature = "udev")]
|
|
||||||
extern crate udev;
|
|
||||||
/*
|
|
||||||
#[cfg(feature = "backend_session_logind")]
|
|
||||||
extern crate dbus;
|
|
||||||
#[cfg(feature = "backend_session_logind")]
|
#[cfg(feature = "backend_session_logind")]
|
||||||
extern crate systemd;
|
extern crate systemd;
|
||||||
*/
|
#[cfg(feature = "udev")]
|
||||||
|
extern crate udev;
|
||||||
#[cfg(feature = "backend_winit")]
|
#[cfg(feature = "backend_winit")]
|
||||||
extern crate wayland_client;
|
extern crate wayland_client;
|
||||||
#[cfg(feature = "backend_winit")]
|
#[cfg(feature = "backend_winit")]
|
||||||
|
|
|
@ -6,7 +6,7 @@ containers:
|
||||||
setup:
|
setup:
|
||||||
- !UbuntuRelease { codename: artful }
|
- !UbuntuRelease { codename: artful }
|
||||||
- !UbuntuUniverse
|
- !UbuntuUniverse
|
||||||
- !Install [build-essential, wget, curl, pkg-config, file, openssl, sudo, ca-certificates, libssl-dev, cmake, libudev-dev, libgbm-dev, libxkbcommon-dev, libegl1-mesa-dev, libwayland-dev, libinput-dev]
|
- !Install [build-essential, wget, curl, pkg-config, file, openssl, sudo, ca-certificates, libssl-dev, cmake, libudev-dev, libgbm-dev, libxkbcommon-dev, libegl1-mesa-dev, libwayland-dev, libinput-dev, libsystemd-dev, libdbus-1-dev]
|
||||||
|
|
||||||
stable:
|
stable:
|
||||||
auto-clean: true
|
auto-clean: true
|
||||||
|
|
Loading…
Reference in New Issue