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