window.rs

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