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