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