window.rs

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