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