Skip to main content

spell_framework/wayland_adapter/window/
popup.rs

1use slint::platform::WindowAdapter;
2use smithay_client_toolkit::{
3    reexports::{
4        client::{
5            QueueHandle,
6            protocol::{wl_shm, wl_surface::WlSurface},
7        },
8        protocols::xdg::shell::client::xdg_surface::XdgSurface,
9    },
10    shell::xdg::popup::Popup,
11    shm::slot::{Buffer, SlotPool},
12};
13use std::{
14    cell::{Cell, RefCell},
15    collections::HashMap,
16    rc::Rc,
17};
18use tracing::{info, warn};
19
20use crate::{
21    PopupSlint,
22    configure::{PopupConf, PopupCore},
23    slint_adapter::{ADAPTERS, SpellSkiaWinAdapter},
24    wayland_adapter::{
25        SpellWin,
26        fractional_scaling::{
27            FractionalScaleHandler, FractionalScaleState, delegate_fractional_scale,
28        },
29        viewporter::{ViewporterState, delegate_viewporter},
30    },
31};
32
33pub(super) struct PopupManager {
34    id_gen: u32,
35    popups: HashMap<u32, Box<dyn PopupSlint>>,
36    pool: Option<Rc<RefCell<SlotPool>>>,
37}
38
39impl PopupManager {
40    pub(super) fn new() -> Self {
41        PopupManager {
42            id_gen: 0,
43            popups: HashMap::new(),
44            pool: None,
45        }
46    }
47
48    pub(super) fn return_popup(&self, popup_inner: &Popup) -> Option<&dyn PopupSlint> {
49        for popup in self.popups.values() {
50            if popup_inner == popup.inner() {
51                return Some(popup.as_ref());
52            }
53        }
54        None
55    }
56
57    pub(super) fn set_pool(&mut self, pool: Rc<RefCell<SlotPool>>) {
58        self.pool = Some(pool);
59    }
60
61    pub(super) fn create_popup_core(
62        &mut self,
63        popup: Popup,
64        popup_conf: PopupConf,
65        _fractional_scale_state: &FractionalScaleState,
66        _viewporter_state: &ViewporterState,
67        _qh: &QueueHandle<SpellWin>,
68    ) -> PopupCore {
69        // let fractional_scale = fractional_scale_state.get_scale(popup.wl_surface(), qh);
70        // let viewport = viewporter_state.get_viewport(popup.wl_surface(), qh, fractional_scale);
71        let stride = popup_conf.width as i32 * 4;
72        let (buffer, _) = self
73            .pool
74            .as_ref()
75            .unwrap()
76            .borrow_mut()
77            .create_buffer(
78                popup_conf.width as i32,
79                popup_conf.height as i32,
80                stride,
81                wl_shm::Format::Argb8888,
82            )
83            .expect("failed to create buffer for popup");
84        // viewport.set_destination(popup_conf.width as i32, popup_conf.height as i32);
85        // popup.wl_surface().attach(Some(buffer.wl_buffer()), 0, 0);
86        // popup
87        //     .wl_surface()
88        //     .damage(0, 0, popup_conf.width as i32, popup_conf.height as i32);
89        // popup.xdg_surface().set_window_geometry(
90        //     0,
91        //     0,
92        //     popup_conf.width as i32,
93        //     popup_conf.height as i32,
94        // );
95        popup.wl_surface().commit();
96        // popup.xdg_surface().config
97        // popup.wl_surface().set_buffer_transform(
98        //     smithay_client_toolkit::reexports::client::protocol::wl_output::Transform::Normal,
99        // );
100        // popup.wl_surface().set_buffer_scale(1);
101        // popup.wl_surface().commit();
102
103        PopupCore {
104            pool: self.pool.as_ref().unwrap().clone(),
105            popup,
106            popup_conf,
107            buffer,
108            // viewport,
109        }
110    }
111
112    pub(super) fn add_popup<T: PopupSlint + 'static>(&mut self, popup_instance: T) -> u32 {
113        self.popups.insert(self.id_gen, Box::new(popup_instance));
114        info!("[Popup Manager]: Popup added for rendering");
115        self.id_gen = self.id_gen.wrapping_add(1);
116        self.id_gen - 1
117    }
118
119    pub(super) fn redraw_popups(&self, qh: &QueueHandle<SpellWin>) {
120        for popup in self.popups.values() {
121            popup.converter_popup(popup.inner().wl_surface(), qh);
122        }
123    }
124
125    pub(super) fn return_adapter(
126        &self,
127        surface: &WlSurface,
128    ) -> Option<&std::rc::Rc<SpellSkiaWinAdapter>> {
129        for popup in self.popups.values() {
130            if popup.inner().wl_surface() == surface {
131                return Some(popup.adapter());
132            }
133        }
134        None
135    }
136
137    pub(super) fn call_ack(&self, xdg_surface: &XdgSurface, serial: u32) {
138        for popup in self.popups.values() {
139            if popup.inner().xdg_surface() == xdg_surface {
140                popup.inner().xdg_surface().ack_configure(serial);
141            }
142        }
143    }
144
145    pub(super) fn close_popup(&mut self, id: &u32) {
146        if let Some(rem_popup) = self.popups.remove(id) {
147            rem_popup.inner().xdg_popup().destroy();
148            info!("Removed Popup with id: {}", id);
149        } else {
150            warn!(
151                "[PopupManager]: trying to remove a non-existant popup with id: {}",
152                id
153            );
154        };
155    }
156}
157
158/// This struct holds the backend information for creating and managing a XDG
159/// popup in spell. It needs a [`PopupCore`] instance for initialisation
160/// and it needs to be initialsed before the corresponding slint frontend. It is
161/// better to wrap it in an external wrapper object along with frontend to satisfy
162/// trait requirements of [`PopupSlint`]. For example, refer to popup example in
163/// spell-demo.
164pub struct SpellXDGPopup {
165    adapter: Rc<SpellSkiaWinAdapter>,
166    popup: Popup,
167    buffer: Buffer,
168    first_configure: Cell<bool>,
169    // viewport: Viewport,
170}
171
172delegate_fractional_scale!(SpellXDGPopup);
173delegate_viewporter!(SpellXDGPopup);
174
175impl SpellXDGPopup {
176    /// Creates an instance provided [`PopupCore`].
177    pub fn new(popup_settings: PopupCore) -> Self {
178        let adapter_value: Rc<SpellSkiaWinAdapter> = SpellSkiaWinAdapter::new(
179            popup_settings.pool,
180            RefCell::new(popup_settings.buffer.slot()),
181            popup_settings.popup_conf.width,
182            popup_settings.popup_conf.height,
183        );
184        ADAPTERS.with_borrow_mut(|v| v.push(adapter_value.clone()));
185        SpellXDGPopup {
186            adapter: adapter_value,
187            popup: popup_settings.popup,
188            buffer: popup_settings.buffer,
189            first_configure: Cell::new(true),
190            // viewport: popup_settings.viewport,
191        }
192    }
193
194    /// Method necessary for a [`PopupSlint`] implementation.
195    pub fn popup(&self) -> &Popup {
196        &self.popup
197    }
198
199    /// Method necessary for a [`PopupSlint`] implementation.
200    pub fn first_configure(&self) -> bool {
201        if self.first_configure.get() {
202            self.first_configure.set(false);
203            true
204        } else {
205            false
206        }
207    }
208
209    /// Method necessary for a [`PopupSlint`] implementation.
210    pub fn adapter(&self) -> &std::rc::Rc<SpellSkiaWinAdapter> {
211        &self.adapter
212    }
213
214    /// Method necessary for a [`PopupSlint`] implementation.
215    pub fn converter_popup<'a>(&self, wl_surface: &'a WlSurface, qh: &'a QueueHandle<SpellWin>) {
216        slint::platform::update_timers_and_animations();
217        let width: u32 = self.adapter.as_ref().size.get().width;
218        let height: u32 = self.adapter.as_ref().size.get().height;
219        let window_adapter = self.adapter.clone();
220
221        let redraw_val: bool = window_adapter.draw_if_needed();
222        let buffer = &self.buffer;
223        if self.first_configure.get() || redraw_val {
224            // if self.first_configure {
225            // self.first_configure.set(false);
226            wl_surface.damage_buffer(0, 0, width as i32, height as i32);
227            // } else {
228            //     for (position, size) in self.damaged_part.as_ref().unwrap().iter() {
229            //         // println!(
230            //         //     "{}, {}, {}, {}",
231            //         //     position.x, position.y, size.width as i32, size.height as i32,
232            //         // );
233            //         // if size.width != width && size.height != height {
234            //         self.layer.wl_surface().damage_buffer(
235            //             position.x,
236            //             position.y,
237            //             size.width as i32,
238            //             size.height as i32,
239            //         );
240            //         //}
241            //     }
242            // }
243            // Request our next frame
244            wl_surface.attach(Some(buffer.wl_buffer()), 0, 0);
245            wl_surface.frame(qh, wl_surface.clone());
246            wl_surface.commit();
247        } else {
248            wl_surface.commit();
249        }
250    }
251}
252
253impl FractionalScaleHandler for SpellXDGPopup {
254    fn preferred_scale(
255        &mut self,
256        _: &smithay_client_toolkit::reexports::client::Connection,
257        _: &QueueHandle<Self>,
258        _: &WlSurface,
259        scale: u32,
260    ) {
261        info!(
262            "Scale factor of popup changed, invoked from custom trait: {}",
263            scale
264        );
265        // FIXME: Make use of this for proper scaling implementation.
266        let _width_old = self.adapter.size_original.get().width;
267        let _height_old = self.adapter.size_original.get().height;
268        self.popup.wl_surface().damage_buffer(
269            0,
270            0,
271            self.adapter.size.get().width as i32,
272            self.adapter.size.get().height as i32,
273        );
274        // FIXME: Make use of this for proper scaling implementation.
275        let (buffer, _width, _height, scale_factor) = self.adapter.changed_scale_factor(scale);
276        // self.width = width;
277        // self.height = height;
278        self.buffer = buffer;
279        self.adapter
280            .try_dispatch_event(slint::platform::WindowEvent::ScaleFactorChanged { scale_factor })
281            .unwrap();
282        // self.viewport.set_source(
283        //     0.,
284        //     0.,
285        //     self.adapter.size.get().width.into(),
286        //     self.adapter.size.get().height.into(),
287        // );
288        //
289        // self.viewport
290        //     .set_destination(width_old as i32, height_old as i32);
291        self.adapter.request_redraw();
292        self.popup.wl_surface().commit();
293    }
294}