client.rs

  1use std::{rc::Rc, sync::Arc};
  2
  3use parking_lot::Mutex;
  4use xcb::{x, Xid as _};
  5use xkbcommon::xkb;
  6
  7use collections::HashMap;
  8
  9use crate::platform::linux::client::Client;
 10use crate::platform::{
 11    LinuxPlatformInner, PlatformWindow, X11Display, X11Window, X11WindowState, XcbAtoms,
 12};
 13use crate::{
 14    AnyWindowHandle, Bounds, DisplayId, PlatformDisplay, PlatformInput, Point, Size, WindowOptions,
 15};
 16
 17pub(crate) struct X11ClientState {
 18    pub(crate) windows: HashMap<x::Window, Rc<X11WindowState>>,
 19    xkb: xkbcommon::xkb::State,
 20}
 21
 22pub(crate) struct X11Client {
 23    platform_inner: Rc<LinuxPlatformInner>,
 24    xcb_connection: Arc<xcb::Connection>,
 25    x_root_index: i32,
 26    atoms: XcbAtoms,
 27    state: Mutex<X11ClientState>,
 28}
 29
 30impl X11Client {
 31    pub(crate) fn new(
 32        inner: Rc<LinuxPlatformInner>,
 33        xcb_connection: Arc<xcb::Connection>,
 34        x_root_index: i32,
 35        atoms: XcbAtoms,
 36    ) -> Self {
 37        let xkb_context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
 38        let xkb_device_id = xkb::x11::get_core_keyboard_device_id(&xcb_connection);
 39        let xkb_keymap = xkb::x11::keymap_new_from_device(
 40            &xkb_context,
 41            &xcb_connection,
 42            xkb_device_id,
 43            xkb::KEYMAP_COMPILE_NO_FLAGS,
 44        );
 45        let xkb_state =
 46            xkb::x11::state_new_from_device(&xkb_keymap, &xcb_connection, xkb_device_id);
 47
 48        Self {
 49            platform_inner: inner,
 50            xcb_connection,
 51            x_root_index,
 52            atoms,
 53            state: Mutex::new(X11ClientState {
 54                windows: HashMap::default(),
 55                xkb: xkb_state,
 56            }),
 57        }
 58    }
 59
 60    fn get_window(&self, win: x::Window) -> Rc<X11WindowState> {
 61        let state = self.state.lock();
 62        Rc::clone(&state.windows[&win])
 63    }
 64}
 65
 66impl Client for X11Client {
 67    fn run(&self, on_finish_launching: Box<dyn FnOnce()>) {
 68        on_finish_launching();
 69        //Note: here and below, don't keep the lock() open when calling
 70        // into window functions as they may invoke callbacks that need
 71        // to immediately access the platform (self).
 72        while !self.platform_inner.state.lock().quit_requested {
 73            let event = self.xcb_connection.wait_for_event().unwrap();
 74            match event {
 75                xcb::Event::X(x::Event::ClientMessage(ev)) => {
 76                    if let x::ClientMessageData::Data32([atom, ..]) = ev.data() {
 77                        if atom == self.atoms.wm_del_window.resource_id() {
 78                            // window "x" button clicked by user, we gracefully exit
 79                            let window = self.state.lock().windows.remove(&ev.window()).unwrap();
 80                            window.destroy();
 81                            let state = self.state.lock();
 82                            self.platform_inner.state.lock().quit_requested |=
 83                                state.windows.is_empty();
 84                        }
 85                    }
 86                }
 87                xcb::Event::X(x::Event::Expose(ev)) => {
 88                    self.get_window(ev.window()).refresh();
 89                }
 90                xcb::Event::X(x::Event::ConfigureNotify(ev)) => {
 91                    let bounds = Bounds {
 92                        origin: Point {
 93                            x: ev.x().into(),
 94                            y: ev.y().into(),
 95                        },
 96                        size: Size {
 97                            width: ev.width().into(),
 98                            height: ev.height().into(),
 99                        },
100                    };
101                    self.get_window(ev.window()).configure(bounds)
102                }
103                xcb::Event::Present(xcb::present::Event::CompleteNotify(ev)) => {
104                    let window = self.get_window(ev.window());
105                    window.refresh();
106                    window.request_refresh();
107                }
108                xcb::Event::Present(xcb::present::Event::IdleNotify(_ev)) => {}
109                xcb::Event::X(x::Event::KeyPress(ev)) => {
110                    let window = self.get_window(ev.event());
111                    let modifiers = super::modifiers_from_state(ev.state());
112                    let key = {
113                        let code = ev.detail().into();
114                        let mut state = self.state.lock();
115                        let key = state.xkb.key_get_utf8(code);
116                        state.xkb.update_key(code, xkb::KeyDirection::Down);
117                        key
118                    };
119                    window.handle_input(PlatformInput::KeyDown(crate::KeyDownEvent {
120                        keystroke: crate::Keystroke {
121                            modifiers,
122                            key,
123                            ime_key: None,
124                        },
125                        is_held: false,
126                    }));
127                }
128                xcb::Event::X(x::Event::KeyRelease(ev)) => {
129                    let window = self.get_window(ev.event());
130                    let modifiers = super::modifiers_from_state(ev.state());
131                    let key = {
132                        let code = ev.detail().into();
133                        let mut state = self.state.lock();
134                        let key = state.xkb.key_get_utf8(code);
135                        state.xkb.update_key(code, xkb::KeyDirection::Up);
136                        key
137                    };
138                    window.handle_input(PlatformInput::KeyUp(crate::KeyUpEvent {
139                        keystroke: crate::Keystroke {
140                            modifiers,
141                            key,
142                            ime_key: None,
143                        },
144                    }));
145                }
146                xcb::Event::X(x::Event::ButtonPress(ev)) => {
147                    let window = self.get_window(ev.event());
148                    let modifiers = super::modifiers_from_state(ev.state());
149                    let position =
150                        Point::new((ev.event_x() as f32).into(), (ev.event_y() as f32).into());
151                    if let Some(button) = super::button_of_key(ev.detail()) {
152                        window.handle_input(PlatformInput::MouseDown(crate::MouseDownEvent {
153                            button,
154                            position,
155                            modifiers,
156                            click_count: 1,
157                        }));
158                    } else {
159                        log::warn!("Unknown button press: {ev:?}");
160                    }
161                }
162                xcb::Event::X(x::Event::ButtonRelease(ev)) => {
163                    let window = self.get_window(ev.event());
164                    let modifiers = super::modifiers_from_state(ev.state());
165                    let position =
166                        Point::new((ev.event_x() as f32).into(), (ev.event_y() as f32).into());
167                    if let Some(button) = super::button_of_key(ev.detail()) {
168                        window.handle_input(PlatformInput::MouseUp(crate::MouseUpEvent {
169                            button,
170                            position,
171                            modifiers,
172                            click_count: 1,
173                        }));
174                    }
175                }
176                xcb::Event::X(x::Event::MotionNotify(ev)) => {
177                    let window = self.get_window(ev.event());
178                    let pressed_button = super::button_from_state(ev.state());
179                    let position =
180                        Point::new((ev.event_x() as f32).into(), (ev.event_y() as f32).into());
181                    let modifiers = super::modifiers_from_state(ev.state());
182                    window.handle_input(PlatformInput::MouseMove(crate::MouseMoveEvent {
183                        pressed_button,
184                        position,
185                        modifiers,
186                    }));
187                }
188                xcb::Event::X(x::Event::LeaveNotify(ev)) => {
189                    let window = self.get_window(ev.event());
190                    let pressed_button = super::button_from_state(ev.state());
191                    let position =
192                        Point::new((ev.event_x() as f32).into(), (ev.event_y() as f32).into());
193                    let modifiers = super::modifiers_from_state(ev.state());
194                    window.handle_input(PlatformInput::MouseExited(crate::MouseExitEvent {
195                        pressed_button,
196                        position,
197                        modifiers,
198                    }));
199                }
200                _ => {}
201            }
202
203            if let Ok(runnable) = self.platform_inner.main_receiver.try_recv() {
204                runnable.run();
205            }
206        }
207
208        if let Some(ref mut fun) = self.platform_inner.callbacks.lock().quit {
209            fun();
210        }
211    }
212    fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
213        let setup = self.xcb_connection.get_setup();
214        setup
215            .roots()
216            .enumerate()
217            .map(|(root_id, _)| {
218                Rc::new(X11Display::new(&self.xcb_connection, root_id as i32))
219                    as Rc<dyn PlatformDisplay>
220            })
221            .collect()
222    }
223    fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
224        Some(Rc::new(X11Display::new(&self.xcb_connection, id.0 as i32)))
225    }
226
227    fn open_window(
228        &self,
229        _handle: AnyWindowHandle,
230        options: WindowOptions,
231    ) -> Box<dyn PlatformWindow> {
232        let x_window = self.xcb_connection.generate_id();
233
234        let window_ptr = Rc::new(X11WindowState::new(
235            options,
236            &self.xcb_connection,
237            self.x_root_index,
238            x_window,
239            &self.atoms,
240        ));
241        window_ptr.request_refresh();
242
243        self.state
244            .lock()
245            .windows
246            .insert(x_window, Rc::clone(&window_ptr));
247        Box::new(X11Window(window_ptr))
248    }
249}