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