client.rs

  1use std::rc::Rc;
  2use std::sync::Arc;
  3
  4use parking_lot::Mutex;
  5use xcb::{x, Xid};
  6
  7use collections::HashMap;
  8
  9use crate::platform::linux::client::Client;
 10use crate::platform::{
 11    LinuxPlatformInner, PlatformWindow, X11Display, X11Window, X11WindowState, XcbAtoms,
 12};
 13use crate::{AnyWindowHandle, Bounds, DisplayId, PlatformDisplay, Point, Size, WindowOptions};
 14
 15pub(crate) struct X11ClientState {
 16    pub(crate) windows: HashMap<x::Window, Rc<X11WindowState>>,
 17}
 18
 19pub(crate) struct X11Client {
 20    platform_inner: Arc<LinuxPlatformInner>,
 21    xcb_connection: Arc<xcb::Connection>,
 22    x_root_index: i32,
 23    atoms: XcbAtoms,
 24    state: Mutex<X11ClientState>,
 25}
 26
 27impl X11Client {
 28    pub(crate) fn new(
 29        inner: Arc<LinuxPlatformInner>,
 30        xcb_connection: Arc<xcb::Connection>,
 31        x_root_index: i32,
 32        atoms: XcbAtoms,
 33    ) -> Self {
 34        Self {
 35            platform_inner: inner,
 36            xcb_connection,
 37            x_root_index,
 38            atoms,
 39            state: Mutex::new(X11ClientState {
 40                windows: HashMap::default(),
 41            }),
 42        }
 43    }
 44
 45    fn get_window(&self, win: x::Window) -> Rc<X11WindowState> {
 46        let state = self.state.lock();
 47        Rc::clone(&state.windows[&win])
 48    }
 49}
 50
 51impl Client for X11Client {
 52    fn run(&self, on_finish_launching: Box<dyn FnOnce()>) {
 53        on_finish_launching();
 54        //Note: here and below, don't keep the lock() open when calling
 55        // into window functions as they may invoke callbacks that need
 56        // to immediately access the platform (self).
 57        while !self.platform_inner.state.lock().quit_requested {
 58            let event = self.xcb_connection.wait_for_event().unwrap();
 59            match event {
 60                xcb::Event::X(x::Event::ClientMessage(ev)) => {
 61                    if let x::ClientMessageData::Data32([atom, ..]) = ev.data() {
 62                        if atom == self.atoms.wm_del_window.resource_id() {
 63                            // window "x" button clicked by user, we gracefully exit
 64                            let window = self.state.lock().windows.remove(&ev.window()).unwrap();
 65                            window.destroy();
 66                            let state = self.state.lock();
 67                            self.platform_inner.state.lock().quit_requested |=
 68                                state.windows.is_empty();
 69                        }
 70                    }
 71                }
 72                xcb::Event::X(x::Event::Expose(ev)) => {
 73                    self.get_window(ev.window()).refresh();
 74                }
 75                xcb::Event::X(x::Event::ConfigureNotify(ev)) => {
 76                    let bounds = Bounds {
 77                        origin: Point {
 78                            x: ev.x().into(),
 79                            y: ev.y().into(),
 80                        },
 81                        size: Size {
 82                            width: ev.width().into(),
 83                            height: ev.height().into(),
 84                        },
 85                    };
 86                    self.get_window(ev.window()).configure(bounds)
 87                }
 88                xcb::Event::Present(xcb::present::Event::CompleteNotify(ev)) => {
 89                    let window = self.get_window(ev.window());
 90                    window.refresh();
 91                    window.request_refresh();
 92                }
 93                xcb::Event::Present(xcb::present::Event::IdleNotify(_ev)) => {}
 94                _ => {}
 95            }
 96
 97            if let Ok(runnable) = self.platform_inner.main_receiver.try_recv() {
 98                runnable.run();
 99            }
100        }
101
102        if let Some(ref mut fun) = self.platform_inner.callbacks.lock().quit {
103            fun();
104        }
105    }
106    fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
107        let setup = self.xcb_connection.get_setup();
108        setup
109            .roots()
110            .enumerate()
111            .map(|(root_id, _)| {
112                Rc::new(X11Display::new(&self.xcb_connection, root_id as i32))
113                    as Rc<dyn PlatformDisplay>
114            })
115            .collect()
116    }
117    fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
118        Some(Rc::new(X11Display::new(&self.xcb_connection, id.0 as i32)))
119    }
120
121    fn open_window(
122        &self,
123        _handle: AnyWindowHandle,
124        options: WindowOptions,
125    ) -> Box<dyn PlatformWindow> {
126        let x_window = self.xcb_connection.generate_id();
127
128        let window_ptr = Rc::new(X11WindowState::new(
129            options,
130            &self.xcb_connection,
131            self.x_root_index,
132            x_window,
133            &self.atoms,
134        ));
135        window_ptr.request_refresh();
136
137        self.state
138            .lock()
139            .windows
140            .insert(x_window, Rc::clone(&window_ptr));
141        Box::new(X11Window(window_ptr))
142    }
143}