window.rs

  1use super::BladeRenderer;
  2use crate::{
  3    Bounds, GlobalPixels, LinuxDisplay, Pixels, PlatformDisplay, PlatformInputHandler,
  4    PlatformWindow, Point, Size, WindowAppearance, WindowBounds, WindowOptions, XcbAtoms,
  5};
  6use blade_graphics as gpu;
  7use parking_lot::Mutex;
  8use raw_window_handle as rwh;
  9use std::{
 10    ffi::c_void,
 11    mem,
 12    rc::Rc,
 13    sync::{self, Arc},
 14};
 15use xcb::{x, Xid as _};
 16
 17#[derive(Default)]
 18struct Callbacks {
 19    request_frame: Option<Box<dyn FnMut()>>,
 20    input: Option<Box<dyn FnMut(crate::PlatformInput) -> bool>>,
 21    active_status_change: Option<Box<dyn FnMut(bool)>>,
 22    resize: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
 23    fullscreen: Option<Box<dyn FnMut(bool)>>,
 24    moved: Option<Box<dyn FnMut()>>,
 25    should_close: Option<Box<dyn FnMut() -> bool>>,
 26    close: Option<Box<dyn FnOnce()>>,
 27    appearance_changed: Option<Box<dyn FnMut()>>,
 28}
 29
 30struct LinuxWindowInner {
 31    bounds: Bounds<i32>,
 32    title_height: i32,
 33    border_width: i32,
 34    scale_factor: f32,
 35    renderer: BladeRenderer,
 36}
 37
 38impl LinuxWindowInner {
 39    fn render_extent(&self) -> gpu::Extent {
 40        gpu::Extent {
 41            width: (self.bounds.size.width - 2 * self.border_width) as u32,
 42            height: (self.bounds.size.height - 2 * self.border_width - self.title_height) as u32,
 43            depth: 1,
 44        }
 45    }
 46    fn content_size(&self) -> Size<Pixels> {
 47        let extent = self.render_extent();
 48        Size {
 49            width: extent.width.into(),
 50            height: extent.height.into(),
 51        }
 52    }
 53}
 54
 55struct RawWindow {
 56    connection: *mut c_void,
 57    screen_id: i32,
 58    window_id: u32,
 59    visual_id: u32,
 60}
 61
 62pub(crate) struct LinuxWindowState {
 63    xcb_connection: Arc<xcb::Connection>,
 64    display: Rc<dyn PlatformDisplay>,
 65    raw: RawWindow,
 66    x_window: x::Window,
 67    callbacks: Mutex<Callbacks>,
 68    inner: Mutex<LinuxWindowInner>,
 69}
 70
 71#[derive(Clone)]
 72pub(crate) struct LinuxWindow(pub(crate) Arc<LinuxWindowState>);
 73
 74unsafe impl rwh::HasRawWindowHandle for RawWindow {
 75    fn raw_window_handle(&self) -> rwh::RawWindowHandle {
 76        let mut wh = rwh::XcbWindowHandle::empty();
 77        wh.window = self.window_id;
 78        wh.visual_id = self.visual_id;
 79        wh.into()
 80    }
 81}
 82unsafe impl rwh::HasRawDisplayHandle for RawWindow {
 83    fn raw_display_handle(&self) -> rwh::RawDisplayHandle {
 84        let mut dh = rwh::XcbDisplayHandle::empty();
 85        dh.connection = self.connection;
 86        dh.screen = self.screen_id;
 87        dh.into()
 88    }
 89}
 90
 91impl rwh::HasWindowHandle for LinuxWindow {
 92    fn window_handle(&self) -> Result<rwh::WindowHandle, rwh::HandleError> {
 93        Ok(unsafe {
 94            let raw_handle = rwh::HasRawWindowHandle::raw_window_handle(&self.0.raw);
 95            rwh::WindowHandle::borrow_raw(raw_handle, rwh::ActiveHandle::new())
 96        })
 97    }
 98}
 99impl rwh::HasDisplayHandle for LinuxWindow {
100    fn display_handle(&self) -> Result<rwh::DisplayHandle, rwh::HandleError> {
101        Ok(unsafe {
102            let raw_handle = rwh::HasRawDisplayHandle::raw_display_handle(&self.0.raw);
103            rwh::DisplayHandle::borrow_raw(raw_handle)
104        })
105    }
106}
107
108impl LinuxWindowState {
109    pub fn new(
110        options: WindowOptions,
111        xcb_connection: &Arc<xcb::Connection>,
112        x_main_screen_index: i32,
113        x_window: x::Window,
114        atoms: &XcbAtoms,
115    ) -> Self {
116        let x_screen_index = options
117            .display_id
118            .map_or(x_main_screen_index, |did| did.0 as i32);
119        let screen = xcb_connection
120            .get_setup()
121            .roots()
122            .nth(x_screen_index as usize)
123            .unwrap();
124
125        let xcb_values = [
126            x::Cw::BackPixel(screen.white_pixel()),
127            x::Cw::EventMask(
128                x::EventMask::EXPOSURE | x::EventMask::STRUCTURE_NOTIFY | x::EventMask::KEY_PRESS,
129            ),
130        ];
131
132        let bounds = match options.bounds {
133            WindowBounds::Fullscreen | WindowBounds::Maximized => Bounds {
134                origin: Point::default(),
135                size: Size {
136                    width: screen.width_in_pixels() as i32,
137                    height: screen.height_in_pixels() as i32,
138                },
139            },
140            WindowBounds::Fixed(bounds) => bounds.map(|p| p.0 as i32),
141        };
142        let border_width = 0i32;
143
144        xcb_connection.send_request(&x::CreateWindow {
145            depth: x::COPY_FROM_PARENT as u8,
146            wid: x_window,
147            parent: screen.root(),
148            x: bounds.origin.x as i16,
149            y: bounds.origin.y as i16,
150            width: bounds.size.width as u16,
151            height: bounds.size.height as u16,
152            border_width: border_width as u16,
153            class: x::WindowClass::InputOutput,
154            visual: screen.root_visual(),
155            value_list: &xcb_values,
156        });
157
158        if let Some(titlebar) = options.titlebar {
159            if let Some(title) = titlebar.title {
160                xcb_connection.send_request(&x::ChangeProperty {
161                    mode: x::PropMode::Replace,
162                    window: x_window,
163                    property: x::ATOM_WM_NAME,
164                    r#type: x::ATOM_STRING,
165                    data: title.as_bytes(),
166                });
167            }
168        }
169        xcb_connection
170            .send_and_check_request(&x::ChangeProperty {
171                mode: x::PropMode::Replace,
172                window: x_window,
173                property: atoms.wm_protocols,
174                r#type: x::ATOM_ATOM,
175                data: &[atoms.wm_del_window],
176            })
177            .unwrap();
178
179        xcb_connection.send_request(&x::MapWindow { window: x_window });
180        xcb_connection.flush().unwrap();
181
182        let raw = RawWindow {
183            connection: as_raw_xcb_connection::AsRawXcbConnection::as_raw_xcb_connection(
184                xcb_connection,
185            ) as *mut _,
186            screen_id: x_screen_index,
187            window_id: x_window.resource_id(),
188            visual_id: screen.root_visual(),
189        };
190        let gpu = Arc::new(
191            unsafe {
192                gpu::Context::init_windowed(
193                    &raw,
194                    gpu::ContextDesc {
195                        validation: cfg!(debug_assertions),
196                        capture: false,
197                    },
198                )
199            }
200            .unwrap(),
201        );
202
203        let gpu_extent = gpu::Extent {
204            width: bounds.size.width as u32,
205            height: bounds.size.height as u32,
206            depth: 1,
207        };
208
209        Self {
210            xcb_connection: Arc::clone(xcb_connection),
211            display: Rc::new(LinuxDisplay::new(xcb_connection, x_screen_index)),
212            raw,
213            x_window,
214            callbacks: Mutex::new(Callbacks::default()),
215            inner: Mutex::new(LinuxWindowInner {
216                bounds,
217                title_height: 0, //TODO
218                border_width,
219                scale_factor: 1.0,
220                renderer: BladeRenderer::new(gpu, gpu_extent),
221            }),
222        }
223    }
224
225    pub fn destroy(&self) {
226        self.inner.lock().renderer.destroy();
227        self.xcb_connection.send_request(&x::UnmapWindow {
228            window: self.x_window,
229        });
230        self.xcb_connection.send_request(&x::DestroyWindow {
231            window: self.x_window,
232        });
233        if let Some(fun) = self.callbacks.lock().close.take() {
234            fun();
235        }
236    }
237
238    pub fn expose(&self) {
239        let mut cb = self.callbacks.lock();
240        if let Some(ref mut fun) = cb.request_frame {
241            fun();
242        }
243    }
244
245    pub fn configure(&self, bounds: Bounds<i32>) {
246        let mut resize_args = None;
247        let mut do_move = false;
248        {
249            let mut inner = self.inner.lock();
250            let old_bounds = mem::replace(&mut inner.bounds, bounds);
251            do_move = old_bounds.origin != bounds.origin;
252            if old_bounds.size != bounds.size {
253                let extent = inner.render_extent();
254                inner.renderer.resize(extent);
255                resize_args = Some((inner.content_size(), inner.scale_factor));
256            }
257        }
258
259        let mut callbacks = self.callbacks.lock();
260        if let Some((content_size, scale_factor)) = resize_args {
261            if let Some(ref mut fun) = callbacks.resize {
262                fun(content_size, scale_factor)
263            }
264        }
265        if do_move {
266            if let Some(ref mut fun) = callbacks.moved {
267                fun()
268            }
269        }
270    }
271}
272
273impl PlatformWindow for LinuxWindow {
274    fn bounds(&self) -> WindowBounds {
275        WindowBounds::Fixed(self.0.inner.lock().bounds.map(|v| GlobalPixels(v as f32)))
276    }
277
278    fn content_size(&self) -> Size<Pixels> {
279        self.0.inner.lock().content_size()
280    }
281
282    fn scale_factor(&self) -> f32 {
283        self.0.inner.lock().scale_factor
284    }
285
286    fn titlebar_height(&self) -> Pixels {
287        unimplemented!()
288    }
289
290    fn appearance(&self) -> WindowAppearance {
291        unimplemented!()
292    }
293
294    fn display(&self) -> Rc<dyn PlatformDisplay> {
295        Rc::clone(&self.0.display)
296    }
297
298    fn mouse_position(&self) -> Point<Pixels> {
299        Point::default()
300    }
301
302    fn modifiers(&self) -> crate::Modifiers {
303        crate::Modifiers::default()
304    }
305
306    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
307        self
308    }
309
310    fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {}
311
312    fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
313        None
314    }
315
316    fn prompt(
317        &self,
318        _level: crate::PromptLevel,
319        _msg: &str,
320        _detail: Option<&str>,
321        _answers: &[&str],
322    ) -> futures::channel::oneshot::Receiver<usize> {
323        unimplemented!()
324    }
325
326    fn activate(&self) {}
327
328    fn set_title(&mut self, title: &str) {}
329
330    fn set_edited(&mut self, edited: bool) {}
331
332    fn show_character_palette(&self) {
333        unimplemented!()
334    }
335
336    fn minimize(&self) {
337        unimplemented!()
338    }
339
340    fn zoom(&self) {
341        unimplemented!()
342    }
343
344    fn toggle_full_screen(&self) {
345        unimplemented!()
346    }
347
348    fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
349        self.0.callbacks.lock().request_frame = Some(callback);
350    }
351
352    fn on_input(&self, callback: Box<dyn FnMut(crate::PlatformInput) -> bool>) {
353        self.0.callbacks.lock().input = Some(callback);
354    }
355
356    fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
357        self.0.callbacks.lock().active_status_change = Some(callback);
358    }
359
360    fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
361        self.0.callbacks.lock().resize = Some(callback);
362    }
363
364    fn on_fullscreen(&self, callback: Box<dyn FnMut(bool)>) {
365        self.0.callbacks.lock().fullscreen = Some(callback);
366    }
367
368    fn on_moved(&self, callback: Box<dyn FnMut()>) {
369        self.0.callbacks.lock().moved = Some(callback);
370    }
371
372    fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>) {
373        self.0.callbacks.lock().should_close = Some(callback);
374    }
375
376    fn on_close(&self, callback: Box<dyn FnOnce()>) {
377        self.0.callbacks.lock().close = Some(callback);
378    }
379
380    fn on_appearance_changed(&self, callback: Box<dyn FnMut()>) {
381        self.0.callbacks.lock().appearance_changed = Some(callback);
382    }
383
384    fn is_topmost_for_position(&self, _position: crate::Point<Pixels>) -> bool {
385        unimplemented!()
386    }
387
388    fn invalidate(&self) {}
389
390    fn draw(&self, scene: &crate::Scene) {
391        let mut inner = self.0.inner.lock();
392        inner.renderer.draw(scene);
393    }
394
395    fn sprite_atlas(&self) -> sync::Arc<dyn crate::PlatformAtlas> {
396        let mut inner = self.0.inner.lock();
397        inner.renderer.atlas().clone()
398    }
399}