window.rs

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