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 resize(&mut self, width: u16, height: u16) {
170        self.content_size = Size {
171            width: Pixels(width as f32),
172            height: Pixels(height as f32),
173        };
174        self.renderer.resize(blade::Extent {
175            width: width as u32,
176            height: height as u32,
177            depth: 1,
178        });
179        if let Some(ref mut fun) = self.callbacks.resize {
180            fun(self.content_size, 1.0);
181        }
182    }
183
184    pub fn request_frame(&mut self) {
185        if let Some(ref mut fun) = self.callbacks.request_frame {
186            fun();
187        }
188    }
189
190    pub fn destroy(&mut self) {
191        self.sprite_atlas.destroy();
192        self.renderer.destroy();
193    }
194}
195
196impl PlatformWindow for LinuxWindow {
197    fn bounds(&self) -> WindowBounds {
198        //TODO: update when window moves
199        self.0.lock().window_bounds
200    }
201
202    fn content_size(&self) -> Size<Pixels> {
203        self.0.lock().content_size
204    }
205
206    fn scale_factor(&self) -> f32 {
207        1.0
208    }
209
210    fn titlebar_height(&self) -> Pixels {
211        unimplemented!()
212    }
213
214    fn appearance(&self) -> WindowAppearance {
215        unimplemented!()
216    }
217
218    fn display(&self) -> Rc<dyn PlatformDisplay> {
219        Rc::clone(&self.0.lock().display)
220    }
221
222    fn mouse_position(&self) -> Point<Pixels> {
223        Point::default()
224    }
225
226    fn modifiers(&self) -> crate::Modifiers {
227        crate::Modifiers::default()
228    }
229
230    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
231        self
232    }
233
234    fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {}
235
236    fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
237        None
238    }
239
240    fn prompt(
241        &self,
242        _level: crate::PromptLevel,
243        _msg: &str,
244        _detail: Option<&str>,
245        _answers: &[&str],
246    ) -> futures::channel::oneshot::Receiver<usize> {
247        unimplemented!()
248    }
249
250    fn activate(&self) {}
251
252    fn set_title(&mut self, title: &str) {}
253
254    fn set_edited(&mut self, edited: bool) {}
255
256    fn show_character_palette(&self) {
257        unimplemented!()
258    }
259
260    fn minimize(&self) {
261        unimplemented!()
262    }
263
264    fn zoom(&self) {
265        unimplemented!()
266    }
267
268    fn toggle_full_screen(&self) {
269        unimplemented!()
270    }
271
272    fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
273        self.0.lock().callbacks.request_frame = Some(callback);
274    }
275
276    fn on_input(&self, callback: Box<dyn FnMut(crate::PlatformInput) -> bool>) {}
277
278    fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {}
279
280    fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
281        self.0.lock().callbacks.resize = Some(callback);
282    }
283
284    fn on_fullscreen(&self, _callback: Box<dyn FnMut(bool)>) {}
285
286    fn on_moved(&self, callback: Box<dyn FnMut()>) {}
287
288    fn on_should_close(&self, _callback: Box<dyn FnMut() -> bool>) {}
289
290    fn on_close(&self, _callback: Box<dyn FnOnce()>) {}
291
292    fn on_appearance_changed(&self, _callback: Box<dyn FnMut()>) {}
293
294    fn is_topmost_for_position(&self, _position: crate::Point<Pixels>) -> bool {
295        unimplemented!()
296    }
297
298    fn invalidate(&self) {}
299
300    fn draw(&self, scene: &crate::Scene) {
301        self.0.lock().renderer.draw(scene);
302    }
303
304    fn sprite_atlas(&self) -> sync::Arc<dyn crate::PlatformAtlas> {
305        self.0.lock().sprite_atlas.clone()
306    }
307}