1use tokio::{sync::{mpsc::{Sender, Receiver}, oneshot}, io::unix::AsyncFd};
2use wayland_client::{EventQueue, protocol::{wl_seat::{WlSeat, self}, wl_registry::{WlRegistry, self}, wl_output::{WlOutput, self}, wl_compositor::{WlCompositor, self}, wl_surface::{WlSurface, self}}, globals::{self, GlobalListContents}, Connection, QueueHandle, Dispatch, WEnum};
3use wayland_protocols::ext::session_lock::v1::client::{
4 ext_session_lock_manager_v1::{self, ExtSessionLockManagerV1}, ext_session_lock_surface_v1::{self, ExtSessionLockSurfaceV1}, ext_session_lock_v1::{self, ExtSessionLockV1},
5};
6
7use super::errors::{SessionLockhandlerError, SessionLockhandlerErrorCodes};
8
9#[derive(Debug)]
10pub enum SessionLockMessage {
11 Lock { wl_surface: Option<WlSurface>, reply_to: oneshot::Sender<Result<bool, SessionLockhandlerError>> },
12 Unlock { reply_to: oneshot::Sender<Result<bool, SessionLockhandlerError>> },
13 AckConfigure { reply_to: oneshot::Sender<Result<bool, SessionLockhandlerError>> },
14}
15
16#[derive(Debug)]
17pub enum SessionLockEvent {
18 Locked,
19 Finished,
20 Configure { serial: u32, width: u32, height: u32 },
21}
22
23pub struct SessionLockState {
24 qh: QueueHandle<SessionLockState>,
25 event_tx: Sender<SessionLockEvent>,
26 seat: Option<WlSeat>,
27 output: Option<WlOutput>,
28 compositor: Option<WlCompositor>,
29 surface: Option<WlSurface>,
30 session_lock_manager: Option<ExtSessionLockManagerV1>,
31 session_lock: Option<ExtSessionLockV1>,
32 session_lock_surface: Option<ExtSessionLockSurfaceV1>,
33 session_lock_configure_serial: Option<u32>
34}
35
36pub struct SessionLockHandler {
37 event_queue: EventQueue<SessionLockState>,
38 state: SessionLockState,
39}
40
41impl SessionLockHandler {
42 pub fn new(event_tx: Sender<SessionLockEvent>) -> Self {
43 let conn = wayland_client::Connection::connect_to_env()
44 .map_err(|_| "could not connect to wayland socket, try setting WAYLAND_DISPLAY.")
45 .unwrap();
46 let (globals, mut event_queue) = globals::registry_queue_init::<SessionLockState>(&conn).unwrap();
47 let qh = event_queue.handle();
48
49 let session_lock_manager = globals
50 .bind::<ExtSessionLockManagerV1, _, _>(&qh, core::ops::RangeInclusive::new(1, 1), ())
51 .map_err(|_| "compositor does not implement ext session lock manager (v1).").unwrap();
52 let seat = globals
53 .bind::<WlSeat, _, _>(&qh, core::ops::RangeInclusive::new(1, 1), ())
54 .map_err(|_| "failed to retrieve the seat from global.")
55 .unwrap();
56 let output = globals
57 .bind::<WlOutput, _, _>(&qh, core::ops::RangeInclusive::new(1, 1), ())
58 .map_err(|_| "failed to retrieve the output from global.")
59 .unwrap();
60 let compositor = globals
61 .bind::<WlCompositor, _, _>(&qh, core::ops::RangeInclusive::new(1, 5), ())
62 .map_err(|_| "failed to retrieve the compositor from global").unwrap();
63 let surface = compositor.create_surface(&qh, ());
64
65 let mut state = SessionLockState {
66 qh,
67 event_tx,
68 seat: Some(seat),
69 output: Some(output),
70 compositor: Some(compositor),
71 surface: Some(surface),
72 session_lock_manager: Some(session_lock_manager),
73 session_lock: None,
74 session_lock_surface: None,
75 session_lock_configure_serial: None,
76 };
77
78 event_queue.roundtrip(&mut state).unwrap();
79
80 SessionLockHandler {
81 event_queue,
82 state,
83 }
84 }
85
86 pub async fn run(&mut self, mut msg_rx: Receiver<SessionLockMessage>) {
87 let event_queue = &mut self.event_queue;
88 let mut session_lock_state = &mut self.state;
89
90 loop {
91 event_queue.dispatch_pending(&mut session_lock_state).unwrap();
93 let read_guard = event_queue.prepare_read().unwrap();
94 let fd = read_guard.connection_fd();
95 let async_fd = AsyncFd::new(fd).unwrap();
96
97 tokio::select! {
98 async_guard = async_fd.readable() => {
99 async_guard.unwrap().clear_ready();
100 std::mem::drop(async_fd);
103 let event = read_guard.read();
105 match event {
106 Ok(0) => {},
108 Ok(_) => {
110 event_queue.dispatch_pending(&mut session_lock_state).unwrap();
111 },
112 Err(_) => {}
114 }
116 },
117 msg = msg_rx.recv() => {
118 if msg.is_none() {
119 continue;
120 }
121 match msg.unwrap() {
122 SessionLockMessage::Lock { wl_surface, reply_to } => {
123 let res = session_lock_state.lock(wl_surface);
124 let _ = reply_to.send(res);
125 },
126 SessionLockMessage::Unlock { reply_to } => {
127 let res = session_lock_state.unlock();
128 let _ = reply_to.send(res);
129 },
130 SessionLockMessage::AckConfigure { reply_to } => {
131 let res = session_lock_state.ack_configure();
132 let _ = reply_to.send(res);
133 },
134 }
135 }
136 }
137
138 let _ = event_queue.flush().expect("wayland connection closed");
140 }
141 }
142}
143
144impl SessionLockState {
145 fn dispatch_event(&self, event: SessionLockEvent) {
146 let tx: Sender<SessionLockEvent> = self.event_tx.clone();
147 tokio::task::spawn(async move {
148 let _ = tx.send(event).await;
149 });
150 }
151
152 fn lock(&mut self, wl_surface: Option<WlSurface>) -> Result<bool, SessionLockhandlerError> {
153 let session_lock_manager = match &self.session_lock_manager {
154 Some(m) => m,
155 None => {
156 return Err(SessionLockhandlerError::new(
157 SessionLockhandlerErrorCodes::ManagerIsNotSet,
158 format!("session lock manager uninitialized or is not set")
159 ));
160 },
161 };
162 let output = match &self.output {
163 Some(o) => o,
164 None => {
165 return Err(SessionLockhandlerError::new(
166 SessionLockhandlerErrorCodes::WLOutputIsNotSet,
167 format!("wl_output uninitialized or is not set")
168 ));
169 },
170 };
171 let mut surface = match &self.surface {
173 Some(o) => o,
174 None => {
175 return Err(SessionLockhandlerError::new(
176 SessionLockhandlerErrorCodes::WLSurfaceIsNotSet,
177 format!("wl_surface uninitialized or is not set")
178 ));
179 },
180 };
181 if wl_surface.is_some() {
182 surface = wl_surface.as_ref().unwrap();
183 }
184
185 let session_lock = session_lock_manager.lock(&self.qh, ());
186
187 session_lock.get_lock_surface(&surface, &output, &self.qh, ());
189
190 self.session_lock = Some(session_lock);
192 Ok(true)
193 }
194
195 fn unlock(&mut self) -> Result<bool, SessionLockhandlerError> {
196 let session_lock = match &self.session_lock {
197 Some(l) => l,
198 None => {
199 return Err(SessionLockhandlerError::new(
200 SessionLockhandlerErrorCodes::SessionLockIsNotSet,
201 format!("session_lock uninitialized or is not set, did you lock the session?")
202 ));
203 },
204 };
205
206 session_lock.unlock_and_destroy();
208
209 self.session_lock = None;
211 self.session_lock_surface = None;
212 self.session_lock_configure_serial = None;
213
214 Ok(true)
215 }
216
217 fn ack_configure(&self) -> Result<bool, SessionLockhandlerError> {
218 let session_lock_surface = match &self.session_lock_surface {
219 Some(l) => l,
220 None => {
221 return Err(SessionLockhandlerError::new(
222 SessionLockhandlerErrorCodes::SessionLockSurfaceIsNotSet,
223 format!("session_lock_surface uninitialized or is not set, did you lock the session?")
224 ));
225 },
226 };
227 let session_lock_configure_serial = match self.session_lock_configure_serial {
228 Some(l) => l,
229 None => {
230 return Err(SessionLockhandlerError::new(
231 SessionLockhandlerErrorCodes::SessionLockSurfaceSerialIsNotSet,
232 format!("session_lock_configure_serial uninitialized or is not set")
233 ));
234 },
235 };
236
237 session_lock_surface.ack_configure(session_lock_configure_serial);
239
240 Ok(true)
241 }
242}
243
244impl Dispatch<WlRegistry, GlobalListContents> for SessionLockState {
245 fn event(
246 _: &mut Self,
247 registry: &wl_registry::WlRegistry,
248 event: wl_registry::Event,
249 _: &GlobalListContents,
250 _: &Connection,
251 qh: &QueueHandle<SessionLockState>,
252 ) {
253 if let wl_registry::Event::Global {
254 name,
255 interface,
256 version,
257 } = event
258 {
259 if interface == "wl_seat" {
260 registry.bind::<WlSeat, (), SessionLockState>(name, version, qh, ());
261 }
262 }
263 }
264}
265
266impl Dispatch<WlSeat, ()> for SessionLockState {
267 fn event(
268 state: &mut Self,
269 seat: &WlSeat,
270 event: wl_seat::Event,
271 _: &(),
272 _: &Connection,
273 _: &QueueHandle<SessionLockState>,
274 ) {
275 if let wl_seat::Event::Name { .. } = event {
276 state.seat = Some(seat.to_owned());
277 }
278 }
279}
280
281impl Dispatch<WlOutput, ()> for SessionLockState {
282 fn event(
283 state: &mut Self,
284 output: &WlOutput,
285 event: wl_output::Event,
286 _: &(),
287 _: &Connection,
288 _: &QueueHandle<SessionLockState>,
289 ) {
290 if let wl_output::Event::Name { .. } = event {
291 state.output = Some(output.to_owned());
292 }
293 }
294}
295
296impl Dispatch<WlCompositor, ()> for SessionLockState {
297 fn event(
298 state: &mut Self,
299 compositor: &WlCompositor,
300 event: wl_compositor::Event,
301 _: &(),
302 _: &Connection,
303 _: &QueueHandle<SessionLockState>,
304 ) {
305 state.compositor = Some(compositor.to_owned());
306 }
307}
308
309impl Dispatch<WlSurface, ()> for SessionLockState {
310 fn event(
311 state: &mut Self,
312 surface: &WlSurface,
313 event: wl_surface::Event,
314 _: &(),
315 _: &Connection,
316 _: &QueueHandle<SessionLockState>,
317 ) {
318 state.surface = Some(surface.to_owned());
319 }
320}
321
322impl Dispatch<ExtSessionLockManagerV1, ()> for SessionLockState {
323 fn event(
324 _: &mut Self,
325 _: &ExtSessionLockManagerV1,
326 event: <ExtSessionLockManagerV1 as wayland_client::Proxy>::Event,
327 _: &(),
328 _: &Connection,
329 _: &QueueHandle<Self>,
330 ) {}
331}
332
333impl Dispatch<ExtSessionLockV1, ()> for SessionLockState {
334 fn event(
335 state: &mut Self,
336 _: &ExtSessionLockV1,
337 event: <ExtSessionLockV1 as wayland_client::Proxy>::Event,
338 _: &(),
339 _: &Connection,
340 _: &QueueHandle<Self>,
341 ) {
342 match event {
343 ext_session_lock_v1::Event::Locked => {
344 state.dispatch_event(SessionLockEvent::Locked);
345 },
346 ext_session_lock_v1::Event::Finished => {
347 state.session_lock = None;
348 state.session_lock_surface = None;
349 state.session_lock_configure_serial = None;
350
351 state.dispatch_event(SessionLockEvent::Finished);
352 },
353 _ => {},
354 }
355 }
356}
357
358impl Dispatch<ExtSessionLockSurfaceV1, ()> for SessionLockState {
359 fn event(
360 state: &mut Self,
361 surface: &ExtSessionLockSurfaceV1,
362 event: <ExtSessionLockSurfaceV1 as wayland_client::Proxy>::Event,
363 _: &(),
364 _: &Connection,
365 _: &QueueHandle<Self>,
366 ) {
367 state.session_lock_surface = Some(surface.to_owned());
368 match event {
369 ext_session_lock_surface_v1::Event::Configure { serial, width, height } => {
370 state.session_lock_configure_serial = Some(serial);
371 state.dispatch_event(SessionLockEvent::Configure { serial, width, height })
372 },
373 _ => todo!(),
374 }
375 }
376}
377