window.rs

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