window.rs

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