1 | use crate::{compositor::Surface, error::GlobalError, globals::GlobalData, registry::GlobalProxy}; |
2 | use std::sync::{ |
3 | atomic::{AtomicBool, Ordering}, |
4 | Arc, Weak, |
5 | }; |
6 | use wayland_client::{ |
7 | globals::GlobalList, |
8 | protocol::{wl_output, wl_surface}, |
9 | Connection, Dispatch, Proxy, QueueHandle, |
10 | }; |
11 | use wayland_protocols::ext::session_lock::v1::client::{ |
12 | ext_session_lock_manager_v1, ext_session_lock_surface_v1, ext_session_lock_v1, |
13 | }; |
14 | |
15 | mod dispatch; |
16 | |
17 | /// Handler trait for session lock protocol. |
18 | pub 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)] |
41 | pub struct SessionLockSurfaceConfigure { |
42 | pub new_size: (u32, u32), |
43 | } |
44 | |
45 | #[derive (Debug)] |
46 | struct SessionLockSurfaceInner { |
47 | surface: Surface, |
48 | session_lock_surface: ext_session_lock_surface_v1::ExtSessionLockSurfaceV1, |
49 | } |
50 | |
51 | impl Drop for SessionLockSurfaceInner { |
52 | fn drop(&mut self) { |
53 | self.session_lock_surface.destroy(); |
54 | } |
55 | } |
56 | |
57 | #[must_use ] |
58 | #[derive (Debug, Clone)] |
59 | pub struct SessionLockSurface(Arc<SessionLockSurfaceInner>); |
60 | |
61 | impl 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)] |
74 | pub struct SessionLockSurfaceData { |
75 | inner: Weak<SessionLockSurfaceInner>, |
76 | } |
77 | |
78 | impl SessionLockSurfaceData { |
79 | pub fn session_lock_surface(&self) -> Option<SessionLockSurface> { |
80 | self.inner.upgrade().map(SessionLockSurface) |
81 | } |
82 | } |
83 | |
84 | #[derive (Debug)] |
85 | pub struct SessionLockInner { |
86 | session_lock: ext_session_lock_v1::ExtSessionLockV1, |
87 | locked: AtomicBool, |
88 | } |
89 | |
90 | impl 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)] |
105 | pub struct SessionLock(Arc<SessionLockInner>); |
106 | |
107 | impl 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(order:Ordering::SeqCst) |
114 | } |
115 | |
116 | pub fn unlock(&self) { |
117 | if self.0.locked.load(order:Ordering::SeqCst) { |
118 | self.0.session_lock.unlock_and_destroy(); |
119 | } |
120 | } |
121 | } |
122 | |
123 | #[derive (Debug)] |
124 | pub struct SessionLockData { |
125 | inner: Weak<SessionLockInner>, |
126 | } |
127 | |
128 | impl 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)] |
162 | pub struct SessionLockState { |
163 | session_lock_manager: GlobalProxy<ext_session_lock_manager_v1::ExtSessionLockManagerV1>, |
164 | } |
165 | |
166 | impl 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 ] |
198 | macro_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 | } |
217 | |