window.rs

  1use super::BladeRenderer;
  2use crate::{
  3    Bounds, GlobalPixels, LinuxDisplay, Pixels, PlatformDisplay, PlatformInputHandler,
  4    PlatformWindow, Point, Size, WindowAppearance, WindowBounds, WindowOptions, XcbAtoms,
  5};
  6use blade_graphics as gpu;
  7use parking_lot::Mutex;
  8use raw_window_handle as rwh;
  9use std::{
 10    ffi::c_void,
 11    mem,
 12    num::NonZeroU32,
 13    ptr::NonNull,
 14    rc::Rc,
 15    sync::{self, Arc},
 16};
 17use xcb::{x, Xid as _};
 18
 19#[derive(Default)]
 20struct Callbacks {
 21    request_frame: Option<Box<dyn FnMut()>>,
 22    input: Option<Box<dyn FnMut(crate::PlatformInput) -> bool>>,
 23    active_status_change: Option<Box<dyn FnMut(bool)>>,
 24    resize: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
 25    fullscreen: Option<Box<dyn FnMut(bool)>>,
 26    moved: Option<Box<dyn FnMut()>>,
 27    should_close: Option<Box<dyn FnMut() -> bool>>,
 28    close: Option<Box<dyn FnOnce()>>,
 29    appearance_changed: Option<Box<dyn FnMut()>>,
 30}
 31
 32struct LinuxWindowInner {
 33    bounds: Bounds<i32>,
 34    title_height: i32,
 35    border_width: i32,
 36    scale_factor: f32,
 37    renderer: BladeRenderer,
 38}
 39
 40impl LinuxWindowInner {
 41    fn render_extent(&self) -> gpu::Extent {
 42        gpu::Extent {
 43            width: (self.bounds.size.width - 2 * self.border_width) as u32,
 44            height: (self.bounds.size.height - 2 * self.border_width - self.title_height) as u32,
 45            depth: 1,
 46        }
 47    }
 48    fn content_size(&self) -> Size<Pixels> {
 49        let extent = self.render_extent();
 50        Size {
 51            width: extent.width.into(),
 52            height: extent.height.into(),
 53        }
 54    }
 55}
 56
 57struct RawWindow {
 58    connection: *mut c_void,
 59    screen_id: i32,
 60    window_id: u32,
 61    visual_id: u32,
 62}
 63
 64pub(crate) struct LinuxWindowState {
 65    xcb_connection: Arc<xcb::Connection>,
 66    display: Rc<dyn PlatformDisplay>,
 67    raw: RawWindow,
 68    x_window: x::Window,
 69    callbacks: Mutex<Callbacks>,
 70    inner: Mutex<LinuxWindowInner>,
 71}
 72
 73#[derive(Clone)]
 74pub(crate) struct LinuxWindow(pub(crate) Arc<LinuxWindowState>);
 75
 76unsafe impl blade_rwh::HasRawWindowHandle for RawWindow {
 77    fn raw_window_handle(&self) -> blade_rwh::RawWindowHandle {
 78        let mut wh = blade_rwh::XcbWindowHandle::empty();
 79        wh.window = self.window_id;
 80        wh.visual_id = self.visual_id;
 81        wh.into()
 82    }
 83}
 84unsafe impl blade_rwh::HasRawDisplayHandle for RawWindow {
 85    fn raw_display_handle(&self) -> blade_rwh::RawDisplayHandle {
 86        let mut dh = blade_rwh::XcbDisplayHandle::empty();
 87        dh.connection = self.connection;
 88        dh.screen = self.screen_id;
 89        dh.into()
 90    }
 91}
 92
 93impl rwh::HasWindowHandle for LinuxWindow {
 94    fn window_handle(&self) -> Result<rwh::WindowHandle, rwh::HandleError> {
 95        Ok(unsafe {
 96            let non_zero = NonZeroU32::new(self.0.raw.window_id).unwrap();
 97            let handle = rwh::XcbWindowHandle::new(non_zero);
 98            rwh::WindowHandle::borrow_raw(handle.into())
 99        })
100    }
101}
102impl rwh::HasDisplayHandle for LinuxWindow {
103    fn display_handle(&self) -> Result<rwh::DisplayHandle, rwh::HandleError> {
104        Ok(unsafe {
105            let non_zero = NonNull::new(self.0.raw.connection).unwrap();
106            let handle = rwh::XcbDisplayHandle::new(Some(non_zero), self.0.raw.screen_id);
107            rwh::DisplayHandle::borrow_raw(handle.into())
108        })
109    }
110}
111
112impl LinuxWindowState {
113    pub fn new(
114        options: WindowOptions,
115        xcb_connection: &Arc<xcb::Connection>,
116        x_main_screen_index: i32,
117        x_window: x::Window,
118        atoms: &XcbAtoms,
119    ) -> Self {
120        let x_screen_index = options
121            .display_id
122            .map_or(x_main_screen_index, |did| did.0 as i32);
123        let screen = xcb_connection
124            .get_setup()
125            .roots()
126            .nth(x_screen_index as usize)
127            .unwrap();
128
129        let xcb_values = [
130            x::Cw::BackPixel(screen.white_pixel()),
131            x::Cw::EventMask(
132                x::EventMask::EXPOSURE | x::EventMask::STRUCTURE_NOTIFY | x::EventMask::KEY_PRESS,
133            ),
134        ];
135
136        let bounds = match options.bounds {
137            WindowBounds::Fullscreen | WindowBounds::Maximized => Bounds {
138                origin: Point::default(),
139                size: Size {
140                    width: screen.width_in_pixels() as i32,
141                    height: screen.height_in_pixels() as i32,
142                },
143            },
144            WindowBounds::Fixed(bounds) => bounds.map(|p| p.0 as i32),
145        };
146        let border_width = 0i32;
147
148        xcb_connection.send_request(&x::CreateWindow {
149            depth: x::COPY_FROM_PARENT as u8,
150            wid: x_window,
151            parent: screen.root(),
152            x: bounds.origin.x as i16,
153            y: bounds.origin.y as i16,
154            width: bounds.size.width as u16,
155            height: bounds.size.height as u16,
156            border_width: border_width as u16,
157            class: x::WindowClass::InputOutput,
158            visual: screen.root_visual(),
159            value_list: &xcb_values,
160        });
161
162        if let Some(titlebar) = options.titlebar {
163            if let Some(title) = titlebar.title {
164                xcb_connection.send_request(&x::ChangeProperty {
165                    mode: x::PropMode::Replace,
166                    window: x_window,
167                    property: x::ATOM_WM_NAME,
168                    r#type: x::ATOM_STRING,
169                    data: title.as_bytes(),
170                });
171            }
172        }
173        xcb_connection
174            .send_and_check_request(&x::ChangeProperty {
175                mode: x::PropMode::Replace,
176                window: x_window,
177                property: atoms.wm_protocols,
178                r#type: x::ATOM_ATOM,
179                data: &[atoms.wm_del_window],
180            })
181            .unwrap();
182
183        xcb_connection.send_request(&x::MapWindow { window: x_window });
184        xcb_connection.flush().unwrap();
185
186        let raw = RawWindow {
187            connection: as_raw_xcb_connection::AsRawXcbConnection::as_raw_xcb_connection(
188                xcb_connection,
189            ) as *mut _,
190            screen_id: x_screen_index,
191            window_id: x_window.resource_id(),
192            visual_id: screen.root_visual(),
193        };
194        let gpu = Arc::new(
195            unsafe {
196                gpu::Context::init_windowed(
197                    &raw,
198                    gpu::ContextDesc {
199                        validation: cfg!(debug_assertions),
200                        capture: false,
201                    },
202                )
203            }
204            .unwrap(),
205        );
206
207        let gpu_extent = gpu::Extent {
208            width: bounds.size.width as u32,
209            height: bounds.size.height as u32,
210            depth: 1,
211        };
212
213        Self {
214            xcb_connection: Arc::clone(xcb_connection),
215            display: Rc::new(LinuxDisplay::new(xcb_connection, x_screen_index)),
216            raw,
217            x_window,
218            callbacks: Mutex::new(Callbacks::default()),
219            inner: Mutex::new(LinuxWindowInner {
220                bounds,
221                title_height: 0, //TODO
222                border_width,
223                scale_factor: 1.0,
224                renderer: BladeRenderer::new(gpu, gpu_extent),
225            }),
226        }
227    }
228
229    pub fn destroy(&self) {
230        self.inner.lock().renderer.destroy();
231        self.xcb_connection.send_request(&x::UnmapWindow {
232            window: self.x_window,
233        });
234        self.xcb_connection.send_request(&x::DestroyWindow {
235            window: self.x_window,
236        });
237        if let Some(fun) = self.callbacks.lock().close.take() {
238            fun();
239        }
240        self.xcb_connection.flush().unwrap();
241    }
242
243    pub fn expose(&self) {
244        let mut cb = self.callbacks.lock();
245        if let Some(ref mut fun) = cb.request_frame {
246            fun();
247        }
248    }
249
250    pub fn configure(&self, bounds: Bounds<i32>) {
251        let mut resize_args = None;
252        let do_move;
253        {
254            let mut inner = self.inner.lock();
255            let old_bounds = mem::replace(&mut inner.bounds, bounds);
256            do_move = old_bounds.origin != bounds.origin;
257            if old_bounds.size != bounds.size {
258                let extent = inner.render_extent();
259                inner.renderer.resize(extent);
260                resize_args = Some((inner.content_size(), inner.scale_factor));
261            }
262        }
263
264        let mut callbacks = self.callbacks.lock();
265        if let Some((content_size, scale_factor)) = resize_args {
266            if let Some(ref mut fun) = callbacks.resize {
267                fun(content_size, scale_factor)
268            }
269        }
270        if do_move {
271            if let Some(ref mut fun) = callbacks.moved {
272                fun()
273            }
274        }
275    }
276}
277
278impl PlatformWindow for LinuxWindow {
279    fn bounds(&self) -> WindowBounds {
280        WindowBounds::Fixed(self.0.inner.lock().bounds.map(|v| GlobalPixels(v as f32)))
281    }
282
283    fn content_size(&self) -> Size<Pixels> {
284        self.0.inner.lock().content_size()
285    }
286
287    fn scale_factor(&self) -> f32 {
288        self.0.inner.lock().scale_factor
289    }
290
291    fn titlebar_height(&self) -> Pixels {
292        unimplemented!()
293    }
294
295    fn appearance(&self) -> WindowAppearance {
296        unimplemented!()
297    }
298
299    fn display(&self) -> Rc<dyn PlatformDisplay> {
300        Rc::clone(&self.0.display)
301    }
302
303    fn mouse_position(&self) -> Point<Pixels> {
304        Point::default()
305    }
306
307    fn modifiers(&self) -> crate::Modifiers {
308        crate::Modifiers::default()
309    }
310
311    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
312        self
313    }
314
315    fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {}
316
317    fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
318        None
319    }
320
321    fn prompt(
322        &self,
323        _level: crate::PromptLevel,
324        _msg: &str,
325        _detail: Option<&str>,
326        _answers: &[&str],
327    ) -> futures::channel::oneshot::Receiver<usize> {
328        unimplemented!()
329    }
330
331    fn activate(&self) {}
332
333    fn set_title(&mut self, title: &str) {}
334
335    fn set_edited(&mut self, edited: bool) {}
336
337    fn show_character_palette(&self) {
338        unimplemented!()
339    }
340
341    fn minimize(&self) {
342        unimplemented!()
343    }
344
345    fn zoom(&self) {
346        unimplemented!()
347    }
348
349    fn toggle_full_screen(&self) {
350        unimplemented!()
351    }
352
353    fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
354        self.0.callbacks.lock().request_frame = Some(callback);
355    }
356
357    fn on_input(&self, callback: Box<dyn FnMut(crate::PlatformInput) -> bool>) {
358        self.0.callbacks.lock().input = Some(callback);
359    }
360
361    fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
362        self.0.callbacks.lock().active_status_change = Some(callback);
363    }
364
365    fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
366        self.0.callbacks.lock().resize = Some(callback);
367    }
368
369    fn on_fullscreen(&self, callback: Box<dyn FnMut(bool)>) {
370        self.0.callbacks.lock().fullscreen = Some(callback);
371    }
372
373    fn on_moved(&self, callback: Box<dyn FnMut()>) {
374        self.0.callbacks.lock().moved = Some(callback);
375    }
376
377    fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>) {
378        self.0.callbacks.lock().should_close = Some(callback);
379    }
380
381    fn on_close(&self, callback: Box<dyn FnOnce()>) {
382        self.0.callbacks.lock().close = Some(callback);
383    }
384
385    fn on_appearance_changed(&self, callback: Box<dyn FnMut()>) {
386        self.0.callbacks.lock().appearance_changed = Some(callback);
387    }
388
389    fn is_topmost_for_position(&self, _position: crate::Point<Pixels>) -> bool {
390        unimplemented!()
391    }
392
393    fn invalidate(&self) {}
394
395    fn draw(&self, scene: &crate::Scene) {
396        let mut inner = self.0.inner.lock();
397        inner.renderer.draw(scene);
398    }
399
400    fn sprite_atlas(&self) -> sync::Arc<dyn crate::PlatformAtlas> {
401        let inner = self.0.inner.lock();
402        inner.renderer.atlas().clone()
403    }
404}