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