window.rs

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