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