window.rs

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