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