window.rs

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