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