client.rs

  1use std::cell::RefCell;
  2use std::rc::Rc;
  3use std::time::{Duration, Instant};
  4
  5use calloop::timer::{TimeoutAction, Timer};
  6use calloop::{EventLoop, LoopHandle};
  7use calloop_wayland_source::WaylandSource;
  8use collections::HashMap;
  9use copypasta::wayland_clipboard::{create_clipboards_from_external, Clipboard, Primary};
 10use copypasta::ClipboardProvider;
 11use util::ResultExt;
 12use wayland_backend::client::ObjectId;
 13use wayland_backend::protocol::WEnum;
 14use wayland_client::globals::{registry_queue_init, GlobalList, GlobalListContents};
 15use wayland_client::protocol::wl_callback::{self, WlCallback};
 16use wayland_client::protocol::wl_output;
 17use wayland_client::protocol::wl_pointer::{AxisRelativeDirection, AxisSource};
 18use wayland_client::{
 19    delegate_noop,
 20    protocol::{
 21        wl_buffer, wl_compositor, wl_keyboard, wl_pointer, wl_registry, wl_seat, wl_shm,
 22        wl_shm_pool, wl_surface,
 23    },
 24    Connection, Dispatch, Proxy, QueueHandle,
 25};
 26use wayland_protocols::wp::fractional_scale::v1::client::{
 27    wp_fractional_scale_manager_v1, wp_fractional_scale_v1,
 28};
 29use wayland_protocols::wp::viewporter::client::{wp_viewport, wp_viewporter};
 30use wayland_protocols::xdg::decoration::zv1::client::{
 31    zxdg_decoration_manager_v1, zxdg_toplevel_decoration_v1,
 32};
 33use wayland_protocols::xdg::shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base};
 34use xkbcommon::xkb::ffi::XKB_KEYMAP_FORMAT_TEXT_V1;
 35use xkbcommon::xkb::{self, Keycode, KEYMAP_COMPILE_NO_FLAGS};
 36
 37use super::super::DOUBLE_CLICK_INTERVAL;
 38use crate::platform::linux::is_within_click_distance;
 39use crate::platform::linux::wayland::cursor::Cursor;
 40use crate::platform::linux::wayland::window::WaylandWindow;
 41use crate::platform::linux::LinuxClient;
 42use crate::platform::PlatformWindow;
 43use crate::{point, px, MouseExitEvent};
 44use crate::{
 45    AnyWindowHandle, CursorStyle, DisplayId, KeyDownEvent, KeyUpEvent, Keystroke, Modifiers,
 46    ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
 47    NavigationDirection, Pixels, PlatformDisplay, PlatformInput, Point, ScrollDelta,
 48    ScrollWheelEvent, TouchPhase,
 49};
 50use crate::{LinuxCommon, WindowParams};
 51
 52/// Used to convert evdev scancode to xkb scancode
 53const MIN_KEYCODE: u32 = 8;
 54
 55#[derive(Debug, Clone)]
 56pub struct Globals {
 57    pub qh: QueueHandle<WaylandClient>,
 58    pub compositor: wl_compositor::WlCompositor,
 59    pub wm_base: xdg_wm_base::XdgWmBase,
 60    pub shm: wl_shm::WlShm,
 61    pub viewporter: Option<wp_viewporter::WpViewporter>,
 62    pub fractional_scale_manager:
 63        Option<wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1>,
 64    pub decoration_manager: Option<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1>,
 65}
 66
 67impl Globals {
 68    fn new(globals: GlobalList, qh: QueueHandle<WaylandClient>) -> Self {
 69        Globals {
 70            compositor: globals
 71                .bind(
 72                    &qh,
 73                    wl_surface::REQ_SET_BUFFER_SCALE_SINCE
 74                        ..=wl_surface::EVT_PREFERRED_BUFFER_SCALE_SINCE,
 75                    (),
 76                )
 77                .unwrap(),
 78            shm: globals.bind(&qh, 1..=1, ()).unwrap(),
 79            wm_base: globals.bind(&qh, 1..=1, ()).unwrap(),
 80            viewporter: globals.bind(&qh, 1..=1, ()).ok(),
 81            fractional_scale_manager: globals.bind(&qh, 1..=1, ()).ok(),
 82            decoration_manager: globals.bind(&qh, 1..=1, ()).ok(),
 83            qh,
 84        }
 85    }
 86}
 87
 88pub(crate) struct WaylandClientState {
 89    globals: Globals,
 90    // Surface to Window mapping
 91    windows: HashMap<ObjectId, WaylandWindow>,
 92    // Output to scale mapping
 93    output_scales: HashMap<ObjectId, i32>,
 94    keymap_state: Option<xkb::State>,
 95    click: ClickState,
 96    repeat: KeyRepeat,
 97    modifiers: Modifiers,
 98    scroll_direction: f64,
 99    axis_source: AxisSource,
100    mouse_location: Option<Point<Pixels>>,
101    enter_token: Option<()>,
102    button_pressed: Option<MouseButton>,
103    mouse_focused_window: Option<WaylandWindow>,
104    keyboard_focused_window: Option<WaylandWindow>,
105    loop_handle: LoopHandle<'static, WaylandClient>,
106    cursor_icon_name: String,
107    cursor: Cursor,
108    clipboard: Clipboard,
109    primary: Primary,
110    event_loop: Option<EventLoop<'static, WaylandClient>>,
111    common: LinuxCommon,
112}
113
114pub struct ClickState {
115    last_click: Instant,
116    last_location: Point<Pixels>,
117    current_count: usize,
118}
119
120pub(crate) struct KeyRepeat {
121    characters_per_second: u32,
122    delay: Duration,
123    current_id: u64,
124    current_keysym: Option<xkb::Keysym>,
125}
126
127#[derive(Clone)]
128pub struct WaylandClient(Rc<RefCell<WaylandClientState>>);
129
130const WL_SEAT_MIN_VERSION: u32 = 4;
131const WL_OUTPUT_VERSION: u32 = 2;
132
133fn wl_seat_version(version: u32) -> u32 {
134    if version >= wl_pointer::EVT_AXIS_VALUE120_SINCE {
135        wl_pointer::EVT_AXIS_VALUE120_SINCE
136    } else if version >= WL_SEAT_MIN_VERSION {
137        WL_SEAT_MIN_VERSION
138    } else {
139        panic!(
140            "wl_seat below required version: {} < {}",
141            version, WL_SEAT_MIN_VERSION
142        );
143    }
144}
145
146impl WaylandClient {
147    pub(crate) fn new() -> Self {
148        let conn = Connection::connect_to_env().unwrap();
149
150        let (globals, mut event_queue) = registry_queue_init::<WaylandClient>(&conn).unwrap();
151        let qh = event_queue.handle();
152        let mut outputs = HashMap::default();
153
154        globals.contents().with_list(|list| {
155            for global in list {
156                match &global.interface[..] {
157                    "wl_seat" => {
158                        globals.registry().bind::<wl_seat::WlSeat, _, _>(
159                            global.name,
160                            wl_seat_version(global.version),
161                            &qh,
162                            (),
163                        );
164                    }
165                    "wl_output" => {
166                        let output = globals.registry().bind::<wl_output::WlOutput, _, _>(
167                            global.name,
168                            WL_OUTPUT_VERSION,
169                            &qh,
170                            (),
171                        );
172                        outputs.insert(output.id(), 1);
173                    }
174                    _ => {}
175                }
176            }
177        });
178
179        let display = conn.backend().display_ptr() as *mut std::ffi::c_void;
180        let (primary, clipboard) = unsafe { create_clipboards_from_external(display) };
181
182        let event_loop = EventLoop::try_new().unwrap();
183
184        let (common, main_receiver) = LinuxCommon::new(event_loop.get_signal());
185
186        let handle = event_loop.handle();
187
188        handle.insert_source(main_receiver, |event, _, _: &mut WaylandClient| {
189            if let calloop::channel::Event::Msg(runnable) = event {
190                runnable.run();
191            }
192        });
193
194        let globals = Globals::new(globals, qh);
195
196        let cursor = Cursor::new(&conn, &globals, 24);
197
198        let mut state = Rc::new(RefCell::new(WaylandClientState {
199            globals,
200            output_scales: outputs,
201            windows: HashMap::default(),
202            common,
203            keymap_state: None,
204            click: ClickState {
205                last_click: Instant::now(),
206                last_location: Point::new(px(0.0), px(0.0)),
207                current_count: 0,
208            },
209            repeat: KeyRepeat {
210                characters_per_second: 16,
211                delay: Duration::from_millis(500),
212                current_id: 0,
213                current_keysym: None,
214            },
215            modifiers: Modifiers {
216                shift: false,
217                control: false,
218                alt: false,
219                function: false,
220                platform: false,
221            },
222            scroll_direction: -1.0,
223            axis_source: AxisSource::Wheel,
224            mouse_location: None,
225            button_pressed: None,
226            mouse_focused_window: None,
227            keyboard_focused_window: None,
228            loop_handle: handle.clone(),
229            cursor_icon_name: "arrow".to_string(),
230            enter_token: None,
231            cursor,
232            clipboard,
233            primary,
234            event_loop: Some(event_loop),
235        }));
236
237        WaylandSource::new(conn, event_queue).insert(handle);
238
239        Self(state)
240    }
241}
242
243impl LinuxClient for WaylandClient {
244    fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
245        Vec::new()
246    }
247
248    fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
249        unimplemented!()
250    }
251
252    fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>> {
253        None
254    }
255
256    fn open_window(
257        &self,
258        handle: AnyWindowHandle,
259        params: WindowParams,
260    ) -> Box<dyn PlatformWindow> {
261        let mut state = self.0.borrow_mut();
262
263        let (window, surface_id) = WaylandWindow::new(state.globals.clone(), params);
264        state.windows.insert(surface_id, window.clone());
265
266        Box::new(window)
267    }
268
269    fn set_cursor_style(&self, style: CursorStyle) {
270        // Based on cursor names from https://gitlab.gnome.org/GNOME/adwaita-icon-theme (GNOME)
271        // and https://github.com/KDE/breeze (KDE). Both of them seem to be also derived from
272        // Web CSS cursor names: https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#values
273        let cursor_icon_name = match style {
274            CursorStyle::Arrow => "arrow",
275            CursorStyle::IBeam => "text",
276            CursorStyle::Crosshair => "crosshair",
277            CursorStyle::ClosedHand => "grabbing",
278            CursorStyle::OpenHand => "grab",
279            CursorStyle::PointingHand => "pointer",
280            CursorStyle::ResizeLeft => "w-resize",
281            CursorStyle::ResizeRight => "e-resize",
282            CursorStyle::ResizeLeftRight => "ew-resize",
283            CursorStyle::ResizeUp => "n-resize",
284            CursorStyle::ResizeDown => "s-resize",
285            CursorStyle::ResizeUpDown => "ns-resize",
286            CursorStyle::DisappearingItem => "grabbing", // todo(linux) - couldn't find equivalent icon in linux
287            CursorStyle::IBeamCursorForVerticalLayout => "vertical-text",
288            CursorStyle::OperationNotAllowed => "not-allowed",
289            CursorStyle::DragLink => "alias",
290            CursorStyle::DragCopy => "copy",
291            CursorStyle::ContextualMenu => "context-menu",
292        }
293        .to_string();
294
295        self.0.borrow_mut().cursor_icon_name = cursor_icon_name;
296    }
297
298    fn with_common<R>(&self, f: impl FnOnce(&mut LinuxCommon) -> R) -> R {
299        f(&mut self.0.borrow_mut().common)
300    }
301
302    fn run(&self) {
303        let mut event_loop = self
304            .0
305            .borrow_mut()
306            .event_loop
307            .take()
308            .expect("App is already running");
309
310        event_loop.run(None, &mut self.clone(), |_| {}).log_err();
311    }
312
313    fn write_to_clipboard(&self, item: crate::ClipboardItem) {
314        self.0.borrow_mut().clipboard.set_contents(item.text);
315    }
316
317    fn read_from_clipboard(&self) -> Option<crate::ClipboardItem> {
318        self.0
319            .borrow_mut()
320            .clipboard
321            .get_contents()
322            .ok()
323            .map(|s| crate::ClipboardItem {
324                text: s,
325                metadata: None,
326            })
327    }
328}
329
330impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for WaylandClient {
331    fn event(
332        state: &mut Self,
333        registry: &wl_registry::WlRegistry,
334        event: wl_registry::Event,
335        _: &GlobalListContents,
336        _: &Connection,
337        qh: &QueueHandle<Self>,
338    ) {
339        let mut state = state.0.borrow_mut();
340        match event {
341            wl_registry::Event::Global {
342                name,
343                interface,
344                version,
345            } => match &interface[..] {
346                "wl_seat" => {
347                    registry.bind::<wl_seat::WlSeat, _, _>(name, wl_seat_version(version), qh, ());
348                }
349                "wl_output" => {
350                    let output =
351                        registry.bind::<wl_output::WlOutput, _, _>(name, WL_OUTPUT_VERSION, qh, ());
352
353                    state.output_scales.insert(output.id(), 1);
354                }
355                _ => {}
356            },
357            wl_registry::Event::GlobalRemove { name: _ } => {}
358            _ => {}
359        }
360    }
361}
362
363delegate_noop!(WaylandClient: ignore wl_compositor::WlCompositor);
364delegate_noop!(WaylandClient: ignore wl_shm::WlShm);
365delegate_noop!(WaylandClient: ignore wl_shm_pool::WlShmPool);
366delegate_noop!(WaylandClient: ignore wl_buffer::WlBuffer);
367delegate_noop!(WaylandClient: ignore wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1);
368delegate_noop!(WaylandClient: ignore zxdg_decoration_manager_v1::ZxdgDecorationManagerV1);
369delegate_noop!(WaylandClient: ignore wp_viewporter::WpViewporter);
370delegate_noop!(WaylandClient: ignore wp_viewport::WpViewport);
371
372impl Dispatch<WlCallback, ObjectId> for WaylandClient {
373    fn event(
374        this: &mut WaylandClient,
375        _: &wl_callback::WlCallback,
376        event: wl_callback::Event,
377        surface_id: &ObjectId,
378        _: &Connection,
379        qh: &QueueHandle<Self>,
380    ) {
381        let state = this.0.borrow_mut();
382        let Some(window) = state.windows.get(surface_id).cloned() else {
383            return;
384        };
385
386        drop(state);
387        match event {
388            wl_callback::Event::Done { callback_data } => {
389                window.frame();
390            }
391            _ => {}
392        }
393    }
394}
395
396impl Dispatch<wl_surface::WlSurface, ()> for WaylandClient {
397    fn event(
398        state: &mut Self,
399        surface: &wl_surface::WlSurface,
400        event: <wl_surface::WlSurface as Proxy>::Event,
401        _: &(),
402        _: &Connection,
403        _: &QueueHandle<Self>,
404    ) {
405        let mut state = state.0.borrow_mut();
406        let Some(window) = state.windows.get(&surface.id()).cloned() else {
407            return;
408        };
409        let scales = state.output_scales.clone();
410        drop(state);
411
412        window.handle_surface_event(event, scales);
413    }
414}
415
416impl Dispatch<wl_output::WlOutput, ()> for WaylandClient {
417    fn event(
418        state: &mut Self,
419        output: &wl_output::WlOutput,
420        event: <wl_output::WlOutput as Proxy>::Event,
421        _: &(),
422        _: &Connection,
423        _: &QueueHandle<Self>,
424    ) {
425        let mut state = state.0.borrow_mut();
426        let Some(mut output_scale) = state.output_scales.get_mut(&output.id()) else {
427            return;
428        };
429
430        match event {
431            wl_output::Event::Scale { factor } => {
432                *output_scale = factor;
433            }
434            _ => {}
435        }
436    }
437}
438
439impl Dispatch<xdg_surface::XdgSurface, ObjectId> for WaylandClient {
440    fn event(
441        state: &mut Self,
442        xdg_surface: &xdg_surface::XdgSurface,
443        event: xdg_surface::Event,
444        surface_id: &ObjectId,
445        _: &Connection,
446        _: &QueueHandle<Self>,
447    ) {
448        let mut state = state.0.borrow_mut();
449        let Some(window) = state.windows.get(surface_id).cloned() else {
450            return;
451        };
452
453        drop(state);
454        window.handle_xdg_surface_event(event);
455    }
456}
457
458impl Dispatch<xdg_toplevel::XdgToplevel, ObjectId> for WaylandClient {
459    fn event(
460        this: &mut Self,
461        xdg_toplevel: &xdg_toplevel::XdgToplevel,
462        event: <xdg_toplevel::XdgToplevel as Proxy>::Event,
463        surface_id: &ObjectId,
464        _: &Connection,
465        _: &QueueHandle<Self>,
466    ) {
467        let mut state = this.0.borrow_mut();
468
469        let Some(window) = state.windows.get(surface_id).cloned() else {
470            return;
471        };
472
473        drop(state);
474        let should_close = window.handle_toplevel_event(event);
475
476        if should_close {
477            let mut state = this.0.borrow_mut();
478            state.windows.remove(surface_id);
479        }
480    }
481}
482
483impl Dispatch<xdg_wm_base::XdgWmBase, ()> for WaylandClient {
484    fn event(
485        _: &mut Self,
486        wm_base: &xdg_wm_base::XdgWmBase,
487        event: <xdg_wm_base::XdgWmBase as Proxy>::Event,
488        _: &(),
489        _: &Connection,
490        _: &QueueHandle<Self>,
491    ) {
492        if let xdg_wm_base::Event::Ping { serial } = event {
493            wm_base.pong(serial);
494        }
495    }
496}
497
498impl Dispatch<wl_seat::WlSeat, ()> for WaylandClient {
499    fn event(
500        state: &mut Self,
501        seat: &wl_seat::WlSeat,
502        event: wl_seat::Event,
503        data: &(),
504        conn: &Connection,
505        qh: &QueueHandle<Self>,
506    ) {
507        if let wl_seat::Event::Capabilities {
508            capabilities: WEnum::Value(capabilities),
509        } = event
510        {
511            if capabilities.contains(wl_seat::Capability::Keyboard) {
512                seat.get_keyboard(qh, ());
513            }
514            if capabilities.contains(wl_seat::Capability::Pointer) {
515                seat.get_pointer(qh, ());
516            }
517        }
518    }
519}
520
521impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClient {
522    fn event(
523        this: &mut Self,
524        keyboard: &wl_keyboard::WlKeyboard,
525        event: wl_keyboard::Event,
526        data: &(),
527        conn: &Connection,
528        qh: &QueueHandle<Self>,
529    ) {
530        let mut state = this.0.borrow_mut();
531        match event {
532            wl_keyboard::Event::RepeatInfo { rate, delay } => {
533                state.repeat.characters_per_second = rate as u32;
534                state.repeat.delay = Duration::from_millis(delay as u64);
535            }
536            wl_keyboard::Event::Keymap {
537                format: WEnum::Value(format),
538                fd,
539                size,
540                ..
541            } => {
542                assert_eq!(
543                    format,
544                    wl_keyboard::KeymapFormat::XkbV1,
545                    "Unsupported keymap format"
546                );
547                let keymap = unsafe {
548                    xkb::Keymap::new_from_fd(
549                        &xkb::Context::new(xkb::CONTEXT_NO_FLAGS),
550                        fd,
551                        size as usize,
552                        XKB_KEYMAP_FORMAT_TEXT_V1,
553                        KEYMAP_COMPILE_NO_FLAGS,
554                    )
555                    .log_err()
556                    .flatten()
557                    .expect("Failed to create keymap")
558                };
559                state.keymap_state = Some(xkb::State::new(&keymap));
560            }
561            wl_keyboard::Event::Enter { surface, .. } => {
562                state.keyboard_focused_window = state.windows.get(&surface.id()).cloned();
563
564                if let Some(window) = state.keyboard_focused_window.clone() {
565                    drop(state);
566                    window.set_focused(true);
567                }
568            }
569            wl_keyboard::Event::Leave { surface, .. } => {
570                let keyboard_focused_window = state.windows.get(&surface.id()).cloned();
571                state.keyboard_focused_window = None;
572
573                if let Some(window) = keyboard_focused_window {
574                    drop(state);
575                    window.set_focused(false);
576                }
577            }
578            wl_keyboard::Event::Modifiers {
579                mods_depressed,
580                mods_latched,
581                mods_locked,
582                group,
583                ..
584            } => {
585                let focused_window = state.keyboard_focused_window.clone();
586                let Some(focused_window) = focused_window else {
587                    return;
588                };
589
590                let keymap_state = state.keymap_state.as_mut().unwrap();
591                keymap_state.update_mask(mods_depressed, mods_latched, mods_locked, 0, 0, group);
592                state.modifiers = Modifiers::from_xkb(keymap_state);
593
594                let input = PlatformInput::ModifiersChanged(ModifiersChangedEvent {
595                    modifiers: state.modifiers,
596                });
597
598                drop(state);
599                focused_window.handle_input(input);
600            }
601            wl_keyboard::Event::Key {
602                key,
603                state: WEnum::Value(key_state),
604                ..
605            } => {
606                let focused_window = state.keyboard_focused_window.clone();
607                let Some(focused_window) = focused_window else {
608                    return;
609                };
610                let focused_window = focused_window.clone();
611
612                let keymap_state = state.keymap_state.as_ref().unwrap();
613                let keycode = Keycode::from(key + MIN_KEYCODE);
614                let keysym = keymap_state.key_get_one_sym(keycode);
615
616                match key_state {
617                    wl_keyboard::KeyState::Pressed if !keysym.is_modifier_key() => {
618                        let input = PlatformInput::KeyDown(KeyDownEvent {
619                            keystroke: Keystroke::from_xkb(keymap_state, state.modifiers, keycode),
620                            is_held: false, // todo(linux)
621                        });
622
623                        state.repeat.current_id += 1;
624                        state.repeat.current_keysym = Some(keysym);
625
626                        let rate = state.repeat.characters_per_second;
627                        let id = state.repeat.current_id;
628                        state
629                            .loop_handle
630                            .insert_source(Timer::from_duration(state.repeat.delay), {
631                                let input = input.clone();
632                                move |event, _metadata, client| {
633                                    let state = client.0.borrow_mut();
634                                    let is_repeating = id == state.repeat.current_id
635                                        && state.repeat.current_keysym.is_some()
636                                        && state.keyboard_focused_window.is_some();
637
638                                    if !is_repeating {
639                                        return TimeoutAction::Drop;
640                                    }
641
642                                    let focused_window =
643                                        state.keyboard_focused_window.as_ref().unwrap().clone();
644
645                                    drop(state);
646                                    focused_window.handle_input(input.clone());
647
648                                    TimeoutAction::ToDuration(Duration::from_secs(1) / rate)
649                                }
650                            })
651                            .unwrap();
652
653                        drop(state);
654                        focused_window.handle_input(input);
655                    }
656                    wl_keyboard::KeyState::Released if !keysym.is_modifier_key() => {
657                        let input = PlatformInput::KeyUp(KeyUpEvent {
658                            keystroke: Keystroke::from_xkb(keymap_state, state.modifiers, keycode),
659                        });
660
661                        state.repeat.current_keysym = None;
662
663                        drop(state);
664                        focused_window.handle_input(input);
665                    }
666                    _ => {}
667                }
668            }
669            _ => {}
670        }
671    }
672}
673
674fn linux_button_to_gpui(button: u32) -> Option<MouseButton> {
675    // These values are coming from <linux/input-event-codes.h>.
676    const BTN_LEFT: u32 = 0x110;
677    const BTN_RIGHT: u32 = 0x111;
678    const BTN_MIDDLE: u32 = 0x112;
679    const BTN_SIDE: u32 = 0x113;
680    const BTN_EXTRA: u32 = 0x114;
681    const BTN_FORWARD: u32 = 0x115;
682    const BTN_BACK: u32 = 0x116;
683
684    Some(match button {
685        BTN_LEFT => MouseButton::Left,
686        BTN_RIGHT => MouseButton::Right,
687        BTN_MIDDLE => MouseButton::Middle,
688        BTN_BACK | BTN_SIDE => MouseButton::Navigate(NavigationDirection::Back),
689        BTN_FORWARD | BTN_EXTRA => MouseButton::Navigate(NavigationDirection::Forward),
690        _ => return None,
691    })
692}
693
694impl Dispatch<wl_pointer::WlPointer, ()> for WaylandClient {
695    fn event(
696        client: &mut Self,
697        wl_pointer: &wl_pointer::WlPointer,
698        event: wl_pointer::Event,
699        data: &(),
700        conn: &Connection,
701        qh: &QueueHandle<Self>,
702    ) {
703        let mut state = client.0.borrow_mut();
704
705        match event {
706            wl_pointer::Event::Enter {
707                serial,
708                surface,
709                surface_x,
710                surface_y,
711                ..
712            } => {
713                state.mouse_location = Some(point(px(surface_x as f32), px(surface_y as f32)));
714
715                if let Some(window) = state.windows.get(&surface.id()).cloned() {
716                    state.enter_token = Some(());
717                    state.mouse_focused_window = Some(window.clone());
718                    state.cursor.set_serial_id(serial);
719                    state.cursor.set_icon(&wl_pointer, None);
720                    drop(state);
721                    window.set_focused(true);
722                }
723            }
724            wl_pointer::Event::Leave { surface, .. } => {
725                if let Some(focused_window) = state.mouse_focused_window.clone() {
726                    state.enter_token.take();
727                    let input = PlatformInput::MouseExited(MouseExitEvent {
728                        position: state.mouse_location.unwrap(),
729                        pressed_button: state.button_pressed,
730                        modifiers: state.modifiers,
731                    });
732                    state.mouse_focused_window = None;
733                    state.mouse_location = None;
734
735                    drop(state);
736                    focused_window.handle_input(input);
737                    focused_window.set_focused(false);
738                }
739            }
740            wl_pointer::Event::Motion {
741                time,
742                surface_x,
743                surface_y,
744                ..
745            } => {
746                if state.mouse_focused_window.is_none() {
747                    return;
748                }
749                state.mouse_location = Some(point(px(surface_x as f32), px(surface_y as f32)));
750                state.cursor.set_icon(&wl_pointer, None);
751
752                if let Some(window) = state.mouse_focused_window.clone() {
753                    let input = PlatformInput::MouseMove(MouseMoveEvent {
754                        position: state.mouse_location.unwrap(),
755                        pressed_button: state.button_pressed,
756                        modifiers: state.modifiers,
757                    });
758                    drop(state);
759                    window.handle_input(input);
760                }
761            }
762            wl_pointer::Event::Button {
763                button,
764                state: WEnum::Value(button_state),
765                ..
766            } => {
767                let button = linux_button_to_gpui(button);
768                let Some(button) = button else { return };
769                if state.mouse_focused_window.is_none() {
770                    return;
771                }
772                match button_state {
773                    wl_pointer::ButtonState::Pressed => {
774                        let click_elapsed = state.click.last_click.elapsed();
775
776                        if click_elapsed < DOUBLE_CLICK_INTERVAL
777                            && is_within_click_distance(
778                                state.click.last_location,
779                                state.mouse_location.unwrap(),
780                            )
781                        {
782                            state.click.current_count += 1;
783                        } else {
784                            state.click.current_count = 1;
785                        }
786
787                        state.click.last_click = Instant::now();
788                        state.click.last_location = state.mouse_location.unwrap();
789
790                        state.button_pressed = Some(button);
791
792                        if let Some(window) = state.mouse_focused_window.clone() {
793                            let input = PlatformInput::MouseDown(MouseDownEvent {
794                                button,
795                                position: state.mouse_location.unwrap(),
796                                modifiers: state.modifiers,
797                                click_count: state.click.current_count,
798                                first_mouse: state.enter_token.take().is_some(),
799                            });
800                            drop(state);
801                            window.handle_input(input);
802                        }
803                    }
804                    wl_pointer::ButtonState::Released => {
805                        state.button_pressed = None;
806
807                        if let Some(window) = state.mouse_focused_window.clone() {
808                            let input = PlatformInput::MouseUp(MouseUpEvent {
809                                button,
810                                position: state.mouse_location.unwrap(),
811                                modifiers: state.modifiers,
812                                click_count: state.click.current_count,
813                            });
814                            drop(state);
815                            window.handle_input(input);
816                        }
817                    }
818                    _ => {}
819                }
820            }
821            wl_pointer::Event::AxisRelativeDirection {
822                direction: WEnum::Value(direction),
823                ..
824            } => {
825                state.scroll_direction = match direction {
826                    AxisRelativeDirection::Identical => -1.0,
827                    AxisRelativeDirection::Inverted => 1.0,
828                    _ => -1.0,
829                }
830            }
831            wl_pointer::Event::AxisSource {
832                axis_source: WEnum::Value(axis_source),
833            } => {
834                state.axis_source = axis_source;
835            }
836            wl_pointer::Event::AxisValue120 {
837                axis: WEnum::Value(axis),
838                value120,
839            } => {
840                if let Some(focused_window) = state.mouse_focused_window.clone() {
841                    let value = value120 as f64 * state.scroll_direction;
842
843                    let input = PlatformInput::ScrollWheel(ScrollWheelEvent {
844                        position: state.mouse_location.unwrap(),
845                        delta: match axis {
846                            wl_pointer::Axis::VerticalScroll => {
847                                ScrollDelta::Pixels(point(px(0.0), px(value as f32)))
848                            }
849                            wl_pointer::Axis::HorizontalScroll => {
850                                ScrollDelta::Pixels(point(px(value as f32), px(0.0)))
851                            }
852                            _ => unimplemented!(),
853                        },
854                        modifiers: state.modifiers,
855                        touch_phase: TouchPhase::Moved,
856                    });
857                    drop(state);
858                    focused_window.handle_input(input)
859                }
860            }
861            wl_pointer::Event::Axis {
862                time,
863                axis: WEnum::Value(axis),
864                value,
865                ..
866            } => {
867                // We handle discrete scroll events with `AxisValue120`.
868                if wl_pointer.version() >= wl_pointer::EVT_AXIS_VALUE120_SINCE
869                    && state.axis_source == AxisSource::Wheel
870                {
871                    return;
872                }
873                if let Some(focused_window) = state.mouse_focused_window.clone() {
874                    let value = value * state.scroll_direction;
875
876                    let input = PlatformInput::ScrollWheel(ScrollWheelEvent {
877                        position: state.mouse_location.unwrap(),
878                        delta: match axis {
879                            wl_pointer::Axis::VerticalScroll => {
880                                ScrollDelta::Pixels(point(px(0.0), px(value as f32)))
881                            }
882                            wl_pointer::Axis::HorizontalScroll => {
883                                ScrollDelta::Pixels(point(px(value as f32), px(0.0)))
884                            }
885                            _ => unimplemented!(),
886                        },
887                        modifiers: state.modifiers,
888                        touch_phase: TouchPhase::Moved,
889                    });
890                    drop(state);
891                    focused_window.handle_input(input)
892                }
893            }
894            _ => {}
895        }
896    }
897}
898
899impl Dispatch<wp_fractional_scale_v1::WpFractionalScaleV1, ObjectId> for WaylandClient {
900    fn event(
901        state: &mut Self,
902        _: &wp_fractional_scale_v1::WpFractionalScaleV1,
903        event: <wp_fractional_scale_v1::WpFractionalScaleV1 as Proxy>::Event,
904        surface_id: &ObjectId,
905        _: &Connection,
906        _: &QueueHandle<Self>,
907    ) {
908        let mut state = state.0.borrow_mut();
909
910        let Some(window) = state.windows.get(surface_id).cloned() else {
911            return;
912        };
913
914        drop(state);
915        window.handle_fractional_scale_event(event);
916    }
917}
918
919impl Dispatch<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1, ObjectId> for WaylandClient {
920    fn event(
921        state: &mut Self,
922        _: &zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1,
923        event: zxdg_toplevel_decoration_v1::Event,
924        surface_id: &ObjectId,
925        _: &Connection,
926        _: &QueueHandle<Self>,
927    ) {
928        let mut state = state.0.borrow_mut();
929
930        let Some(window) = state.windows.get(surface_id).cloned() else {
931            return;
932        };
933
934        drop(state);
935        window.handle_toplevel_decoration_event(event);
936    }
937}