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