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