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