window.rs

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