use crate::dbus::DbusError;
use crate::dbus::DbusSocket;
use crate::dbus::FALSE;
use crate::dbus::SignalHandler;
use crate::env::XDG_SESSION_ID;
use crate::utils::errorfmt::ErrorFmt;
use crate::utils::major_minor::MajorMinor;
use crate::utils::major_minor::major_minor;
use crate::wire_dbus::org;
use crate::wire_dbus::org::freedesktop::login1::seat::SwitchToReply;
use crate::wire_dbus::org::freedesktop::login1::session::PauseDevice;
use crate::wire_dbus::org::freedesktop::login1::session::ResumeDevice;
use crate::wire_dbus::org::freedesktop::login1::session::TakeDeviceReply;
use std::rc::Rc;
use thiserror::Error;
use uapi::c;
const LOGIND_NAME: &str = "org.freedesktop.login1";
const MANAGER_PATH: &str = "/org/freedesktop/login1";
#[derive(Debug, Error)]
pub enum LogindError {
#[error("XDG_SESSION_ID is not set")]
XdgSessionId,
#[error("Could not retrieve the session dbus path")]
GetSession(DbusError),
#[error("Could not retrieve the session's seat name")]
GetSeatName(DbusError),
#[error(transparent)]
TakeControl(DbusError),
}
pub struct Session {
socket: Rc<DbusSocket>,
seat: String,
session_path: String,
}
impl Session {
pub async fn get(socket: &Rc<DbusSocket>) -> Result<Self, LogindError> {
let Some(session_id) = *XDG_SESSION_ID else {
return Err(LogindError::XdgSessionId);
};
let session_path = {
let session = socket
.call_async(
LOGIND_NAME,
MANAGER_PATH,
org::freedesktop::login1::manager::GetSession {
session_id: session_id.into(),
},
)
.await;
match session {
Ok(s) => s.get().object_path.to_string(),
Err(e) => return Err(LogindError::GetSession(e)),
}
};
let seat = {
let seat = socket
.get_async::<org::freedesktop::login1::session::Seat>(LOGIND_NAME, &session_path)
.await;
match seat {
Ok(s) => s.get().1.0.to_string(),
Err(e) => return Err(LogindError::GetSeatName(e)),
}
};
Ok(Self {
socket: socket.clone(),
seat,
session_path,
})
}
pub async fn take_control(&self) -> Result<(), LogindError> {
let res = self
.socket
.call_async(
LOGIND_NAME,
&self.session_path,
org::freedesktop::login1::session::TakeControl { force: FALSE },
)
.await;
if let Err(e) = res {
return Err(LogindError::TakeControl(e));
}
self.socket.call(
LOGIND_NAME,
&self.session_path,
org::freedesktop::login1::session::SetType {
ty: "wayland".into(),
},
|res| {
if let Err(e) = res {
log::warn!("Could not change session type to wayland: {}", ErrorFmt(e));
}
},
);
Ok(())
}
pub fn get_device<F>(&self, dev: c::dev_t, f: F)
where
F: FnOnce(Result<&TakeDeviceReply, DbusError>) + 'static,
{
let MajorMinor { major, minor } = major_minor(dev);
self.socket.call(
LOGIND_NAME,
&self.session_path,
org::freedesktop::login1::session::TakeDevice {
major: major as _,
minor: minor as _,
},
f,
);
}
pub fn on_pause<F>(&self, f: F) -> Result<SignalHandler, DbusError>
where
F: for<'b> Fn(PauseDevice<'b>) + 'static,
{
self.socket.handle_signal::<PauseDevice<'_>, _>(
Some(LOGIND_NAME),
Some(&self.session_path),
f,
)
}
pub fn on_resume<F>(&self, f: F) -> Result<SignalHandler, DbusError>
where
F: Fn(ResumeDevice) + 'static,
{
self.socket
.handle_signal::<ResumeDevice, _>(Some(LOGIND_NAME), Some(&self.session_path), f)
}
pub fn device_paused(&self, major: u32, minor: u32) {
self.socket.call_noreply(
LOGIND_NAME,
&self.session_path,
org::freedesktop::login1::session::PauseDeviceComplete { major, minor },
);
}
pub fn switch_to<F>(&self, vtnr: u32, f: F)
where
F: FnOnce(Result<&SwitchToReply, DbusError>) + 'static,
{
self.socket.call(
LOGIND_NAME,
&self.seat,
org::freedesktop::login1::seat::SwitchTo { vtnr },
f,
);
}
}