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
 46impl Client for X11Client {
 47    fn run(&self, on_finish_launching: Box<dyn FnOnce()>) {
 48        on_finish_launching();
 49        //Note: here and below, don't keep the lock() open when calling
 50        // into window functions as they may invoke callbacks that need
 51        // to immediately access the platform (self).
 52        while !self.platform_inner.state.lock().quit_requested {
 53            let event = self.xcb_connection.wait_for_event().unwrap();
 54            match event {
 55                xcb::Event::X(x::Event::ClientMessage(ev)) => {
 56                    if let x::ClientMessageData::Data32([atom, ..]) = ev.data() {
 57                        if atom == self.atoms.wm_del_window.resource_id() {
 58                            // window "x" button clicked by user, we gracefully exit
 59                            let window = self.state.lock().windows.remove(&ev.window()).unwrap();
 60                            window.destroy();
 61                            let mut state = self.state.lock();
 62                            self.platform_inner.state.lock().quit_requested |=
 63                                state.windows.is_empty();
 64                        }
 65                    }
 66                }
 67                xcb::Event::X(x::Event::Expose(ev)) => {
 68                    let window = {
 69                        let state = self.state.lock();
 70                        Rc::clone(&state.windows[&ev.window()])
 71                    };
 72                    window.expose();
 73                }
 74                xcb::Event::X(x::Event::ConfigureNotify(ev)) => {
 75                    let bounds = Bounds {
 76                        origin: Point {
 77                            x: ev.x().into(),
 78                            y: ev.y().into(),
 79                        },
 80                        size: Size {
 81                            width: ev.width().into(),
 82                            height: ev.height().into(),
 83                        },
 84                    };
 85                    let window = {
 86                        let state = self.state.lock();
 87                        Rc::clone(&state.windows[&ev.window()])
 88                    };
 89                    window.configure(bounds)
 90                }
 91                _ => {}
 92            }
 93
 94            if let Ok(runnable) = self.platform_inner.main_receiver.try_recv() {
 95                runnable.run();
 96            }
 97        }
 98
 99        if let Some(ref mut fun) = self.platform_inner.callbacks.lock().quit {
100            fun();
101        }
102    }
103    fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
104        let setup = self.xcb_connection.get_setup();
105        setup
106            .roots()
107            .enumerate()
108            .map(|(root_id, _)| {
109                Rc::new(X11Display::new(&self.xcb_connection, root_id as i32))
110                    as Rc<dyn PlatformDisplay>
111            })
112            .collect()
113    }
114    fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
115        Some(Rc::new(X11Display::new(&self.xcb_connection, id.0 as i32)))
116    }
117
118    fn open_window(
119        &self,
120        handle: AnyWindowHandle,
121        options: WindowOptions,
122    ) -> Box<dyn PlatformWindow> {
123        let x_window = self.xcb_connection.generate_id();
124
125        let window_ptr = Rc::new(X11WindowState::new(
126            options,
127            &self.xcb_connection,
128            self.x_root_index,
129            x_window,
130            &self.atoms,
131        ));
132
133        self.state
134            .lock()
135            .windows
136            .insert(x_window, Rc::clone(&window_ptr));
137        Box::new(X11Window(window_ptr))
138    }
139}