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                    },
241                )
242            }
243            .unwrap(),
244        );
245
246        // Note: this has to be done after the GPU init, or otherwise
247        // the sizes are immediately invalidated.
248        let gpu_extent = query_render_extent(xcb_connection, x_window);
249
250        Self {
251            xcb_connection: xcb_connection.clone(),
252            display: Rc::new(X11Display::new(xcb_connection, x_screen_index)),
253            raw,
254            x_window,
255            callbacks: RefCell::new(Callbacks::default()),
256            inner: RefCell::new(LinuxWindowInner {
257                bounds,
258                scale_factor: 1.0,
259                renderer: BladeRenderer::new(gpu, gpu_extent),
260                input_handler: None,
261            }),
262        }
263    }
264
265    pub fn destroy(&self) {
266        self.inner.borrow_mut().renderer.destroy();
267        self.xcb_connection.send_request(&x::UnmapWindow {
268            window: self.x_window,
269        });
270        self.xcb_connection.send_request(&x::DestroyWindow {
271            window: self.x_window,
272        });
273        if let Some(fun) = self.callbacks.borrow_mut().close.take() {
274            fun();
275        }
276        self.xcb_connection.flush().unwrap();
277    }
278
279    pub fn refresh(&self) {
280        let mut cb = self.callbacks.borrow_mut();
281        if let Some(ref mut fun) = cb.request_frame {
282            fun();
283        }
284    }
285
286    pub fn configure(&self, bounds: Bounds<i32>) {
287        let mut resize_args = None;
288        let do_move;
289        {
290            let mut inner = self.inner.borrow_mut();
291            let old_bounds = mem::replace(&mut inner.bounds, bounds);
292            do_move = old_bounds.origin != bounds.origin;
293            // todo(linux): use normal GPUI types here, refactor out the double
294            // viewport check and extra casts ( )
295            let gpu_size = query_render_extent(&self.xcb_connection, self.x_window);
296            if inner.renderer.viewport_size() != gpu_size {
297                inner
298                    .renderer
299                    .update_drawable_size(size(gpu_size.width as f64, gpu_size.height as f64));
300                resize_args = Some((inner.content_size(), inner.scale_factor));
301            }
302        }
303
304        let mut callbacks = self.callbacks.borrow_mut();
305        if let Some((content_size, scale_factor)) = resize_args {
306            if let Some(ref mut fun) = callbacks.resize {
307                fun(content_size, scale_factor)
308            }
309        }
310        if do_move {
311            if let Some(ref mut fun) = callbacks.moved {
312                fun()
313            }
314        }
315    }
316
317    pub fn handle_input(&self, input: PlatformInput) {
318        if let Some(ref mut fun) = self.callbacks.borrow_mut().input {
319            if fun(input.clone()) {
320                return;
321            }
322        }
323        if let PlatformInput::KeyDown(event) = input {
324            let mut inner = self.inner.borrow_mut();
325            if let Some(ref mut input_handler) = inner.input_handler {
326                if let Some(ime_key) = &event.keystroke.ime_key {
327                    input_handler.replace_text_in_range(None, ime_key);
328                }
329            }
330        }
331    }
332
333    pub fn set_focused(&self, focus: bool) {
334        if let Some(ref mut fun) = self.callbacks.borrow_mut().active_status_change {
335            fun(focus);
336        }
337    }
338}
339
340impl PlatformWindow for X11Window {
341    fn bounds(&self) -> WindowBounds {
342        WindowBounds::Fixed(
343            self.0
344                .inner
345                .borrow_mut()
346                .bounds
347                .map(|v| GlobalPixels(v as f32)),
348        )
349    }
350
351    fn content_size(&self) -> Size<Pixels> {
352        self.0.inner.borrow_mut().content_size()
353    }
354
355    fn scale_factor(&self) -> f32 {
356        self.0.inner.borrow_mut().scale_factor
357    }
358
359    // todo(linux)
360    fn titlebar_height(&self) -> Pixels {
361        unimplemented!()
362    }
363
364    // todo(linux)
365    fn appearance(&self) -> WindowAppearance {
366        WindowAppearance::Light
367    }
368
369    fn display(&self) -> Rc<dyn PlatformDisplay> {
370        Rc::clone(&self.0.display)
371    }
372
373    fn mouse_position(&self) -> Point<Pixels> {
374        let cookie = self.0.xcb_connection.send_request(&x::QueryPointer {
375            window: self.0.x_window,
376        });
377        let reply: x::QueryPointerReply = self.0.xcb_connection.wait_for_reply(cookie).unwrap();
378        Point::new(
379            (reply.root_x() as u32).into(),
380            (reply.root_y() as u32).into(),
381        )
382    }
383
384    // todo(linux)
385    fn modifiers(&self) -> Modifiers {
386        Modifiers::default()
387    }
388
389    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
390        self
391    }
392
393    fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
394        self.0.inner.borrow_mut().input_handler = Some(input_handler);
395    }
396
397    fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
398        self.0.inner.borrow_mut().input_handler.take()
399    }
400
401    // todo(linux)
402    fn prompt(
403        &self,
404        _level: PromptLevel,
405        _msg: &str,
406        _detail: Option<&str>,
407        _answers: &[&str],
408    ) -> futures::channel::oneshot::Receiver<usize> {
409        unimplemented!()
410    }
411
412    fn activate(&self) {
413        self.0.xcb_connection.send_request(&x::ConfigureWindow {
414            window: self.0.x_window,
415            value_list: &[x::ConfigWindow::StackMode(x::StackMode::Above)],
416        });
417    }
418
419    fn set_title(&mut self, title: &str) {
420        self.0.xcb_connection.send_request(&x::ChangeProperty {
421            mode: x::PropMode::Replace,
422            window: self.0.x_window,
423            property: x::ATOM_WM_NAME,
424            r#type: x::ATOM_STRING,
425            data: title.as_bytes(),
426        });
427    }
428
429    // todo(linux)
430    fn set_edited(&mut self, edited: bool) {}
431
432    // todo(linux), this corresponds to `orderFrontCharacterPalette` on macOS,
433    // but it looks like the equivalent for Linux is GTK specific:
434    //
435    // https://docs.gtk.org/gtk3/signal.Entry.insert-emoji.html
436    //
437    // This API might need to change, or we might need to build an emoji picker into GPUI
438    fn show_character_palette(&self) {
439        unimplemented!()
440    }
441
442    // todo(linux)
443    fn minimize(&self) {
444        unimplemented!()
445    }
446
447    // todo(linux)
448    fn zoom(&self) {
449        unimplemented!()
450    }
451
452    // todo(linux)
453    fn toggle_full_screen(&self) {
454        unimplemented!()
455    }
456
457    fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
458        self.0.callbacks.borrow_mut().request_frame = Some(callback);
459    }
460
461    fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> bool>) {
462        self.0.callbacks.borrow_mut().input = Some(callback);
463    }
464
465    fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
466        self.0.callbacks.borrow_mut().active_status_change = Some(callback);
467    }
468
469    fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
470        self.0.callbacks.borrow_mut().resize = Some(callback);
471    }
472
473    fn on_fullscreen(&self, callback: Box<dyn FnMut(bool)>) {
474        self.0.callbacks.borrow_mut().fullscreen = Some(callback);
475    }
476
477    fn on_moved(&self, callback: Box<dyn FnMut()>) {
478        self.0.callbacks.borrow_mut().moved = Some(callback);
479    }
480
481    fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>) {
482        self.0.callbacks.borrow_mut().should_close = Some(callback);
483    }
484
485    fn on_close(&self, callback: Box<dyn FnOnce()>) {
486        self.0.callbacks.borrow_mut().close = Some(callback);
487    }
488
489    fn on_appearance_changed(&self, callback: Box<dyn FnMut()>) {
490        self.0.callbacks.borrow_mut().appearance_changed = Some(callback);
491    }
492
493    // todo(linux)
494    fn is_topmost_for_position(&self, _position: Point<Pixels>) -> bool {
495        unimplemented!()
496    }
497
498    fn draw(&self, scene: &Scene) {
499        let mut inner = self.0.inner.borrow_mut();
500        inner.renderer.draw(scene);
501    }
502
503    fn sprite_atlas(&self) -> sync::Arc<dyn PlatformAtlas> {
504        let inner = self.0.inner.borrow_mut();
505        inner.renderer.sprite_atlas().clone()
506    }
507}