client.rs

  1use std::rc::Rc;
  2use std::sync::Arc;
  3
  4use parking_lot::Mutex;
  5use wayland_backend::protocol::WEnum;
  6use wayland_client::protocol::wl_callback::WlCallback;
  7use wayland_client::protocol::wl_pointer::AxisRelativeDirection;
  8use wayland_client::{
  9    delegate_noop,
 10    protocol::{
 11        wl_buffer, wl_callback, wl_compositor, wl_keyboard, wl_pointer, wl_registry, wl_seat,
 12        wl_shm, wl_shm_pool,
 13        wl_surface::{self, WlSurface},
 14    },
 15    Connection, Dispatch, EventQueue, Proxy, QueueHandle,
 16};
 17use wayland_protocols::xdg::shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base};
 18use xkbcommon::xkb;
 19use xkbcommon::xkb::ffi::XKB_KEYMAP_FORMAT_TEXT_V1;
 20use xkbcommon::xkb::{Keycode, KEYMAP_COMPILE_NO_FLAGS};
 21
 22use crate::platform::linux::client::Client;
 23use crate::platform::linux::wayland::window::WaylandWindow;
 24use crate::platform::{LinuxPlatformInner, PlatformWindow};
 25use crate::PlatformInput::KeyDown;
 26use crate::ScrollDelta;
 27use crate::{
 28    platform::linux::wayland::window::WaylandWindowState, AnyWindowHandle, DisplayId, KeyDownEvent,
 29    KeyUpEvent, Keystroke, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
 30    Pixels, PlatformDisplay, PlatformInput, Point, ScrollWheelEvent, TouchPhase, WindowOptions,
 31};
 32
 33const MIN_KEYCODE: u32 = 8; // used to convert evdev scancode to xkb scancode
 34
 35pub(crate) struct WaylandClientState {
 36    compositor: Option<wl_compositor::WlCompositor>,
 37    buffer: Option<wl_buffer::WlBuffer>,
 38    wm_base: Option<xdg_wm_base::XdgWmBase>,
 39    windows: Vec<(xdg_surface::XdgSurface, Rc<WaylandWindowState>)>,
 40    platform_inner: Rc<LinuxPlatformInner>,
 41    wl_seat: Option<wl_seat::WlSeat>,
 42    keymap_state: Option<xkb::State>,
 43    modifiers: Modifiers,
 44    scroll_direction: f64,
 45    mouse_location: Option<Point<Pixels>>,
 46    button_pressed: Option<MouseButton>,
 47    mouse_focused_window: Option<Rc<WaylandWindowState>>,
 48    keyboard_focused_window: Option<Rc<WaylandWindowState>>,
 49}
 50
 51pub(crate) struct WaylandClient {
 52    platform_inner: Rc<LinuxPlatformInner>,
 53    conn: Arc<Connection>,
 54    state: Mutex<WaylandClientState>,
 55    event_queue: Mutex<EventQueue<WaylandClientState>>,
 56    qh: Arc<QueueHandle<WaylandClientState>>,
 57}
 58
 59impl WaylandClient {
 60    pub(crate) fn new(linux_platform_inner: Rc<LinuxPlatformInner>, conn: Arc<Connection>) -> Self {
 61        let state = WaylandClientState {
 62            compositor: None,
 63            buffer: None,
 64            wm_base: None,
 65            windows: Vec::new(),
 66            platform_inner: Rc::clone(&linux_platform_inner),
 67            wl_seat: None,
 68            keymap_state: None,
 69            modifiers: Modifiers {
 70                shift: false,
 71                control: false,
 72                alt: false,
 73                function: false,
 74                command: false,
 75            },
 76            scroll_direction: -1.0,
 77            mouse_location: None,
 78            button_pressed: None,
 79            mouse_focused_window: None,
 80            keyboard_focused_window: None,
 81        };
 82        let event_queue: EventQueue<WaylandClientState> = conn.new_event_queue();
 83        let qh = event_queue.handle();
 84        Self {
 85            platform_inner: linux_platform_inner,
 86            conn,
 87            state: Mutex::new(state),
 88            event_queue: Mutex::new(event_queue),
 89            qh: Arc::new(qh),
 90        }
 91    }
 92}
 93
 94impl Client for WaylandClient {
 95    fn run(&self, on_finish_launching: Box<dyn FnOnce()>) {
 96        let display = self.conn.display();
 97        let mut eq = self.event_queue.lock();
 98        let _registry = display.get_registry(&self.qh, ());
 99
100        eq.roundtrip(&mut self.state.lock()).unwrap();
101
102        on_finish_launching();
103        while !self.platform_inner.state.lock().quit_requested {
104            eq.flush().unwrap();
105            eq.dispatch_pending(&mut self.state.lock()).unwrap();
106            if let Some(guard) = self.conn.prepare_read() {
107                guard.read().unwrap();
108                eq.dispatch_pending(&mut self.state.lock()).unwrap();
109            }
110            if let Ok(runnable) = self.platform_inner.main_receiver.try_recv() {
111                runnable.run();
112            }
113        }
114    }
115
116    fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
117        Vec::new()
118    }
119
120    fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
121        unimplemented!()
122    }
123
124    fn open_window(
125        &self,
126        handle: AnyWindowHandle,
127        options: WindowOptions,
128    ) -> Box<dyn PlatformWindow> {
129        let mut state = self.state.lock();
130
131        let wm_base = state.wm_base.as_ref().unwrap();
132        let compositor = state.compositor.as_ref().unwrap();
133        let wl_surface = compositor.create_surface(&self.qh, ());
134        let xdg_surface = wm_base.get_xdg_surface(&wl_surface, &self.qh, ());
135        let toplevel = xdg_surface.get_toplevel(&self.qh, ());
136        let wl_surface = Arc::new(wl_surface);
137
138        wl_surface.frame(&self.qh, wl_surface.clone());
139        wl_surface.commit();
140
141        let window_state = Rc::new(WaylandWindowState::new(
142            &self.conn,
143            wl_surface.clone(),
144            Arc::new(toplevel),
145            options,
146        ));
147
148        state.windows.push((xdg_surface, Rc::clone(&window_state)));
149        Box::new(WaylandWindow(window_state))
150    }
151}
152
153impl Dispatch<wl_registry::WlRegistry, ()> for WaylandClientState {
154    fn event(
155        state: &mut Self,
156        registry: &wl_registry::WlRegistry,
157        event: wl_registry::Event,
158        _: &(),
159        _: &Connection,
160        qh: &QueueHandle<Self>,
161    ) {
162        if let wl_registry::Event::Global {
163            name, interface, ..
164        } = event
165        {
166            match &interface[..] {
167                "wl_compositor" => {
168                    let compositor =
169                        registry.bind::<wl_compositor::WlCompositor, _, _>(name, 1, qh, ());
170                    state.compositor = Some(compositor);
171                }
172                "xdg_wm_base" => {
173                    let wm_base = registry.bind::<xdg_wm_base::XdgWmBase, _, _>(name, 1, qh, ());
174                    state.wm_base = Some(wm_base);
175                }
176                "wl_seat" => {
177                    let seat = registry.bind::<wl_seat::WlSeat, _, _>(name, 1, qh, ());
178                    state.wl_seat = Some(seat);
179                }
180                _ => {}
181            };
182        }
183    }
184}
185
186delegate_noop!(WaylandClientState: ignore wl_compositor::WlCompositor);
187delegate_noop!(WaylandClientState: ignore wl_surface::WlSurface);
188delegate_noop!(WaylandClientState: ignore wl_shm::WlShm);
189delegate_noop!(WaylandClientState: ignore wl_shm_pool::WlShmPool);
190delegate_noop!(WaylandClientState: ignore wl_buffer::WlBuffer);
191
192impl Dispatch<WlCallback, Arc<WlSurface>> for WaylandClientState {
193    fn event(
194        state: &mut Self,
195        _: &WlCallback,
196        event: wl_callback::Event,
197        surf: &Arc<WlSurface>,
198        _: &Connection,
199        qh: &QueueHandle<Self>,
200    ) {
201        if let wl_callback::Event::Done { .. } = event {
202            for window in &state.windows {
203                if window.1.surface.id() == surf.id() {
204                    window.1.surface.frame(qh, surf.clone());
205                    window.1.update();
206                    window.1.surface.commit();
207                }
208            }
209        }
210    }
211}
212
213impl Dispatch<xdg_surface::XdgSurface, ()> for WaylandClientState {
214    fn event(
215        state: &mut Self,
216        xdg_surface: &xdg_surface::XdgSurface,
217        event: xdg_surface::Event,
218        _: &(),
219        _: &Connection,
220        _: &QueueHandle<Self>,
221    ) {
222        if let xdg_surface::Event::Configure { serial, .. } = event {
223            xdg_surface.ack_configure(serial);
224            for window in &state.windows {
225                if &window.0 == xdg_surface {
226                    window.1.update();
227                    window.1.surface.commit();
228                    return;
229                }
230            }
231        }
232    }
233}
234
235impl Dispatch<xdg_toplevel::XdgToplevel, ()> for WaylandClientState {
236    fn event(
237        state: &mut Self,
238        xdg_toplevel: &xdg_toplevel::XdgToplevel,
239        event: <xdg_toplevel::XdgToplevel as Proxy>::Event,
240        _: &(),
241        _: &Connection,
242        _: &QueueHandle<Self>,
243    ) {
244        if let xdg_toplevel::Event::Configure {
245            width,
246            height,
247            states: _states,
248        } = event
249        {
250            if width == 0 || height == 0 {
251                return;
252            }
253            for window in &state.windows {
254                if window.1.toplevel.id() == xdg_toplevel.id() {
255                    window.1.resize(width, height);
256                    window.1.surface.commit();
257                    return;
258                }
259            }
260        } else if let xdg_toplevel::Event::Close = event {
261            state.windows.retain(|(_, window)| {
262                if window.toplevel.id() == xdg_toplevel.id() {
263                    window.toplevel.destroy();
264                    false
265                } else {
266                    true
267                }
268            });
269            state.platform_inner.state.lock().quit_requested |= state.windows.is_empty();
270        }
271    }
272}
273
274impl Dispatch<xdg_wm_base::XdgWmBase, ()> for WaylandClientState {
275    fn event(
276        _: &mut Self,
277        wm_base: &xdg_wm_base::XdgWmBase,
278        event: <xdg_wm_base::XdgWmBase as Proxy>::Event,
279        _: &(),
280        _: &Connection,
281        _: &QueueHandle<Self>,
282    ) {
283        if let xdg_wm_base::Event::Ping { serial } = event {
284            wm_base.pong(serial);
285        }
286    }
287}
288
289impl Dispatch<wl_seat::WlSeat, ()> for WaylandClientState {
290    fn event(
291        state: &mut Self,
292        seat: &wl_seat::WlSeat,
293        event: wl_seat::Event,
294        data: &(),
295        conn: &Connection,
296        qh: &QueueHandle<Self>,
297    ) {
298        if let wl_seat::Event::Capabilities {
299            capabilities: WEnum::Value(capabilities),
300        } = event
301        {
302            if capabilities.contains(wl_seat::Capability::Keyboard) {
303                seat.get_keyboard(qh, ());
304            }
305            if capabilities.contains(wl_seat::Capability::Pointer) {
306                seat.get_pointer(qh, ());
307            }
308        }
309    }
310}
311
312impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClientState {
313    fn event(
314        state: &mut Self,
315        keyboard: &wl_keyboard::WlKeyboard,
316        event: wl_keyboard::Event,
317        data: &(),
318        conn: &Connection,
319        qh: &QueueHandle<Self>,
320    ) {
321        match event {
322            wl_keyboard::Event::Keymap {
323                format: WEnum::Value(format),
324                fd,
325                size,
326                ..
327            } => {
328                assert_eq!(
329                    format,
330                    wl_keyboard::KeymapFormat::XkbV1,
331                    "Unsupported keymap format"
332                );
333                let keymap = unsafe {
334                    xkb::Keymap::new_from_fd(
335                        &xkb::Context::new(xkb::CONTEXT_NO_FLAGS),
336                        fd,
337                        size as usize,
338                        XKB_KEYMAP_FORMAT_TEXT_V1,
339                        KEYMAP_COMPILE_NO_FLAGS,
340                    )
341                    .unwrap()
342                }
343                .unwrap();
344                state.keymap_state = Some(xkb::State::new(&keymap));
345            }
346            wl_keyboard::Event::Enter { surface, .. } => {
347                for window in &state.windows {
348                    if window.1.surface.id() == surface.id() {
349                        state.keyboard_focused_window = Some(Rc::clone(&window.1));
350                    }
351                }
352            }
353            wl_keyboard::Event::Modifiers {
354                mods_depressed,
355                mods_latched,
356                mods_locked,
357                group,
358                ..
359            } => {
360                state.keymap_state.as_mut().unwrap().update_mask(
361                    mods_depressed,
362                    mods_latched,
363                    mods_locked,
364                    0,
365                    0,
366                    group,
367                );
368            }
369            wl_keyboard::Event::Key {
370                key,
371                state: WEnum::Value(key_state),
372                ..
373            } => {
374                let keymap_state = state.keymap_state.as_ref().unwrap();
375                let key_utf8 = keymap_state.key_get_utf8(Keycode::from(key + MIN_KEYCODE));
376                let key_sym = keymap_state.key_get_one_sym(Keycode::from(key + MIN_KEYCODE));
377
378                let key = if matches!(
379                    key_sym,
380                    xkb::Keysym::BackSpace
381                        | xkb::Keysym::Left
382                        | xkb::Keysym::Right
383                        | xkb::Keysym::Down
384                        | xkb::Keysym::Up
385                        | xkb::Keysym::Super_L
386                        | xkb::Keysym::Super_R
387                ) {
388                    xkb::keysym_get_name(key_sym).to_lowercase()
389                } else {
390                    key_utf8.clone()
391                };
392
393                let focused_window = &state.keyboard_focused_window;
394                if let Some(focused_window) = focused_window {
395                    match key_state {
396                        wl_keyboard::KeyState::Pressed => {
397                            if key_sym == xkb::Keysym::Shift_L || key_sym == xkb::Keysym::Shift_R {
398                                state.modifiers.shift = true;
399                            } else if key_sym == xkb::Keysym::Control_L
400                                || key_sym == xkb::Keysym::Control_R
401                            {
402                                state.modifiers.control = true;
403                            } else if key_sym == xkb::Keysym::Alt_L || key_sym == xkb::Keysym::Alt_R
404                            {
405                                state.modifiers.alt = true;
406                            } else {
407                                focused_window.handle_input(KeyDown(KeyDownEvent {
408                                    keystroke: Keystroke {
409                                        modifiers: state.modifiers,
410                                        key,
411                                        ime_key: None,
412                                    },
413                                    is_held: false, // todo!(linux)
414                                }));
415                            }
416                        }
417                        wl_keyboard::KeyState::Released => {
418                            if key_sym == xkb::Keysym::Shift_L || key_sym == xkb::Keysym::Shift_R {
419                                state.modifiers.shift = false;
420                            } else if key_sym == xkb::Keysym::Control_L
421                                || key_sym == xkb::Keysym::Control_R
422                            {
423                                state.modifiers.control = false;
424                            } else if key_sym == xkb::Keysym::Alt_L || key_sym == xkb::Keysym::Alt_R
425                            {
426                                state.modifiers.alt = false;
427                            } else {
428                                focused_window.handle_input(PlatformInput::KeyUp(KeyUpEvent {
429                                    keystroke: Keystroke {
430                                        modifiers: state.modifiers,
431                                        key,
432                                        ime_key: None,
433                                    },
434                                }));
435                            }
436                        }
437                        _ => {}
438                    }
439                }
440            }
441            wl_keyboard::Event::Leave { .. } => {
442                state.modifiers = Modifiers {
443                    control: false,
444                    alt: false,
445                    shift: false,
446                    command: false,
447                    function: false,
448                };
449            }
450            _ => {}
451        }
452    }
453}
454
455fn linux_button_to_gpui(button: u32) -> MouseButton {
456    match button {
457        0x110 => MouseButton::Left,
458        0x111 => MouseButton::Right,
459        0x112 => MouseButton::Middle,
460        _ => unimplemented!(), // todo!(linux)
461    }
462}
463
464impl Dispatch<wl_pointer::WlPointer, ()> for WaylandClientState {
465    fn event(
466        state: &mut Self,
467        wl_pointer: &wl_pointer::WlPointer,
468        event: wl_pointer::Event,
469        data: &(),
470        conn: &Connection,
471        qh: &QueueHandle<Self>,
472    ) {
473        match event {
474            wl_pointer::Event::Enter {
475                surface,
476                surface_x,
477                surface_y,
478                ..
479            } => {
480                for window in &state.windows {
481                    if window.1.surface.id() == surface.id() {
482                        state.mouse_focused_window = Some(Rc::clone(&window.1));
483                    }
484                }
485                state.mouse_location = Some(Point {
486                    x: Pixels::from(surface_x),
487                    y: Pixels::from(surface_y),
488                });
489            }
490            wl_pointer::Event::Motion {
491                time,
492                surface_x,
493                surface_y,
494                ..
495            } => {
496                let focused_window = &state.mouse_focused_window;
497                if let Some(focused_window) = focused_window {
498                    state.mouse_location = Some(Point {
499                        x: Pixels::from(surface_x),
500                        y: Pixels::from(surface_y),
501                    });
502                    focused_window.handle_input(PlatformInput::MouseMove(MouseMoveEvent {
503                        position: state.mouse_location.unwrap(),
504                        pressed_button: state.button_pressed,
505                        modifiers: state.modifiers,
506                    }))
507                }
508            }
509            wl_pointer::Event::Button {
510                button,
511                state: WEnum::Value(button_state),
512                ..
513            } => {
514                let focused_window = &state.mouse_focused_window;
515                let mouse_location = &state.mouse_location;
516                if let (Some(focused_window), Some(mouse_location)) =
517                    (focused_window, mouse_location)
518                {
519                    match button_state {
520                        wl_pointer::ButtonState::Pressed => {
521                            state.button_pressed = Some(linux_button_to_gpui(button));
522                            focused_window.handle_input(PlatformInput::MouseDown(MouseDownEvent {
523                                button: linux_button_to_gpui(button),
524                                position: *mouse_location,
525                                modifiers: state.modifiers,
526                                click_count: 1,
527                            }));
528                        }
529                        wl_pointer::ButtonState::Released => {
530                            state.button_pressed = None;
531                            focused_window.handle_input(PlatformInput::MouseUp(MouseUpEvent {
532                                button: linux_button_to_gpui(button),
533                                position: *mouse_location,
534                                modifiers: Modifiers {
535                                    shift: false,
536                                    control: false,
537                                    alt: false,
538                                    function: false,
539                                    command: false,
540                                },
541                                click_count: 1,
542                            }));
543                        }
544                        _ => {}
545                    }
546                }
547            }
548            wl_pointer::Event::AxisRelativeDirection {
549                direction: WEnum::Value(direction),
550                ..
551            } => {
552                state.scroll_direction = match direction {
553                    AxisRelativeDirection::Identical => -1.0,
554                    AxisRelativeDirection::Inverted => 1.0,
555                    _ => -1.0,
556                }
557            }
558            wl_pointer::Event::Axis {
559                time,
560                axis: WEnum::Value(axis),
561                value,
562                ..
563            } => {
564                let focused_window = &state.mouse_focused_window;
565                let mouse_location = &state.mouse_location;
566                if let (Some(focused_window), Some(mouse_location)) =
567                    (focused_window, mouse_location)
568                {
569                    let value = value * state.scroll_direction;
570                    focused_window.handle_input(PlatformInput::ScrollWheel(ScrollWheelEvent {
571                        position: *mouse_location,
572                        delta: match axis {
573                            wl_pointer::Axis::VerticalScroll => {
574                                ScrollDelta::Pixels(Point::new(Pixels(0.0), Pixels(value as f32)))
575                            }
576                            wl_pointer::Axis::HorizontalScroll => {
577                                ScrollDelta::Pixels(Point::new(Pixels(value as f32), Pixels(0.0)))
578                            }
579                            _ => unimplemented!(),
580                        },
581                        modifiers: state.modifiers,
582                        touch_phase: TouchPhase::Started,
583                    }))
584                }
585            }
586            wl_pointer::Event::Leave { surface, .. } => {
587                let focused_window = &state.mouse_focused_window;
588                if let Some(focused_window) = focused_window {
589                    focused_window.handle_input(PlatformInput::MouseMove(MouseMoveEvent {
590                        position: Point::<Pixels>::default(),
591                        pressed_button: None,
592                        modifiers: Modifiers::default(),
593                    }));
594                }
595                state.mouse_focused_window = None;
596                state.mouse_location = None;
597            }
598            _ => {}
599        }
600    }
601}