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, HashSet};
  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, ScrollDelta, Size,
 15    TouchPhase, WindowOptions,
 16};
 17
 18pub(crate) struct X11ClientState {
 19    pub(crate) windows: HashMap<x::Window, Rc<X11WindowState>>,
 20    xkb: xkbcommon::xkb::State,
 21}
 22
 23pub(crate) struct X11Client {
 24    platform_inner: Rc<LinuxPlatformInner>,
 25    xcb_connection: Arc<xcb::Connection>,
 26    x_root_index: i32,
 27    atoms: XcbAtoms,
 28    state: Mutex<X11ClientState>,
 29}
 30
 31impl X11Client {
 32    pub(crate) fn new(
 33        inner: Rc<LinuxPlatformInner>,
 34        xcb_connection: Arc<xcb::Connection>,
 35        x_root_index: i32,
 36        atoms: XcbAtoms,
 37    ) -> Self {
 38        let xkb_context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
 39        let xkb_device_id = xkb::x11::get_core_keyboard_device_id(&xcb_connection);
 40        let xkb_keymap = xkb::x11::keymap_new_from_device(
 41            &xkb_context,
 42            &xcb_connection,
 43            xkb_device_id,
 44            xkb::KEYMAP_COMPILE_NO_FLAGS,
 45        );
 46        let xkb_state =
 47            xkb::x11::state_new_from_device(&xkb_keymap, &xcb_connection, xkb_device_id);
 48
 49        Self {
 50            platform_inner: inner,
 51            xcb_connection,
 52            x_root_index,
 53            atoms,
 54            state: Mutex::new(X11ClientState {
 55                windows: HashMap::default(),
 56                xkb: xkb_state,
 57            }),
 58        }
 59    }
 60
 61    fn get_window(&self, win: x::Window) -> Rc<X11WindowState> {
 62        let state = self.state.lock();
 63        Rc::clone(&state.windows[&win])
 64    }
 65}
 66
 67impl Client for X11Client {
 68    fn run(&self, on_finish_launching: Box<dyn FnOnce()>) {
 69        on_finish_launching();
 70        let mut windows_to_refresh = HashSet::<x::Window>::default();
 71        while !self.platform_inner.state.lock().quit_requested {
 72            // We prioritize work in the following order:
 73            //   1. input events from X11
 74            //   2. runnables for the main thread
 75            //   3. drawing/presentation
 76            let event = if let Some(event) = self.xcb_connection.poll_for_event().unwrap() {
 77                event
 78            } else if let Ok(runnable) = self.platform_inner.main_receiver.try_recv() {
 79                runnable.run();
 80                continue;
 81            } else if let Some(x_window) = windows_to_refresh.iter().next().cloned() {
 82                windows_to_refresh.remove(&x_window);
 83                let window = self.get_window(x_window);
 84                window.refresh();
 85                window.request_refresh();
 86                continue;
 87            } else {
 88                profiling::scope!("Wait for event");
 89                self.xcb_connection.wait_for_event().unwrap()
 90            };
 91
 92            match event {
 93                xcb::Event::X(x::Event::ClientMessage(ev)) => {
 94                    if let x::ClientMessageData::Data32([atom, ..]) = ev.data() {
 95                        if atom == self.atoms.wm_del_window.resource_id() {
 96                            windows_to_refresh.remove(&ev.window());
 97                            // window "x" button clicked by user, we gracefully exit
 98                            let window = self.state.lock().windows.remove(&ev.window()).unwrap();
 99                            window.destroy();
100                            let state = self.state.lock();
101                            self.platform_inner.state.lock().quit_requested |=
102                                state.windows.is_empty();
103                        }
104                    }
105                }
106                xcb::Event::X(x::Event::Expose(ev)) => {
107                    windows_to_refresh.insert(ev.window());
108                }
109                xcb::Event::X(x::Event::ConfigureNotify(ev)) => {
110                    let bounds = Bounds {
111                        origin: Point {
112                            x: ev.x().into(),
113                            y: ev.y().into(),
114                        },
115                        size: Size {
116                            width: ev.width().into(),
117                            height: ev.height().into(),
118                        },
119                    };
120                    self.get_window(ev.window()).configure(bounds)
121                }
122                xcb::Event::Present(xcb::present::Event::CompleteNotify(ev)) => {
123                    windows_to_refresh.insert(ev.window());
124                }
125                xcb::Event::Present(xcb::present::Event::IdleNotify(_ev)) => {}
126                xcb::Event::X(x::Event::FocusIn(ev)) => {
127                    let window = self.get_window(ev.event());
128                    window.set_focused(true);
129                }
130                xcb::Event::X(x::Event::FocusOut(ev)) => {
131                    let window = self.get_window(ev.event());
132                    window.set_focused(false);
133                }
134                xcb::Event::X(x::Event::KeyPress(ev)) => {
135                    let window = self.get_window(ev.event());
136                    let modifiers = super::modifiers_from_state(ev.state());
137                    let keystroke = {
138                        let code = ev.detail().into();
139                        let mut state = self.state.lock();
140                        let keystroke = crate::Keystroke::from_xkb(&state.xkb, modifiers, code);
141                        state.xkb.update_key(code, xkb::KeyDirection::Down);
142                        keystroke
143                    };
144
145                    window.handle_input(PlatformInput::KeyDown(crate::KeyDownEvent {
146                        keystroke,
147                        is_held: false,
148                    }));
149                }
150                xcb::Event::X(x::Event::KeyRelease(ev)) => {
151                    let window = self.get_window(ev.event());
152                    let modifiers = super::modifiers_from_state(ev.state());
153                    let keystroke = {
154                        let code = ev.detail().into();
155                        let mut state = self.state.lock();
156                        let keystroke = crate::Keystroke::from_xkb(&state.xkb, modifiers, code);
157                        state.xkb.update_key(code, xkb::KeyDirection::Up);
158                        keystroke
159                    };
160
161                    window.handle_input(PlatformInput::KeyUp(crate::KeyUpEvent { keystroke }));
162                }
163                xcb::Event::X(x::Event::ButtonPress(ev)) => {
164                    let window = self.get_window(ev.event());
165                    let modifiers = super::modifiers_from_state(ev.state());
166                    let position =
167                        Point::new((ev.event_x() as f32).into(), (ev.event_y() as f32).into());
168                    if let Some(button) = super::button_of_key(ev.detail()) {
169                        window.handle_input(PlatformInput::MouseDown(crate::MouseDownEvent {
170                            button,
171                            position,
172                            modifiers,
173                            click_count: 1,
174                        }));
175                    } else if ev.detail() >= 4 && ev.detail() <= 5 {
176                        // https://stackoverflow.com/questions/15510472/scrollwheel-event-in-x11
177                        let delta_x = if ev.detail() == 4 { 1.0 } else { -1.0 };
178                        window.handle_input(PlatformInput::ScrollWheel(crate::ScrollWheelEvent {
179                            position,
180                            delta: ScrollDelta::Lines(Point::new(0.0, delta_x)),
181                            modifiers,
182                            touch_phase: TouchPhase::default(),
183                        }));
184                    } else {
185                        log::warn!("Unknown button press: {ev:?}");
186                    }
187                }
188                xcb::Event::X(x::Event::ButtonRelease(ev)) => {
189                    let window = self.get_window(ev.event());
190                    let modifiers = super::modifiers_from_state(ev.state());
191                    let position =
192                        Point::new((ev.event_x() as f32).into(), (ev.event_y() as f32).into());
193                    if let Some(button) = super::button_of_key(ev.detail()) {
194                        window.handle_input(PlatformInput::MouseUp(crate::MouseUpEvent {
195                            button,
196                            position,
197                            modifiers,
198                            click_count: 1,
199                        }));
200                    }
201                }
202                xcb::Event::X(x::Event::MotionNotify(ev)) => {
203                    let window = self.get_window(ev.event());
204                    let pressed_button = super::button_from_state(ev.state());
205                    let position =
206                        Point::new((ev.event_x() as f32).into(), (ev.event_y() as f32).into());
207                    let modifiers = super::modifiers_from_state(ev.state());
208                    window.handle_input(PlatformInput::MouseMove(crate::MouseMoveEvent {
209                        pressed_button,
210                        position,
211                        modifiers,
212                    }));
213                }
214                xcb::Event::X(x::Event::LeaveNotify(ev)) => {
215                    let window = self.get_window(ev.event());
216                    let pressed_button = super::button_from_state(ev.state());
217                    let position =
218                        Point::new((ev.event_x() as f32).into(), (ev.event_y() as f32).into());
219                    let modifiers = super::modifiers_from_state(ev.state());
220                    window.handle_input(PlatformInput::MouseExited(crate::MouseExitEvent {
221                        pressed_button,
222                        position,
223                        modifiers,
224                    }));
225                }
226                _ => {}
227            }
228        }
229
230        if let Some(ref mut fun) = self.platform_inner.callbacks.lock().quit {
231            fun();
232        }
233    }
234
235    fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
236        let setup = self.xcb_connection.get_setup();
237        setup
238            .roots()
239            .enumerate()
240            .map(|(root_id, _)| {
241                Rc::new(X11Display::new(&self.xcb_connection, root_id as i32))
242                    as Rc<dyn PlatformDisplay>
243            })
244            .collect()
245    }
246
247    fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
248        Some(Rc::new(X11Display::new(&self.xcb_connection, id.0 as i32)))
249    }
250
251    fn open_window(
252        &self,
253        _handle: AnyWindowHandle,
254        options: WindowOptions,
255    ) -> Box<dyn PlatformWindow> {
256        let x_window = self.xcb_connection.generate_id();
257
258        let window_ptr = Rc::new(X11WindowState::new(
259            options,
260            &self.xcb_connection,
261            self.x_root_index,
262            x_window,
263            &self.atoms,
264        ));
265        window_ptr.request_refresh();
266
267        self.state
268            .lock()
269            .windows
270            .insert(x_window, Rc::clone(&window_ptr));
271        Box::new(X11Window(window_ptr))
272    }
273}