use smithay_client_toolkit::session_lock::{
SessionLock, SessionLockHandler, SessionLockState, SessionLockSurface,
SessionLockSurfaceConfigure,
};
use smithay_client_toolkit::reexports::client::{Connection, Proxy, QueueHandle};
use super::wayland::{SurfaceRole, WaylandState, WaylandSurfaceState};
use crate::outputs::OutputId;
use crate::surface::SurfaceId;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LockEvent {
Locked,
Finished,
}
pub struct Lock {
pub(super) session_lock_state: SessionLockState,
pub(super) active_lock: Option<SessionLock>,
pub(super) lock_events: Vec<LockEvent>,
}
impl Lock {
pub(super) fn new(session_lock_state: SessionLockState) -> Self {
Self {
session_lock_state,
active_lock: None,
lock_events: Vec::new(),
}
}
}
impl WaylandState {
pub fn start_session_lock(&mut self) -> bool {
let qh = &self.qh;
if self.lock.active_lock.is_some() {
log::warn!("Session lock requested while a lock is already active");
return false;
}
match self.lock.session_lock_state.lock(qh) {
Ok(lock) => {
self.lock.active_lock = Some(lock);
log::info!("Session lock requested");
true
}
Err(e) => {
log::error!("Compositor does not support ext-session-lock-v1: {e}");
false
}
}
}
pub fn unlock_session(&mut self) {
if let Some(lock) = self.lock.active_lock.take() {
lock.unlock();
log::info!("Session unlocked");
}
}
pub fn is_session_locked(&self) -> bool {
self.lock
.active_lock
.as_ref()
.is_some_and(|l| l.is_locked())
}
pub fn create_lock_surface_with_id(&mut self, id: SurfaceId, output: OutputId) -> bool {
let qh = &self.qh;
let Some(lock) = self.lock.active_lock.clone() else {
log::warn!("Cannot create lock surface without an active session lock");
return false;
};
let Some(wl_output) = self.wl_output_for(output) else {
log::warn!(
"Cannot create lock surface: output {:?} is not connected",
output
);
return false;
};
let wl_surface = self.compositor_state.create_surface(qh);
let lock_surface = lock.create_lock_surface(wl_surface.clone(), &wl_output, qh);
let scaling = self.create_surface_scaling(id, &wl_surface);
self.surface_lookup.insert(wl_surface.id(), id);
let surface_state =
WaylandSurfaceState::new(SurfaceRole::Lock(lock_surface), wl_surface, 0, 0, scaling);
self.surfaces.insert(id, surface_state);
log::info!("Created lock surface {:?} on output {:?}", id, output);
true
}
pub fn take_lock_events(&mut self) -> Vec<LockEvent> {
std::mem::take(&mut self.lock.lock_events)
}
}
impl SessionLockHandler for WaylandState {
fn locked(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, _session_lock: SessionLock) {
log::info!("Session lock granted by compositor");
self.lock.lock_events.push(LockEvent::Locked);
crate::jobs::wake_loop();
}
fn finished(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_session_lock: SessionLock,
) {
log::info!("Session lock finished (denied or ended)");
self.lock.active_lock = None;
self.lock.lock_events.push(LockEvent::Finished);
crate::jobs::wake_loop();
}
fn configure(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
surface: SessionLockSurface,
configure: SessionLockSurfaceConfigure,
_serial: u32,
) {
let surface_id = self.surface_lookup.get(&surface.wl_surface().id()).copied();
if let Some(id) = surface_id
&& let Some(surface_state) = self.surfaces.get_mut(&id)
{
let (width, height) = configure.new_size;
log::info!(
"Lock surface {:?} configure: {}x{} (current {}x{})",
id,
width,
height,
surface_state.width,
surface_state.height
);
if width > 0 {
surface_state.width = width;
}
if height > 0 {
surface_state.height = height;
}
surface_state.configured = true;
crate::jobs::wake_loop();
}
}
}