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