Skip to main content

spell_framework/
wayland_adapter.rs

1//! It provides various widget types for implementing properties
2//! across various functionalities for your shell. The most common widget (or
3//! window as called by many) is [SpellWin]. You can also implement a lock screen
4//! with [`SpellLock`].
5use crate::{
6    SpellAssociatedNew,
7    configure::{HomeHandle, LayerConf, WindowConf, set_up_tracing},
8    slint_adapter::{
9        ADAPTERS, SpellLayerShell, SpellLockShell, SpellMultiWinHandler, SpellSkiaWinAdapter,
10    },
11    wayland_adapter::{
12        fractional_scaling::{
13            FractionalScaleHandler, FractionalScaleState, delegate_fractional_scale,
14        },
15        viewporter::{Viewport, ViewporterState, delegate_viewporter},
16        way_helper::{
17            FingerprintInfo, KeyboardState, PointerState, UsernamePassConvo, set_config,
18            set_event_sources,
19        },
20    },
21};
22pub use lock_impl::SpellSlintLock;
23use nonstick::{
24    AuthnFlags, ConversationAdapter, Result as PamResult, Transaction, TransactionBuilder,
25};
26use slint::{
27    PhysicalSize,
28    platform::{Key, WindowAdapter},
29};
30use smithay_client_toolkit::{
31    compositor::{CompositorHandler, CompositorState, Region},
32    delegate_compositor,
33    delegate_keyboard,
34    delegate_layer,
35    delegate_output,
36    delegate_pointer,
37    delegate_registry,
38    delegate_seat,
39    delegate_session_lock,
40    delegate_shm, // delegate_touch,
41    output::{self, OutputHandler, OutputState},
42    reexports::{
43        calloop::{
44            EventLoop, LoopHandle, RegistrationToken,
45            timer::{TimeoutAction, Timer},
46        },
47        calloop_wayland_source::WaylandSource,
48        client::{
49            Connection, EventQueue, QueueHandle,
50            globals::registry_queue_init,
51            protocol::{wl_output, wl_shm, wl_surface},
52        },
53    },
54    registry::RegistryState,
55    seat::{SeatState, pointer::cursor_shape::CursorShapeManager},
56    session_lock::{SessionLock, SessionLockState, SessionLockSurface},
57    shell::{
58        WaylandSurface,
59        wlr_layer::{
60            KeyboardInteractivity, LayerShell, LayerShellHandler, LayerSurface,
61            LayerSurfaceConfigure,
62        },
63    },
64    shm::{
65        Shm, ShmHandler,
66        slot::{Buffer, Slot, SlotPool},
67    },
68};
69use std::{
70    cell::{Cell, RefCell},
71    collections::HashMap,
72    os::unix::net::UnixListener,
73    process::Command,
74    rc::Rc,
75    sync::{Arc, Mutex, Once, OnceLock, RwLock},
76    time::Duration,
77};
78use tracing::{Level, info, span, trace, warn};
79
80mod fractional_scaling;
81mod lock_impl;
82mod viewporter;
83mod way_helper;
84mod win_impl;
85
86static AVAILABLE_MONITORS: OnceLock<RwLock<HashMap<String, wl_output::WlOutput>>> = OnceLock::new();
87static SET_SLINT_PLATFORM: Once = Once::new();
88
89#[derive(Debug)]
90pub(crate) struct States {
91    pub(crate) registry_state: RegistryState,
92    pub(crate) seat_state: SeatState,
93    pub(crate) output_state: OutputState,
94    pub(crate) pointer_state: PointerState,
95    pub(crate) keyboard_state: KeyboardState,
96    pub(crate) shm: Shm,
97    pub(crate) viewporter: Option<Viewport>,
98}
99
100/// `SpellWin` is the main type for implementing widgets, it covers various properties and trait
101/// implementation, thus providing various features.
102pub struct SpellWin {
103    pub(crate) adapter: Rc<SpellSkiaWinAdapter>,
104    /// loop handle provided in a wrapper by [get_handler](crate::wayland_adapter::SpellWin::get_handler).
105    pub loop_handle: LoopHandle<'static, SpellWin>,
106    pub ipc_handler: Option<UnixListener>,
107    pub(crate) queue: QueueHandle<SpellWin>,
108    pub(crate) buffer: Buffer,
109    pub(crate) states: States,
110    pub(crate) layer: Option<LayerSurface>,
111    pub(crate) first_configure: Cell<bool>,
112    pub(crate) natural_scroll: bool,
113    pub(crate) is_hidden: Cell<bool>,
114    pub layer_name: String,
115    pub(crate) config: WindowConf,
116    pub(crate) input_region: Region,
117    pub(crate) opaque_region: Region,
118    pub event_loop: Rc<RefCell<EventLoop<'static, SpellWin>>>,
119    /// Span required for proper logging.
120    pub span: span::Span,
121    // #[allow(dead_code)]
122    // pub(crate) backspace: calloop::RegistrationToken,
123}
124
125impl std::fmt::Debug for SpellWin {
126    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127        f.debug_struct("SpellWin")
128            .field("adapter", &self.adapter)
129            .field("first_configure", &self.first_configure)
130            .field("is_hidden", &self.is_hidden)
131            .field("config", &self.config)
132            .finish()
133    }
134}
135
136impl SpellWin {
137    pub(crate) fn create_window(
138        conn: &Connection,
139        window_conf: WindowConf,
140        layer_name: String,
141        handle: HomeHandle,
142    ) -> Self {
143        let (globals, mut event_queue) = registry_queue_init(conn).unwrap();
144        let qh: QueueHandle<SpellWin> = event_queue.handle();
145
146        let compositor =
147            CompositorState::bind(&globals, &qh).expect("wl_compositor is not available");
148        let event_loop: EventLoop<'static, SpellWin> =
149            EventLoop::try_new().expect("Failed to initialize the event loop!");
150        let layer_shell = LayerShell::bind(&globals, &qh).expect("layer shell is not available");
151        let shm = Shm::bind(&globals, &qh).expect("wl_shm is not available");
152        let mut pool = SlotPool::new((window_conf.width * window_conf.height * 4) as usize, &shm)
153            .expect("Failed to create pool");
154        let input_region = Region::new(&compositor).expect("Couldn't create region");
155        let opaque_region = Region::new(&compositor).expect("Couldn't create opaque region");
156        input_region.add(0, 0, window_conf.width as i32, window_conf.height as i32);
157        let cursor_manager =
158            CursorShapeManager::bind(&globals, &qh).expect("cursor shape is not available");
159        let fractional_scale_state: FractionalScaleState =
160            FractionalScaleState::bind(&globals, &qh).expect("Fractional Scale couldn't be set");
161        let stride = window_conf.width as i32 * 4;
162
163        let surface = compositor.create_surface(&qh);
164        let viewporter_state =
165            ViewporterState::bind(&globals, &qh).expect("Couldn't set viewporter");
166
167        let (way_pri_buffer, _) = pool
168            .create_buffer(
169                window_conf.width as i32,
170                window_conf.height as i32,
171                stride,
172                wl_shm::Format::Argb8888,
173            )
174            .expect("Creating Buffer");
175
176        let primary_slot = way_pri_buffer.slot();
177
178        let pointer_state = PointerState {
179            pointer: None,
180            pointer_data: None,
181            cursor_shape: cursor_manager,
182        };
183
184        let keyboard_state = KeyboardState {
185            board: None,
186            // board_data: None,
187        };
188
189        #[allow(clippy::type_complexity)]
190        let slint_proxy: Arc<Mutex<Vec<Box<dyn FnOnce() + Send>>>> =
191            Arc::new(Mutex::new(Vec::new()));
192        let adapter_value: Rc<SpellSkiaWinAdapter> = SpellSkiaWinAdapter::new(
193            Rc::new(RefCell::new(pool)),
194            RefCell::new(primary_slot),
195            window_conf.width,
196            window_conf.height,
197            slint_proxy.clone(),
198        );
199
200        ADAPTERS.with_borrow_mut(|v| v.push(adapter_value.clone()));
201        SET_SLINT_PLATFORM.call_once(|| {
202            trace!("Slint platform set");
203            if let Err(err) = slint::platform::set_platform(Box::new(SpellLayerShell::default())) {
204                warn!("Error setting slint platform: {err}");
205            }
206        });
207        set_event_sources(&event_loop, handle);
208
209        let mut win = SpellWin {
210            adapter: adapter_value,
211            loop_handle: event_loop.handle(),
212            ipc_handler: None,
213            queue: qh.clone(),
214            buffer: way_pri_buffer,
215            states: States {
216                registry_state: RegistryState::new(&globals),
217                seat_state: SeatState::new(&globals, &qh),
218                output_state: OutputState::new(&globals, &qh),
219                pointer_state,
220                keyboard_state,
221                shm,
222                viewporter: None,
223            },
224            layer: None,
225            first_configure: Cell::new(true),
226            natural_scroll: window_conf.natural_scroll,
227            is_hidden: Cell::new(false),
228            layer_name: layer_name.clone(),
229            config: window_conf.clone(),
230            input_region,
231            opaque_region,
232            event_loop: Rc::new(RefCell::new(event_loop)),
233            span: span!(Level::INFO, "widget", name = layer_name.as_str(),),
234        };
235
236        if AVAILABLE_MONITORS.get().is_none() {
237            match SpellWin::get_available_monitors(&mut event_queue, &mut win) {
238                Some(monitors) => {
239                    let _ = AVAILABLE_MONITORS.get_or_init(|| RwLock::new(monitors));
240                }
241                None => warn!("Failed to get available monitors"),
242            }
243        }
244
245        let target_output: Option<wl_output::WlOutput> =
246            if let Some(name) = &window_conf.monitor_name {
247                let output = AVAILABLE_MONITORS
248                    .get()
249                    .and_then(|monitors| monitors.read().ok())
250                    .and_then(|monitors| monitors.get(name).cloned());
251                if output.is_none() {
252                    warn!("Monitor '{}' not found, using default monitor", name);
253                }
254                output
255            } else {
256                None
257            };
258
259        let layer = layer_shell.create_layer_surface(
260            &qh,
261            surface,
262            window_conf.layer_type,
263            Some(layer_name.clone()),
264            target_output.as_ref(),
265        );
266        let fractional_scale = fractional_scale_state.get_scale(layer.wl_surface(), &qh);
267
268        let viewporter = viewporter_state.get_viewport(layer.wl_surface(), &qh, fractional_scale);
269
270        set_config(
271            &win.config,
272            &layer,
273            //true,
274            Some(win.input_region.wl_region()),
275            None,
276        );
277        layer.commit();
278
279        win.layer = Some(layer);
280        win.states.viewporter = Some(viewporter);
281
282        info!("Win: {} layer created successfully.", layer_name);
283
284        WaylandSource::new(conn.clone(), event_queue)
285            .insert(win.loop_handle.clone())
286            .unwrap();
287        win
288    }
289
290    /// Fetches the available monitors from the Wayland registry.
291    ///
292    /// This function fetches the available monitors from the Wayland registry and returns a map of
293    /// the available monitors where the key is the name of the monitor and the value is the
294    /// [`wl_output::WlOutput`]. It uses an already registered event queue & spell window.
295    ///
296    /// # Errors
297    ///
298    /// Returns `None` if the registry queue could not be initialized.
299    fn get_available_monitors(
300        event_queue: &mut EventQueue<SpellWin>,
301        win: &mut SpellWin,
302    ) -> Option<HashMap<String, wl_output::WlOutput>> {
303        // roundtrip to get all available monitors from Wayland
304        event_queue.roundtrip(win).ok()?;
305
306        Some(
307            win.states
308                .output_state
309                .outputs()
310                .filter_map(|output| {
311                    let info = win.states.output_state.info(&output)?;
312                    Some((info.name?, output))
313                })
314                .collect(),
315        )
316    }
317
318    /// Returns a handle of [`WinHandle`] to invoke wayland specific features.
319    pub fn get_handler(&self) -> WinHandle {
320        info!("Win: Handle provided.");
321        WinHandle(self.loop_handle.clone())
322    }
323
324    /// This function is called to create a instance of window. This window is then
325    /// finally called by [`cast_spell`](crate::cast_spell) event loop.
326    ///
327    /// # Panics
328    ///
329    /// This function needs to be called "before" initialising your slint window to avoid
330    /// panicing of this function.
331    pub fn invoke_spell(name: &str, window_conf: WindowConf) -> Self {
332        let handle = set_up_tracing(name);
333        let conn = Connection::connect_to_env().unwrap();
334        SpellWin::create_window(&conn, window_conf.clone(), name.to_string(), handle)
335    }
336
337    /// Hides the layer (aka the widget) if it is visible in screen.
338    pub fn hide(&self) {
339        if !self.is_hidden.replace(true) {
340            info!("Win: Hiding window");
341            // let width: u32 = self.adapter.size.get().width;
342            // let height: u32 = self.adapter.size.get().height;
343            // let width: u32 = self.adapter.size.get().width;
344            // let height: u32 = self.adapter.size.get().height;
345            // self.layer.as_ref().unwrap().wl_surface().damage_buffer(
346            //     0,
347            //     0,
348            //     width as i32,
349            //     height as i32,
350            // );
351            //
352            self.layer.as_ref().unwrap().wl_surface().attach(None, 0, 0);
353            // self.layer.as_ref().unwrap().commit();
354        }
355    }
356
357    /// Brings back the layer (aka the widget) back on screen if it is hidden.
358    pub fn show_again(&self) {
359        if self.is_hidden.replace(false) {
360            info!("Win: Showing window again");
361            self.set_config_internal();
362            self.first_configure.set(true);
363            self.layer.as_ref().unwrap().commit();
364        }
365    }
366
367    /// Hides the widget if visible or shows the widget back if hidden.
368    pub fn toggle(&mut self) {
369        info!("Win: view toggled");
370        if self.is_hidden.get() {
371            self.show_again();
372        } else {
373            self.hide();
374        }
375    }
376
377    /// This function adds specific rectangular regions of your complete layer to receive
378    /// input events from pointer and/or touch. The coordinates are in surface local
379    /// format from top left corener. By default, The whole layer is considered for input
380    /// events. Adding existing areas again as input region has no effect. This function
381    /// combined with transparent base widgets can be used to mimic resizable widgets.
382    pub fn add_input_region(&self, x: i32, y: i32, width: i32, height: i32) {
383        info!(
384            "Win: input region added: [x: {}, y: {}, width: {}, height: {}]",
385            x, y, width, height
386        );
387        self.input_region.add(x, y, width, height);
388        self.set_config_internal();
389        self.layer.as_ref().unwrap().commit();
390    }
391
392    /// This function subtracts specific rectangular regions of your complete layer from receiving
393    /// input events from pointer and/or touch. The coordinates are in surface local
394    /// format from top left corener. By default, The whole layer is considered for input
395    /// events. Substracting input areas which are already not input regions has no effect.
396    pub fn subtract_input_region(&self, x: i32, y: i32, width: i32, height: i32) {
397        info!(
398            "Win: input region removed: [x: {}, y: {}, width: {}, height: {}]",
399            x, y, width, height
400        );
401        self.input_region.subtract(x, y, width, height);
402        self.set_config_internal();
403        self.layer.as_ref().unwrap().commit();
404    }
405
406    /// This function marks specific rectangular regions of your complete layer as opaque.
407    /// This can result in specific optimisations from your wayland compositor, setting
408    /// this property is optional. The coordinates are in surface local format from top
409    /// left corener. Not adding opaque regions in it has no isuues but adding transparent
410    /// regions of layer as opaque can cause weird behaviour and glitches.
411    pub fn add_opaque_region(&self, x: i32, y: i32, width: i32, height: i32) {
412        info!(
413            "Win: opaque region added: [x: {}, y: {}, width: {}, height: {}]",
414            x, y, width, height
415        );
416        self.opaque_region.add(x, y, width, height);
417        self.set_config_internal();
418        self.layer.as_ref().unwrap().commit();
419    }
420
421    /// This function removes specific rectangular regions of your complete layer from being opaque.
422    /// This can result in specific optimisations from your wayland compositor, setting
423    /// this property is optional. The coordinates are in surface local format from top
424    /// left corener.
425    pub fn subtract_opaque_region(&self, x: i32, y: i32, width: i32, height: i32) {
426        info!(
427            "Win: opaque region removed: [x: {}, y: {}, width: {}, height: {}]",
428            x, y, width, height
429        );
430        self.opaque_region.subtract(x, y, width, height);
431        self.set_config_internal();
432        self.layer.as_ref().unwrap().commit();
433    }
434
435    fn set_config_internal(&self) {
436        set_config(
437            &self.config,
438            self.layer.as_ref().unwrap(),
439            Some(self.input_region.wl_region()),
440            Some(self.opaque_region.wl_region()),
441        );
442    }
443
444    fn converter(&self, qh: &QueueHandle<Self>) {
445        slint::platform::update_timers_and_animations();
446        let width: u32 = self.adapter.size.get().width;
447        let height: u32 = self.adapter.size.get().height;
448        let window_adapter = self.adapter.clone();
449
450        // Rendering from Skia
451        if !self.is_hidden.get() {
452            // let skia_now = std::time::Instant::now();
453            let redraw_val: bool = window_adapter.draw_if_needed();
454            // let elasped_time = skia_now.elapsed().as_millis();
455            // if elasped_time != 0 {
456            //     debug!("Skia Elapsed Time: {}", skia_now.elapsed().as_millis());
457            // }
458
459            let buffer = &self.buffer;
460            if self.first_configure.get() || redraw_val {
461                // if self.first_configure {
462                self.first_configure.set(false);
463                self.layer.as_ref().unwrap().wl_surface().damage_buffer(
464                    0,
465                    0,
466                    width as i32,
467                    height as i32,
468                );
469                // } else {
470                //     for (position, size) in self.damaged_part.as_ref().unwrap().iter() {
471                //         // println!(
472                //         //     "{}, {}, {}, {}",
473                //         //     position.x, position.y, size.width as i32, size.height as i32,
474                //         // );
475                //         // if size.width != width && size.height != height {
476                //         self.layer.wl_surface().damage_buffer(
477                //             position.x,
478                //             position.y,
479                //             size.width as i32,
480                //             size.height as i32,
481                //         );
482                //         //}
483                //     }
484                // }
485                // Request our next frame
486                self.layer
487                    .as_ref()
488                    .unwrap()
489                    .wl_surface()
490                    .attach(Some(buffer.wl_buffer()), 0, 0);
491            }
492
493            self.layer
494                .as_ref()
495                .unwrap()
496                .wl_surface()
497                .frame(qh, self.layer.as_ref().unwrap().wl_surface().clone());
498            self.layer.as_ref().unwrap().commit();
499        } else {
500            self.layer.as_ref().unwrap().commit();
501        }
502    }
503
504    /// Grabs the focus of keyboard. Can be used in combination with other functions
505    /// to make the widgets keyboard navigable.
506    pub fn grab_focus(&self) {
507        self.config
508            .board_interactivity
509            .set(KeyboardInteractivity::Exclusive);
510        self.layer
511            .as_ref()
512            .unwrap()
513            .set_keyboard_interactivity(KeyboardInteractivity::Exclusive);
514        self.layer.as_ref().unwrap().commit();
515    }
516
517    /// Removes the focus of keyboard from window if it currently has it.
518    pub fn remove_focus(&self) {
519        self.config
520            .board_interactivity
521            .set(KeyboardInteractivity::None);
522        self.layer
523            .as_ref()
524            .unwrap()
525            .set_keyboard_interactivity(KeyboardInteractivity::None);
526        self.layer.as_ref().unwrap().commit();
527    }
528
529    /// This method is used to set exclusive zone. Generally, useful when
530    /// dimensions of width are different than exclusive zone you want.
531    pub fn set_exclusive_zone(&mut self, val: i32) {
532        // self.set_config_internal();
533        self.config.exclusive_zone = Some(val);
534        self.layer.as_ref().unwrap().set_exclusive_zone(val);
535        self.layer.as_ref().unwrap().commit();
536    }
537}
538
539impl SpellAssociatedNew for SpellWin {
540    fn on_call(&mut self) -> Result<(), Box<dyn std::error::Error>> {
541        let event_loop = self.event_loop.clone();
542        event_loop
543            .borrow_mut()
544            .dispatch(std::time::Duration::from_millis(1), self)?;
545        Ok(())
546    }
547
548    fn get_span(&self) -> tracing::span::Span {
549        self.span.clone()
550    }
551}
552
553delegate_compositor!(SpellWin);
554delegate_registry!(SpellWin);
555delegate_output!(SpellWin);
556delegate_shm!(SpellWin);
557delegate_seat!(SpellWin);
558delegate_keyboard!(SpellWin);
559delegate_pointer!(SpellWin);
560// delegate_touch!(SpellWin);
561delegate_layer!(SpellWin);
562delegate_fractional_scale!(SpellWin);
563delegate_viewporter!(SpellWin);
564
565impl ShmHandler for SpellWin {
566    fn shm_state(&mut self) -> &mut Shm {
567        &mut self.states.shm
568    }
569}
570
571impl OutputHandler for SpellWin {
572    fn output_state(&mut self) -> &mut OutputState {
573        &mut self.states.output_state
574    }
575
576    fn new_output(
577        &mut self,
578        _conn: &Connection,
579        _qh: &QueueHandle<Self>,
580        _output: wl_output::WlOutput,
581    ) {
582        trace!("New output Source Added");
583    }
584
585    fn update_output(
586        &mut self,
587        _conn: &Connection,
588        _qh: &QueueHandle<Self>,
589        _output: wl_output::WlOutput,
590    ) {
591        trace!("Existing output is updated");
592    }
593
594    fn output_destroyed(
595        &mut self,
596        _conn: &Connection,
597        _qh: &QueueHandle<Self>,
598        _output: wl_output::WlOutput,
599    ) {
600        trace!("Output is destroyed");
601    }
602}
603
604impl CompositorHandler for SpellWin {
605    fn scale_factor_changed(
606        &mut self,
607        _: &Connection,
608        _: &QueueHandle<Self>,
609        _: &wl_surface::WlSurface,
610        _: i32,
611    ) {
612        info!("Scale factor changed, compositor msg");
613    }
614
615    fn transform_changed(
616        &mut self,
617        _conn: &Connection,
618        _qh: &QueueHandle<Self>,
619        _surface: &wl_surface::WlSurface,
620        _new_transform: wl_output::Transform,
621    ) {
622        trace!("Compositor transformation changed");
623    }
624
625    fn frame(
626        &mut self,
627        _conn: &Connection,
628        qh: &QueueHandle<Self>,
629        _surface: &wl_surface::WlSurface,
630        _time: u32,
631    ) {
632        self.converter(qh);
633    }
634
635    fn surface_enter(
636        &mut self,
637        _conn: &Connection,
638        _qh: &QueueHandle<Self>,
639        _surface: &wl_surface::WlSurface,
640        _output: &wl_output::WlOutput,
641    ) {
642        trace!("Surface entered");
643    }
644
645    fn surface_leave(
646        &mut self,
647        _conn: &Connection,
648        _qh: &QueueHandle<Self>,
649        _surface: &wl_surface::WlSurface,
650        _output: &wl_output::WlOutput,
651    ) {
652        trace!("Surface left");
653    }
654}
655
656impl FractionalScaleHandler for SpellWin {
657    fn preferred_scale(
658        &mut self,
659        _: &Connection,
660        _: &QueueHandle<Self>,
661        _: &wl_surface::WlSurface,
662        scale: u32,
663    ) {
664        info!("Scale factor changed, invoked from custom trait. {}", scale);
665
666        self.layer.as_ref().unwrap().wl_surface().damage_buffer(
667            0,
668            0,
669            self.adapter.size.get().width as i32,
670            self.adapter.size.get().height as i32,
671        );
672        // floor((273 * scale + 60) / 120)
673        let scale_factor: f32 = scale as f32 / 120.0;
674        let width: u32 = (self.adapter.size.get().width * scale + 60) / 120;
675        let height: u32 = (self.adapter.size.get().height * scale + 60) / 120;
676        info!("Physical Size: width: {}, height: {}", width, height);
677
678        self.adapter.scale_factor.set(scale_factor);
679        // TODO I can't get the viewporter to work properly. Currently spell
680        // relies on the scaling by the compositor itself. Technically all crap of
681        // related to scaling can be removed.
682        // self.states.viewporter.as_ref().unwrap().set_source(
683        //     0.,
684        //     0.,
685        //     self.adapter.size.get().width.into(),
686        //     self.adapter.size.get().height.into(),
687        // );
688        //
689        // self.states
690        //     .viewporter
691        //     .as_ref()
692        //     .unwrap()
693        //     .set_destination(width as i32, height as i32);
694        // self.adapter
695        //     .try_dispatch_event(slint::platform::WindowEvent::ScaleFactorChanged { scale_factor })
696        //     .unwrap();
697        self.adapter.request_redraw();
698        self.layer.as_ref().unwrap().commit();
699    }
700}
701
702impl LayerShellHandler for SpellWin {
703    fn closed(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, _layer: &LayerSurface) {
704        trace!("Closure of layer called");
705    }
706
707    fn configure(
708        &mut self,
709        _conn: &Connection,
710        qh: &QueueHandle<Self>,
711        _layer: &LayerSurface,
712        _configure: LayerSurfaceConfigure,
713        _serial: u32,
714    ) {
715        self.converter(qh);
716    }
717}
718
719/// This is a wrapper around calloop's [loop_handle](https://docs.rs/calloop/latest/calloop/struct.LoopHandle.html)
720/// for calling wayland specific features of `SpellWin`. It can be accessed from
721/// [`crate::wayland_adapter::SpellWin::get_handler`].
722#[derive(Clone, Debug)]
723pub struct WinHandle(pub LoopHandle<'static, SpellWin>);
724
725impl WinHandle {
726    /// Internally calls [`crate::wayland_adapter::SpellWin::hide`]
727    pub fn hide(&self) {
728        self.0.insert_idle(|win| win.hide());
729    }
730
731    /// Internally calls [`crate::wayland_adapter::SpellWin::show_again`]
732    pub fn show_again(&self) {
733        self.0.insert_idle(|win| win.show_again());
734    }
735
736    /// Internally calls [`crate::wayland_adapter::SpellWin::toggle`]
737    pub fn toggle(&self) {
738        self.0.insert_idle(|win| win.toggle());
739    }
740
741    /// Internally calls [`crate::wayland_adapter::SpellWin::grab_focus`]
742    pub fn grab_focus(&self) {
743        self.0.insert_idle(|win| win.grab_focus());
744    }
745
746    /// Internally calls [`crate::wayland_adapter::SpellWin::remove_focus`]
747    pub fn remove_focus(&self) {
748        self.0.insert_idle(|win| win.remove_focus());
749    }
750
751    /// Internally calls [`crate::wayland_adapter::SpellWin::add_input_region`]
752    pub fn add_input_region(&self, x: i32, y: i32, width: i32, height: i32) {
753        self.0
754            .insert_idle(move |win| win.add_input_region(x, y, width, height));
755    }
756
757    /// Internally calls [`crate::wayland_adapter::SpellWin::subtract_input_region`]
758    pub fn subtract_input_region(&self, x: i32, y: i32, width: i32, height: i32) {
759        self.0
760            .insert_idle(move |win| win.subtract_input_region(x, y, width, height));
761    }
762
763    /// Internally calls [`crate::wayland_adapter::SpellWin::add_opaque_region`]
764    pub fn add_opaque_region(&self, x: i32, y: i32, width: i32, height: i32) {
765        self.0
766            .insert_idle(move |win| win.add_opaque_region(x, y, width, height));
767    }
768
769    /// Internally calls [`crate::wayland_adapter::SpellWin::subtract_opaque_region`]
770    pub fn subtract_opaque_region(&self, x: i32, y: i32, width: i32, height: i32) {
771        self.0
772            .insert_idle(move |win| win.subtract_opaque_region(x, y, width, height));
773    }
774
775    /// Internally calls [`crate::wayland_adapter::SpellWin::set_exclusive_zone`]
776    pub fn set_exclusive_zone(&self, val: i32) {
777        self.0.insert_idle(move |win| win.set_exclusive_zone(val));
778    }
779}
780
781/// SpellLock is a struct which represents a window lock. It can be run and initialised
782/// on a custom lockscreen implementation with slint.
783/// Know limitations include the abscence to verify from fingerprints and unideal issues on
784/// multi-monitor setup. You can add the path of binary of your lock in your compositor config and idle
785/// manager config to use the program. It will be linked to spell-cli directly in coming releases.
786///
787/// ## Example
788/// Here is a minimal example of rust side, for complete code of slint, check
789/// the codebase of young-shell.
790///
791/// ```rust
792/// use spell_framework::cast_spell;
793/// use std::{error::Error, sync::{Arc, RwLock}};
794/// use slint::ComponentHandle;
795/// use spell_framework::{layer_properties::ForeignController, wayland_adapter::SpellLock};
796/// slint::include_modules!();
797///
798/// fn main() -> Result<(), Box<dyn Error>> {
799///     let lock = SpellLock::invoke_lock_spell();
800///     let lock_ui = LockScreen::new().unwrap();
801///     let looop_handle = lock.get_handler();
802///     lock_ui.on_check_pass({
803///         let lock_handle = lock_ui.as_weak();
804///         move |string_val| {
805///             let lock_handle_a = lock_handle.clone().unwrap();
806///             let lock_handle_b = lock_handle.clone().unwrap();
807///             looop_handle.unlock(
808///                 None,
809///                 string_val.to_string(),
810///                 Box::new(move || {
811///                     lock_handle_a.set_lock_error(true);
812///                 }),
813///                 Box::new(move || {
814///                     lock_handle_b.set_is_lock_activated(false);
815///                 }),
816///             );
817///         }
818///     });
819///     lock_ui.set_is_lock_activated(true);
820///     cast_spell(
821///         lock,
822///         None,
823///         None::<fn(Arc<RwLock<Box<dyn ForeignController>>>)>,
824///     )
825/// }
826/// ```
827pub struct SpellLock {
828    pub(crate) loop_handle: LoopHandle<'static, SpellLock>,
829    pub(crate) conn: Connection,
830    pub(crate) compositor_state: CompositorState,
831    pub(crate) registry_state: RegistryState,
832    pub(crate) output_state: OutputState,
833    pub(crate) keyboard_state: KeyboardState,
834    pub(crate) pointer_state: PointerState,
835    pub(crate) seat_state: SeatState,
836    pub(crate) shm: Shm,
837    pub(crate) session_lock: Option<SessionLock>,
838    pub(crate) lock_surfaces: Vec<SessionLockSurface>,
839    pub(crate) slint_part: Option<SpellSlintLock>,
840    pub(crate) is_locked: bool,
841    // TODO, check if it need internal mutability?
842    pub(crate) event_loop: Rc<RefCell<EventLoop<'static, SpellLock>>>,
843    pub(crate) backspace: Option<RegistrationToken>,
844}
845
846impl std::fmt::Debug for SpellLock {
847    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
848        f.debug_struct("SpellLock")
849            .field("is_locked", &self.is_locked)
850            .finish()
851    }
852}
853impl SpellLock {
854    /// This function creates an instance of SpellLock which can be combined with
855    /// slint windows to create a lockscreen.
856    pub fn invoke_lock_spell() -> Self {
857        let conn = Connection::connect_to_env().unwrap();
858        let _ = set_up_tracing("SpellLock");
859        let (globals, mut event_queue) = registry_queue_init(&conn).unwrap();
860        let qh: QueueHandle<SpellLock> = event_queue.handle();
861        let registry_state = RegistryState::new(&globals);
862        let shm = Shm::bind(&globals, &qh).unwrap();
863        let event_loop: EventLoop<'static, SpellLock> =
864            EventLoop::try_new().expect("Failed to initialize the event loop!");
865        let output_state = OutputState::new(&globals, &qh);
866        let session_lock_state = SessionLockState::new(&globals, &qh);
867        let compositor_state =
868            CompositorState::bind(&globals, &qh).expect("Faild to create compositor state");
869        let cursor_manager =
870            CursorShapeManager::bind(&globals, &qh).expect("cursor shape is not available");
871        let mut win_handler_vec: Vec<(String, (u32, u32))> = Vec::new();
872        let lock_surfaces = Vec::new();
873
874        let keyboard_state = KeyboardState {
875            board: None,
876            // board_data: None,
877        };
878        let pointer_state = PointerState {
879            pointer: None,
880            pointer_data: None,
881            cursor_shape: cursor_manager,
882        };
883        let mut spell_lock = SpellLock {
884            loop_handle: event_loop.handle().clone(),
885            conn: conn.clone(),
886            compositor_state,
887            output_state,
888            keyboard_state,
889            pointer_state,
890            registry_state,
891            seat_state: SeatState::new(&globals, &qh),
892            slint_part: None,
893            shm,
894            session_lock: None,
895            lock_surfaces,
896            is_locked: true,
897            event_loop: Rc::new(RefCell::new(event_loop)),
898            backspace: None,
899        };
900
901        let _ = event_queue.roundtrip(&mut spell_lock);
902
903        let session_lock = Some(
904            session_lock_state
905                .lock(&qh)
906                .expect("ext-session-lock not supported"),
907        );
908
909        spell_lock.session_lock = session_lock;
910        for output in spell_lock.output_state.outputs() {
911            let output_info: output::OutputInfo = spell_lock.output_state.info(&output).unwrap();
912            let output_name: String = output_info.name.unwrap_or_else(|| "SomeOutput".to_string());
913            let dimensions = (
914                output_info.logical_size.unwrap().0 as u32,
915                output_info.logical_size.unwrap().1 as u32,
916            );
917            win_handler_vec.push((output_name, dimensions));
918
919            let session_lock = spell_lock.session_lock.as_ref().unwrap();
920            let surface = spell_lock.compositor_state.create_surface(&qh);
921
922            // It's important to keep the `SessionLockSurface` returned here around, as the
923            // surface will be destroyed when the `SessionLockSurface` is dropped.
924            let lock_surface = session_lock.create_lock_surface(surface, &output, &qh);
925            spell_lock.lock_surfaces.push(lock_surface);
926        }
927        let multi_handler = SpellMultiWinHandler::new_lock(win_handler_vec);
928        let sizes: Vec<PhysicalSize> = multi_handler
929            .borrow()
930            .windows
931            .iter()
932            .map(|(_, conf)| {
933                if let LayerConf::Lock(width, height) = conf {
934                    PhysicalSize {
935                        width: *width,
936                        height: *height,
937                    }
938                } else {
939                    panic!("Shouldn't enter here");
940                }
941            })
942            .collect();
943
944        let mut pool = SlotPool::new(
945            (sizes[0].width * sizes[0].height * 4) as usize,
946            &spell_lock.shm,
947        )
948        .expect("Couldn't create pool");
949        let mut buffer_slots: Vec<RefCell<Slot>> = Vec::new();
950        let buffers: Vec<Buffer> = sizes
951            .iter()
952            .map(|physical_size| {
953                let stride = physical_size.width as i32 * 4;
954                let (wayland_buffer, _) = pool
955                    .create_buffer(
956                        physical_size.width as i32,
957                        physical_size.height as i32,
958                        stride,
959                        wl_shm::Format::Argb8888,
960                    )
961                    .expect("Creating Buffer");
962                buffer_slots.push(RefCell::new(wayland_buffer.slot()));
963                wayland_buffer
964            })
965            .collect();
966        let slint_proxy = Arc::new(Mutex::new(Vec::new()));
967        let pool: Rc<RefCell<SlotPool>> = Rc::new(RefCell::new(pool));
968        let mut adapters: Vec<Rc<SpellSkiaWinAdapter>> = Vec::new();
969        buffer_slots
970            .into_iter()
971            .enumerate()
972            .for_each(|(index, slot)| {
973                let adapter = SpellSkiaWinAdapter::new(
974                    pool.clone(),
975                    slot,
976                    sizes[index].width,
977                    sizes[index].height,
978                    slint_proxy.clone(),
979                );
980                adapters.push(adapter);
981            });
982
983        multi_handler.borrow_mut().adapter = adapters.clone();
984        spell_lock.slint_part = Some(SpellSlintLock {
985            adapters,
986            size: sizes,
987            wayland_buffer: buffers,
988        });
989
990        spell_lock.backspace = Some(
991            spell_lock
992                .loop_handle
993                .insert_source(
994                    Timer::from_duration(Duration::from_millis(1500)),
995                    |_, _, data| {
996                        data.slint_part.as_ref().unwrap().adapters[0]
997                            .try_dispatch_event(slint::platform::WindowEvent::KeyPressed {
998                                text: Key::Backspace.into(),
999                            })
1000                            .unwrap();
1001                        TimeoutAction::ToDuration(Duration::from_millis(1500))
1002                    },
1003                )
1004                .unwrap(),
1005        );
1006
1007        spell_lock
1008            .loop_handle
1009            .disable(&spell_lock.backspace.unwrap())
1010            .unwrap();
1011        let _ = slint::platform::set_platform(Box::new(SpellLockShell::new(multi_handler)));
1012
1013        WaylandSource::new(spell_lock.conn.clone(), event_queue)
1014            .insert(spell_lock.loop_handle.clone())
1015            .unwrap();
1016        spell_lock
1017    }
1018
1019    fn converter_lock(&mut self, qh: &QueueHandle<Self>) {
1020        slint::platform::update_timers_and_animations();
1021        let width: u32 = self.slint_part.as_ref().unwrap().size[0].width;
1022        let height: u32 = self.slint_part.as_ref().unwrap().size[0].height;
1023        let window_adapter = self.slint_part.as_ref().unwrap().adapters[0].clone();
1024
1025        // Rendering from Skia
1026        // if self.is_locked {
1027        // let skia_now = std::time::Instant::now();
1028        let _redraw_val: bool = window_adapter.draw_if_needed();
1029        // println!("Skia Elapsed Time: {}", skia_now.elapsed().as_millis());
1030
1031        let buffer = &self.slint_part.as_ref().unwrap().wayland_buffer[0];
1032        // Damage the entire window
1033        // if self.first_configure {
1034        // self.first_configure = false;
1035        self.lock_surfaces[0]
1036            .wl_surface()
1037            .damage_buffer(0, 0, width as i32, height as i32);
1038        // } else {
1039        //     for (position, size) in self.damaged_part.as_ref().unwrap().iter() {
1040        //         // println!(
1041        //         //     "{}, {}, {}, {}",
1042        //         //     position.x, position.y, size.width as i32, size.height as i32,
1043        //         // );
1044        //         // if size.width != width && size.height != height {
1045        //         self.layer.wl_surface().damage_buffer(
1046        //             position.x,
1047        //             position.y,
1048        //             size.width as i32,
1049        //             size.height as i32,
1050        //         );
1051        //         //}
1052        //     }
1053        // }
1054        // Request our next frame
1055        self.lock_surfaces[0]
1056            .wl_surface()
1057            .frame(qh, self.lock_surfaces[0].wl_surface().clone());
1058        self.lock_surfaces[0]
1059            .wl_surface()
1060            .attach(Some(buffer.wl_buffer()), 0, 0);
1061        // } else {
1062        // println!("Is_hidden is true.");
1063        // }
1064
1065        self.lock_surfaces[0].wl_surface().commit();
1066        // core::mem::swap::<&mut [u8]>(&mut sec_canvas_data.as_mut_slice(), &mut primary_canvas);
1067        // core::mem::swap::<&mut [Rgba8Pixel]>( &mut &mut *work_buffer, &mut &mut *currently_displayed_buffer,);
1068
1069        // TODO save and reuse buffer when the window size is unchanged.  This is especially
1070        // useful if you do damage tracking, since you don't need to redraw the undamaged parts
1071        // of the canvas.
1072    }
1073
1074    fn unlock_finger(&mut self) -> PamResult<()> {
1075        let finger = FingerprintInfo;
1076        let output = Command::new("sh")
1077            .arg("-c")
1078            .arg("last | awk '{print $1}' | sort | uniq -c | sort -nr")
1079            .output()
1080            .expect("Couldn't retrive username");
1081
1082        let val = String::from_utf8_lossy(&output.stdout);
1083        let val_2 = val.split('\n').collect::<Vec<_>>()[0].trim();
1084        let user_name = val_2.split(" ").collect::<Vec<_>>()[1].to_string();
1085
1086        let mut txn = TransactionBuilder::new_with_service("login")
1087            .username(user_name)
1088            .build(finger.into_conversation())?;
1089        // If authentication fails, this will return an error.
1090        // We immediately give up rather than re-prompting the user.
1091        txn.authenticate(AuthnFlags::empty())?;
1092        txn.account_management(AuthnFlags::empty())?;
1093        if let Some(locked_val) = self.session_lock.take() {
1094            locked_val.unlock();
1095        } else {
1096            warn!("Authentication verified but couldn't unlock");
1097        }
1098        self.is_locked = false;
1099        self.conn.roundtrip().unwrap();
1100        Ok(())
1101    }
1102
1103    fn unlock(
1104        &mut self,
1105        username: Option<&str>,
1106        password: &str,
1107        on_unlock_callback: Box<dyn FnOnce()>,
1108    ) -> PamResult<()> {
1109        let user_name;
1110        if let Some(username) = username {
1111            user_name = username.to_string();
1112        } else {
1113            let output = Command::new("sh")
1114                .arg("-c")
1115                .arg("last | awk '{print $1}' | sort | uniq -c | sort -nr")
1116                .output()
1117                .expect("Couldn't retrive username");
1118
1119            let val = String::from_utf8_lossy(&output.stdout);
1120            let val_2 = val.split('\n').collect::<Vec<_>>()[0].trim();
1121            user_name = val_2.split(" ").collect::<Vec<_>>()[1].to_string();
1122        }
1123
1124        let user_pass = UsernamePassConvo {
1125            username: user_name.clone(),
1126            password: password.into(),
1127        };
1128
1129        let mut txn = TransactionBuilder::new_with_service("login")
1130            .username(user_name)
1131            .build(user_pass.into_conversation())?;
1132        // If authentication fails, this will return an error.
1133        // We immediately give up rather than re-prompting the user.
1134        txn.authenticate(AuthnFlags::empty())?;
1135        txn.account_management(AuthnFlags::empty())?;
1136
1137        on_unlock_callback();
1138        if let Some(locked_val) = self.session_lock.take() {
1139            locked_val.unlock();
1140        } else {
1141            warn!("Authentication verified but couldn't unlock");
1142        }
1143        self.is_locked = false;
1144        self.conn.roundtrip().unwrap();
1145        Ok(())
1146    }
1147
1148    /// Provides a lockscreen handler used to invoke the unlock
1149    /// callback with the user entered password.For more details
1150    /// view [`LockHandle`].
1151    pub fn get_handler(&self) -> LockHandle {
1152        LockHandle(self.loop_handle.clone())
1153    }
1154}
1155
1156impl SpellAssociatedNew for SpellLock {
1157    fn on_call(&mut self) -> Result<(), Box<dyn std::error::Error>> {
1158        let event_loop = self.event_loop.clone();
1159        event_loop
1160            .borrow_mut()
1161            .dispatch(std::time::Duration::from_millis(1), self)?;
1162        Ok(())
1163    }
1164    fn is_locked(&self) -> bool {
1165        self.is_locked
1166    }
1167}
1168
1169/// Struct to handle unlocking of a SpellLock instance. It can be captured from
1170/// [`SpellLock::get_handler`].
1171#[derive(Debug, Clone)]
1172pub struct LockHandle(LoopHandle<'static, SpellLock>);
1173
1174impl LockHandle {
1175    /// Call this method to unlock Spelllock. It also takes two callbacks which
1176    /// are invoked when the password parsed is wrong or right (i.e. resulting
1177    /// in an screen unlock) respectively. Callbacks can be used to invoke UI
1178    /// specific changes for your slint frontend.
1179    pub fn unlock(
1180        &self,
1181        username: Option<String>,
1182        password: String,
1183        on_err_callback: Box<dyn FnOnce()>,
1184        on_unlock_callback: Box<dyn FnOnce()>,
1185    ) {
1186        self.0.insert_idle(move |app_data| {
1187            if app_data
1188                .unlock(username.as_deref(), &password, on_unlock_callback)
1189                .is_err()
1190            {
1191                on_err_callback();
1192            }
1193        });
1194    }
1195
1196    pub fn verify_fingerprint(&self, error_callback: Box<dyn FnOnce()>) {
1197        self.0.insert_idle(move |app_data| {
1198            if let Err(err) = app_data.unlock_finger() {
1199                println!("{:?}", err);
1200                error_callback();
1201            } else {
1202                println!("Passed");
1203            }
1204        });
1205    }
1206}
1207delegate_keyboard!(SpellLock);
1208delegate_compositor!(SpellLock);
1209delegate_output!(SpellLock);
1210delegate_shm!(SpellLock);
1211delegate_registry!(SpellLock);
1212delegate_pointer!(SpellLock);
1213delegate_session_lock!(SpellLock);
1214delegate_seat!(SpellLock);
1215
1216/// Future XDGpopup implementation will occur on this struct;
1217pub struct SpellXdg;
1218
1219/// Furture virtual keyboard implementation will be on this type. Currently, it is redundent.
1220pub struct SpellBoard;