smithay_client_toolkit/session_lock/
mod.rs

1use crate::{compositor::Surface, error::GlobalError, globals::GlobalData, registry::GlobalProxy};
2use std::sync::{
3    atomic::{AtomicBool, Ordering},
4    Arc, Weak,
5};
6use wayland_client::{
7    globals::GlobalList,
8    protocol::{wl_output, wl_surface},
9    Connection, Dispatch, Proxy, QueueHandle,
10};
11use wayland_protocols::ext::session_lock::v1::client::{
12    ext_session_lock_manager_v1, ext_session_lock_surface_v1, ext_session_lock_v1,
13};
14
15mod dispatch;
16
17/// Handler trait for session lock protocol.
18pub trait SessionLockHandler: Sized {
19    /// The session lock is active, and the client may create lock surfaces.
20    fn locked(&mut self, conn: &Connection, qh: &QueueHandle<Self>, session_lock: SessionLock);
21
22    /// Session lock is not active and should be destroyed.
23    ///
24    /// This may be sent immediately if the compositor denys the requires to create a lock,
25    /// or may be sent some time after `lock`.
26    fn finished(&mut self, conn: &Connection, qh: &QueueHandle<Self>, session_lock: SessionLock);
27
28    /// Compositor has requested size for surface.
29    fn configure(
30        &mut self,
31        conn: &Connection,
32        qh: &QueueHandle<Self>,
33        surface: SessionLockSurface,
34        configure: SessionLockSurfaceConfigure,
35        serial: u32,
36    );
37}
38
39#[non_exhaustive]
40#[derive(Debug, Clone)]
41pub struct SessionLockSurfaceConfigure {
42    pub new_size: (u32, u32),
43}
44
45#[derive(Debug)]
46struct SessionLockSurfaceInner {
47    surface: Surface,
48    session_lock_surface: ext_session_lock_surface_v1::ExtSessionLockSurfaceV1,
49}
50
51impl Drop for SessionLockSurfaceInner {
52    fn drop(&mut self) {
53        self.session_lock_surface.destroy();
54    }
55}
56
57#[must_use]
58#[derive(Debug, Clone)]
59pub struct SessionLockSurface(Arc<SessionLockSurfaceInner>);
60
61impl SessionLockSurface {
62    pub fn from_ext_session_lock_surface(
63        surface: &ext_session_lock_surface_v1::ExtSessionLockSurfaceV1,
64    ) -> Option<Self> {
65        surface.data::<SessionLockSurfaceData>().and_then(|data| data.inner.upgrade()).map(Self)
66    }
67
68    pub fn wl_surface(&self) -> &wl_surface::WlSurface {
69        self.0.surface.wl_surface()
70    }
71}
72
73#[derive(Debug)]
74pub struct SessionLockSurfaceData {
75    inner: Weak<SessionLockSurfaceInner>,
76}
77
78impl SessionLockSurfaceData {
79    pub fn session_lock_surface(&self) -> Option<SessionLockSurface> {
80        self.inner.upgrade().map(SessionLockSurface)
81    }
82}
83
84#[derive(Debug)]
85pub struct SessionLockInner {
86    session_lock: ext_session_lock_v1::ExtSessionLockV1,
87    locked: AtomicBool,
88}
89
90impl Drop for SessionLockInner {
91    fn drop(&mut self) {
92        // This does nothing if unlock() was called.  It may trigger a protocol error if unlock was
93        // not called; this is an application bug, and choosing not to unlock here results in us
94        // failing secure.
95        self.session_lock.destroy();
96    }
97}
98
99/// A session lock
100///
101/// Once a lock is created, you must wait for either a `locked` or `finished` event before
102/// destroying this object.  If you get a `locked` event, you must explicitly call `unlock` prior
103/// to dropping this object.
104#[derive(Debug, Clone)]
105pub struct SessionLock(Arc<SessionLockInner>);
106
107impl SessionLock {
108    pub fn from_ext_session_lock(surface: &ext_session_lock_v1::ExtSessionLockV1) -> Option<Self> {
109        surface.data::<SessionLockData>().and_then(|data| data.inner.upgrade()).map(Self)
110    }
111
112    pub fn is_locked(&self) -> bool {
113        self.0.locked.load(Ordering::SeqCst)
114    }
115
116    pub fn unlock(&self) {
117        if self.0.locked.load(Ordering::SeqCst) {
118            self.0.session_lock.unlock_and_destroy();
119        }
120    }
121}
122
123#[derive(Debug)]
124pub struct SessionLockData {
125    inner: Weak<SessionLockInner>,
126}
127
128impl SessionLock {
129    pub fn create_lock_surface<D>(
130        &self,
131        surface: impl Into<Surface>,
132        output: &wl_output::WlOutput,
133        qh: &QueueHandle<D>,
134    ) -> SessionLockSurface
135    where
136        D: Dispatch<ext_session_lock_surface_v1::ExtSessionLockSurfaceV1, SessionLockSurfaceData>
137            + 'static,
138    {
139        // Freeze the queue during the creation of the Arc to avoid a race between events on the
140        // new objects being processed and the Weak in the SessionLockSurfaceData becoming usable.
141        let freeze = qh.freeze();
142        let surface = surface.into();
143
144        let inner = Arc::new_cyclic(|weak| {
145            let session_lock_surface = self.0.session_lock.get_lock_surface(
146                surface.wl_surface(),
147                output,
148                qh,
149                SessionLockSurfaceData { inner: weak.clone() },
150            );
151
152            SessionLockSurfaceInner { surface, session_lock_surface }
153        });
154        drop(freeze);
155
156        SessionLockSurface(inner)
157    }
158}
159
160/// A handler for [`ext_session_lock_manager_v1::ExtSessionLockManagerV1`]
161#[derive(Debug)]
162pub struct SessionLockState {
163    session_lock_manager: GlobalProxy<ext_session_lock_manager_v1::ExtSessionLockManagerV1>,
164}
165
166impl SessionLockState {
167    pub fn new<D>(globals: &GlobalList, qh: &QueueHandle<D>) -> Self
168    where
169        D: Dispatch<ext_session_lock_manager_v1::ExtSessionLockManagerV1, GlobalData> + 'static,
170    {
171        let session_lock_manager = GlobalProxy::from(globals.bind(qh, 1..=1, GlobalData));
172        Self { session_lock_manager }
173    }
174
175    pub fn lock<D>(&self, qh: &QueueHandle<D>) -> Result<SessionLock, GlobalError>
176    where
177        D: Dispatch<ext_session_lock_v1::ExtSessionLockV1, SessionLockData> + 'static,
178    {
179        let session_lock_manager = self.session_lock_manager.get()?;
180
181        // Freeze the queue during the creation of the Arc to avoid a race between events on the
182        // new objects being processed and the Weak in the SessionLockData becoming usable.
183        let freeze = qh.freeze();
184
185        let inner = Arc::new_cyclic(|weak| {
186            let session_lock =
187                session_lock_manager.lock(qh, SessionLockData { inner: weak.clone() });
188
189            SessionLockInner { session_lock, locked: AtomicBool::new(false) }
190        });
191        drop(freeze);
192
193        Ok(SessionLock(inner))
194    }
195}
196
197#[macro_export]
198macro_rules! delegate_session_lock {
199    ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
200        $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
201            [
202                $crate::reexports::protocols::ext::session_lock::v1::client::ext_session_lock_manager_v1::ExtSessionLockManagerV1: $crate::globals::GlobalData
203            ] => $crate::session_lock::SessionLockState
204        );
205        $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
206            [
207                $crate::reexports::protocols::ext::session_lock::v1::client::ext_session_lock_v1::ExtSessionLockV1: $crate::session_lock::SessionLockData
208            ] => $crate::session_lock::SessionLockState
209        );
210        $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
211            [
212                $crate::reexports::protocols::ext::session_lock::v1::client::ext_session_lock_surface_v1::ExtSessionLockSurfaceV1: $crate::session_lock::SessionLockSurfaceData
213            ] => $crate::session_lock::SessionLockState
214        );
215    };
216}