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