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