window.rs

  1use std::any::Any;
  2use std::cell::RefCell;
  3use std::ffi::c_void;
  4use std::rc::Rc;
  5use std::sync::Arc;
  6
  7use blade_graphics as gpu;
  8use blade_rwh::{HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle};
  9use futures::channel::oneshot::Receiver;
 10use raw_window_handle::{
 11    DisplayHandle, HandleError, HasDisplayHandle, HasWindowHandle, WindowHandle,
 12};
 13use wayland_client::{protocol::wl_surface, Proxy};
 14use wayland_protocols::wp::viewporter::client::wp_viewport;
 15use wayland_protocols::xdg::shell::client::xdg_toplevel;
 16
 17use crate::platform::blade::BladeRenderer;
 18use crate::platform::linux::wayland::display::WaylandDisplay;
 19use crate::platform::{PlatformAtlas, PlatformInputHandler, PlatformWindow};
 20use crate::scene::Scene;
 21use crate::{
 22    px, size, Bounds, Modifiers, Pixels, PlatformDisplay, PlatformInput, Point, PromptLevel, Size,
 23    WindowAppearance, WindowBounds, WindowOptions,
 24};
 25
 26#[derive(Default)]
 27pub(crate) struct Callbacks {
 28    request_frame: Option<Box<dyn FnMut()>>,
 29    input: Option<Box<dyn FnMut(crate::PlatformInput) -> bool>>,
 30    active_status_change: Option<Box<dyn FnMut(bool)>>,
 31    resize: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
 32    fullscreen: Option<Box<dyn FnMut(bool)>>,
 33    moved: Option<Box<dyn FnMut()>>,
 34    should_close: Option<Box<dyn FnMut() -> bool>>,
 35    close: Option<Box<dyn FnOnce()>>,
 36    appearance_changed: Option<Box<dyn FnMut()>>,
 37}
 38
 39struct WaylandWindowInner {
 40    renderer: BladeRenderer,
 41    bounds: Bounds<i32>,
 42    scale: f32,
 43    input_handler: Option<PlatformInputHandler>,
 44    decoration_state: WaylandDecorationState,
 45}
 46
 47struct RawWindow {
 48    window: *mut c_void,
 49    display: *mut c_void,
 50}
 51
 52unsafe impl HasRawWindowHandle for RawWindow {
 53    fn raw_window_handle(&self) -> RawWindowHandle {
 54        let mut wh = blade_rwh::WaylandWindowHandle::empty();
 55        wh.surface = self.window;
 56        wh.into()
 57    }
 58}
 59
 60unsafe impl HasRawDisplayHandle for RawWindow {
 61    fn raw_display_handle(&self) -> RawDisplayHandle {
 62        let mut dh = blade_rwh::WaylandDisplayHandle::empty();
 63        dh.display = self.display;
 64        dh.into()
 65    }
 66}
 67
 68impl WaylandWindowInner {
 69    fn new(wl_surf: &Arc<wl_surface::WlSurface>, bounds: Bounds<i32>) -> Self {
 70        let raw = RawWindow {
 71            window: wl_surf.id().as_ptr().cast::<c_void>(),
 72            display: wl_surf
 73                .backend()
 74                .upgrade()
 75                .unwrap()
 76                .display_ptr()
 77                .cast::<c_void>(),
 78        };
 79        let gpu = Arc::new(
 80            unsafe {
 81                gpu::Context::init_windowed(
 82                    &raw,
 83                    gpu::ContextDesc {
 84                        validation: false,
 85                        capture: false,
 86                    },
 87                )
 88            }
 89            .unwrap(),
 90        );
 91        let extent = gpu::Extent {
 92            width: bounds.size.width as u32,
 93            height: bounds.size.height as u32,
 94            depth: 1,
 95        };
 96        Self {
 97            renderer: BladeRenderer::new(gpu, extent),
 98            bounds,
 99            scale: 1.0,
100            input_handler: None,
101
102            // On wayland, decorations are by default provided by the client
103            decoration_state: WaylandDecorationState::Client,
104        }
105    }
106}
107
108pub(crate) struct WaylandWindowState {
109    inner: RefCell<WaylandWindowInner>,
110    pub(crate) callbacks: RefCell<Callbacks>,
111    pub(crate) surface: Arc<wl_surface::WlSurface>,
112    pub(crate) toplevel: Arc<xdg_toplevel::XdgToplevel>,
113    viewport: Option<wp_viewport::WpViewport>,
114}
115
116impl WaylandWindowState {
117    pub(crate) fn new(
118        wl_surf: Arc<wl_surface::WlSurface>,
119        viewport: Option<wp_viewport::WpViewport>,
120        toplevel: Arc<xdg_toplevel::XdgToplevel>,
121        options: WindowOptions,
122    ) -> Self {
123        if options.bounds == WindowBounds::Maximized {
124            toplevel.set_maximized();
125        } else if options.bounds == WindowBounds::Fullscreen {
126            toplevel.set_fullscreen(None);
127        }
128
129        let bounds: Bounds<i32> = match options.bounds {
130            WindowBounds::Fullscreen | WindowBounds::Maximized => Bounds {
131                origin: Point::default(),
132                size: Size {
133                    width: 500,
134                    height: 500,
135                }, // todo(implement)
136            },
137            WindowBounds::Fixed(bounds) => bounds.map(|p| p.0 as i32),
138        };
139
140        Self {
141            surface: Arc::clone(&wl_surf),
142            inner: RefCell::new(WaylandWindowInner::new(&wl_surf, bounds)),
143            callbacks: RefCell::new(Callbacks::default()),
144            toplevel,
145            viewport,
146        }
147    }
148
149    pub fn update(&self) {
150        let mut cb = self.callbacks.borrow_mut();
151        if let Some(mut fun) = cb.request_frame.take() {
152            drop(cb);
153            fun();
154            self.callbacks.borrow_mut().request_frame = Some(fun);
155        }
156    }
157
158    pub fn set_size_and_scale(&self, width: i32, height: i32, scale: f32) {
159        self.inner.borrow_mut().scale = scale;
160        self.inner.borrow_mut().bounds.size.width = width;
161        self.inner.borrow_mut().bounds.size.height = height;
162        self.inner.borrow_mut().renderer.update_drawable_size(size(
163            width as f64 * scale as f64,
164            height as f64 * scale as f64,
165        ));
166
167        if let Some(ref mut fun) = self.callbacks.borrow_mut().resize {
168            fun(
169                Size {
170                    width: px(width as f32),
171                    height: px(height as f32),
172                },
173                scale,
174            );
175        }
176
177        if let Some(viewport) = &self.viewport {
178            viewport.set_destination(width, height);
179        }
180    }
181
182    pub fn resize(&self, width: i32, height: i32) {
183        let scale = self.inner.borrow_mut().scale;
184        self.set_size_and_scale(width, height, scale);
185    }
186
187    pub fn rescale(&self, scale: f32) {
188        let bounds = self.inner.borrow_mut().bounds;
189        self.set_size_and_scale(bounds.size.width, bounds.size.height, scale)
190    }
191
192    /// Notifies the window of the state of the decorations.
193    ///
194    /// # Note
195    ///
196    /// This API is indirectly called by the wayland compositor and
197    /// not meant to be called by a user who wishes to change the state
198    /// of the decorations. This is because the state of the decorations
199    /// is managed by the compositor and not the client.
200    pub fn set_decoration_state(&self, state: WaylandDecorationState) {
201        self.inner.borrow_mut().decoration_state = state;
202        log::trace!("Window decorations are now handled by {:?}", state);
203        // todo(linux) - Handle this properly
204    }
205
206    pub fn close(&self) {
207        let mut callbacks = self.callbacks.borrow_mut();
208        if let Some(fun) = callbacks.close.take() {
209            fun()
210        }
211        self.toplevel.destroy();
212    }
213
214    pub fn handle_input(&self, input: PlatformInput) {
215        if let Some(ref mut fun) = self.callbacks.borrow_mut().input {
216            if fun(input.clone()) {
217                return;
218            }
219        }
220        if let PlatformInput::KeyDown(event) = input {
221            let mut inner = self.inner.borrow_mut();
222            if let Some(ref mut input_handler) = inner.input_handler {
223                if let Some(ime_key) = &event.keystroke.ime_key {
224                    input_handler.replace_text_in_range(None, ime_key);
225                }
226            }
227        }
228    }
229
230    pub fn set_focused(&self, focus: bool) {
231        if let Some(ref mut fun) = self.callbacks.borrow_mut().active_status_change {
232            fun(focus);
233        }
234    }
235}
236
237#[derive(Clone)]
238pub(crate) struct WaylandWindow(pub(crate) Rc<WaylandWindowState>);
239
240impl HasWindowHandle for WaylandWindow {
241    fn window_handle(&self) -> Result<WindowHandle<'_>, HandleError> {
242        unimplemented!()
243    }
244}
245
246impl HasDisplayHandle for WaylandWindow {
247    fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
248        unimplemented!()
249    }
250}
251
252impl PlatformWindow for WaylandWindow {
253    // todo(linux)
254    fn bounds(&self) -> WindowBounds {
255        WindowBounds::Maximized
256    }
257
258    fn content_size(&self) -> Size<Pixels> {
259        let inner = self.0.inner.borrow_mut();
260        Size {
261            width: Pixels(inner.bounds.size.width as f32),
262            height: Pixels(inner.bounds.size.height as f32),
263        }
264    }
265
266    fn scale_factor(&self) -> f32 {
267        self.0.inner.borrow_mut().scale
268    }
269
270    // todo(linux)
271    fn titlebar_height(&self) -> Pixels {
272        unimplemented!()
273    }
274
275    // todo(linux)
276    fn appearance(&self) -> WindowAppearance {
277        WindowAppearance::Light
278    }
279
280    // todo(linux)
281    fn display(&self) -> Rc<dyn PlatformDisplay> {
282        Rc::new(WaylandDisplay {})
283    }
284
285    // todo(linux)
286    fn mouse_position(&self) -> Point<Pixels> {
287        Point::default()
288    }
289
290    // todo(linux)
291    fn modifiers(&self) -> Modifiers {
292        crate::Modifiers::default()
293    }
294
295    // todo(linux)
296    fn as_any_mut(&mut self) -> &mut dyn Any {
297        unimplemented!()
298    }
299
300    fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
301        self.0.inner.borrow_mut().input_handler = Some(input_handler);
302    }
303
304    fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
305        self.0.inner.borrow_mut().input_handler.take()
306    }
307
308    // todo(linux)
309    fn prompt(
310        &self,
311        level: PromptLevel,
312        msg: &str,
313        detail: Option<&str>,
314        answers: &[&str],
315    ) -> Receiver<usize> {
316        unimplemented!()
317    }
318
319    fn activate(&self) {
320        // todo(linux)
321    }
322
323    fn set_title(&mut self, title: &str) {
324        self.0.toplevel.set_title(title.to_string());
325    }
326
327    fn set_edited(&mut self, edited: bool) {
328        // todo(linux)
329    }
330
331    fn show_character_palette(&self) {
332        // todo(linux)
333    }
334
335    fn minimize(&self) {
336        // todo(linux)
337    }
338
339    fn zoom(&self) {
340        // todo(linux)
341    }
342
343    fn toggle_full_screen(&self) {
344        // todo(linux)
345    }
346
347    fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
348        self.0.callbacks.borrow_mut().request_frame = Some(callback);
349    }
350
351    fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> bool>) {
352        self.0.callbacks.borrow_mut().input = Some(callback);
353    }
354
355    fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
356        self.0.callbacks.borrow_mut().active_status_change = Some(callback);
357    }
358
359    fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
360        self.0.callbacks.borrow_mut().resize = Some(callback);
361    }
362
363    fn on_fullscreen(&self, callback: Box<dyn FnMut(bool)>) {
364        // todo(linux)
365    }
366
367    fn on_moved(&self, callback: Box<dyn FnMut()>) {
368        self.0.callbacks.borrow_mut().moved = Some(callback);
369    }
370
371    fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>) {
372        self.0.callbacks.borrow_mut().should_close = Some(callback);
373    }
374
375    fn on_close(&self, callback: Box<dyn FnOnce()>) {
376        self.0.callbacks.borrow_mut().close = Some(callback);
377    }
378
379    fn on_appearance_changed(&self, callback: Box<dyn FnMut()>) {
380        // todo(linux)
381    }
382
383    // todo(linux)
384    fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool {
385        false
386    }
387
388    fn draw(&self, scene: &Scene) {
389        let mut inner = self.0.inner.borrow_mut();
390        inner.renderer.draw(scene);
391    }
392
393    fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
394        let inner = self.0.inner.borrow_mut();
395        inner.renderer.sprite_atlas().clone()
396    }
397}
398
399#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
400pub enum WaylandDecorationState {
401    /// Decorations are to be provided by the client
402    Client,
403
404    /// Decorations are provided by the server
405    Server,
406}