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        let cursor_icon_name = state.cursor_icon_name.clone();
705
706        match event {
707            wl_pointer::Event::Enter {
708                serial,
709                surface,
710                surface_x,
711                surface_y,
712                ..
713            } => {
714                state.mouse_location = Some(point(px(surface_x as f32), px(surface_y as f32)));
715
716                if let Some(window) = state.windows.get(&surface.id()).cloned() {
717                    state.enter_token = Some(());
718                    state.mouse_focused_window = Some(window.clone());
719                    state.cursor.set_serial_id(serial);
720                    state
721                        .cursor
722                        .set_icon(&wl_pointer, Some(cursor_icon_name.as_str()));
723                    drop(state);
724                    window.set_focused(true);
725                }
726            }
727            wl_pointer::Event::Leave { surface, .. } => {
728                if let Some(focused_window) = state.mouse_focused_window.clone() {
729                    state.enter_token.take();
730                    let input = PlatformInput::MouseExited(MouseExitEvent {
731                        position: state.mouse_location.unwrap(),
732                        pressed_button: state.button_pressed,
733                        modifiers: state.modifiers,
734                    });
735                    state.mouse_focused_window = None;
736                    state.mouse_location = None;
737
738                    drop(state);
739                    focused_window.handle_input(input);
740                    focused_window.set_focused(false);
741                }
742            }
743            wl_pointer::Event::Motion {
744                time,
745                surface_x,
746                surface_y,
747                ..
748            } => {
749                if state.mouse_focused_window.is_none() {
750                    return;
751                }
752                state.mouse_location = Some(point(px(surface_x as f32), px(surface_y as f32)));
753                state
754                    .cursor
755                    .set_icon(&wl_pointer, Some(cursor_icon_name.as_str()));
756
757                if let Some(window) = state.mouse_focused_window.clone() {
758                    let input = PlatformInput::MouseMove(MouseMoveEvent {
759                        position: state.mouse_location.unwrap(),
760                        pressed_button: state.button_pressed,
761                        modifiers: state.modifiers,
762                    });
763                    drop(state);
764                    window.handle_input(input);
765                }
766            }
767            wl_pointer::Event::Button {
768                button,
769                state: WEnum::Value(button_state),
770                ..
771            } => {
772                let button = linux_button_to_gpui(button);
773                let Some(button) = button else { return };
774                if state.mouse_focused_window.is_none() {
775                    return;
776                }
777                match button_state {
778                    wl_pointer::ButtonState::Pressed => {
779                        let click_elapsed = state.click.last_click.elapsed();
780
781                        if click_elapsed < DOUBLE_CLICK_INTERVAL
782                            && is_within_click_distance(
783                                state.click.last_location,
784                                state.mouse_location.unwrap(),
785                            )
786                        {
787                            state.click.current_count += 1;
788                        } else {
789                            state.click.current_count = 1;
790                        }
791
792                        state.click.last_click = Instant::now();
793                        state.click.last_location = state.mouse_location.unwrap();
794
795                        state.button_pressed = Some(button);
796
797                        if let Some(window) = state.mouse_focused_window.clone() {
798                            let input = PlatformInput::MouseDown(MouseDownEvent {
799                                button,
800                                position: state.mouse_location.unwrap(),
801                                modifiers: state.modifiers,
802                                click_count: state.click.current_count,
803                                first_mouse: state.enter_token.take().is_some(),
804                            });
805                            drop(state);
806                            window.handle_input(input);
807                        }
808                    }
809                    wl_pointer::ButtonState::Released => {
810                        state.button_pressed = None;
811
812                        if let Some(window) = state.mouse_focused_window.clone() {
813                            let input = PlatformInput::MouseUp(MouseUpEvent {
814                                button,
815                                position: state.mouse_location.unwrap(),
816                                modifiers: state.modifiers,
817                                click_count: state.click.current_count,
818                            });
819                            drop(state);
820                            window.handle_input(input);
821                        }
822                    }
823                    _ => {}
824                }
825            }
826            wl_pointer::Event::AxisRelativeDirection {
827                direction: WEnum::Value(direction),
828                ..
829            } => {
830                state.scroll_direction = match direction {
831                    AxisRelativeDirection::Identical => -1.0,
832                    AxisRelativeDirection::Inverted => 1.0,
833                    _ => -1.0,
834                }
835            }
836            wl_pointer::Event::AxisSource {
837                axis_source: WEnum::Value(axis_source),
838            } => {
839                state.axis_source = axis_source;
840            }
841            wl_pointer::Event::AxisValue120 {
842                axis: WEnum::Value(axis),
843                value120,
844            } => {
845                if let Some(focused_window) = state.mouse_focused_window.clone() {
846                    let value = value120 as f64 * state.scroll_direction;
847
848                    let input = PlatformInput::ScrollWheel(ScrollWheelEvent {
849                        position: state.mouse_location.unwrap(),
850                        delta: match axis {
851                            wl_pointer::Axis::VerticalScroll => {
852                                ScrollDelta::Pixels(point(px(0.0), px(value as f32)))
853                            }
854                            wl_pointer::Axis::HorizontalScroll => {
855                                ScrollDelta::Pixels(point(px(value as f32), px(0.0)))
856                            }
857                            _ => unimplemented!(),
858                        },
859                        modifiers: state.modifiers,
860                        touch_phase: TouchPhase::Moved,
861                    });
862                    drop(state);
863                    focused_window.handle_input(input)
864                }
865            }
866            wl_pointer::Event::Axis {
867                time,
868                axis: WEnum::Value(axis),
869                value,
870                ..
871            } => {
872                // We handle discrete scroll events with `AxisValue120`.
873                if wl_pointer.version() >= wl_pointer::EVT_AXIS_VALUE120_SINCE
874                    && state.axis_source == AxisSource::Wheel
875                {
876                    return;
877                }
878                if let Some(focused_window) = state.mouse_focused_window.clone() {
879                    let value = value * state.scroll_direction;
880
881                    let input = PlatformInput::ScrollWheel(ScrollWheelEvent {
882                        position: state.mouse_location.unwrap(),
883                        delta: match axis {
884                            wl_pointer::Axis::VerticalScroll => {
885                                ScrollDelta::Pixels(point(px(0.0), px(value as f32)))
886                            }
887                            wl_pointer::Axis::HorizontalScroll => {
888                                ScrollDelta::Pixels(point(px(value as f32), px(0.0)))
889                            }
890                            _ => unimplemented!(),
891                        },
892                        modifiers: state.modifiers,
893                        touch_phase: TouchPhase::Moved,
894                    });
895                    drop(state);
896                    focused_window.handle_input(input)
897                }
898            }
899            _ => {}
900        }
901    }
902}
903
904impl Dispatch<wp_fractional_scale_v1::WpFractionalScaleV1, ObjectId> for WaylandClient {
905    fn event(
906        state: &mut Self,
907        _: &wp_fractional_scale_v1::WpFractionalScaleV1,
908        event: <wp_fractional_scale_v1::WpFractionalScaleV1 as Proxy>::Event,
909        surface_id: &ObjectId,
910        _: &Connection,
911        _: &QueueHandle<Self>,
912    ) {
913        let mut state = state.0.borrow_mut();
914
915        let Some(window) = state.windows.get(surface_id).cloned() else {
916            return;
917        };
918
919        drop(state);
920        window.handle_fractional_scale_event(event);
921    }
922}
923
924impl Dispatch<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1, ObjectId> for WaylandClient {
925    fn event(
926        state: &mut Self,
927        _: &zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1,
928        event: zxdg_toplevel_decoration_v1::Event,
929        surface_id: &ObjectId,
930        _: &Connection,
931        _: &QueueHandle<Self>,
932    ) {
933        let mut state = state.0.borrow_mut();
934
935        let Some(window) = state.windows.get(surface_id).cloned() else {
936            return;
937        };
938
939        drop(state);
940        window.handle_toplevel_decoration_event(event);
941    }
942}