client.rs

   1use core::hash;
   2use std::cell::{RefCell, RefMut};
   3use std::os::fd::{AsRawFd, BorrowedFd};
   4use std::path::PathBuf;
   5use std::rc::{Rc, Weak};
   6use std::sync::Arc;
   7use std::time::{Duration, Instant};
   8
   9use async_task::Runnable;
  10use calloop::timer::{TimeoutAction, Timer};
  11use calloop::{EventLoop, LoopHandle};
  12use calloop_wayland_source::WaylandSource;
  13use collections::HashMap;
  14use copypasta::wayland_clipboard::{create_clipboards_from_external, Clipboard, Primary};
  15use copypasta::ClipboardProvider;
  16use filedescriptor::Pipe;
  17use smallvec::SmallVec;
  18use util::ResultExt;
  19use wayland_backend::client::ObjectId;
  20use wayland_backend::protocol::WEnum;
  21use wayland_client::event_created_child;
  22use wayland_client::globals::{registry_queue_init, GlobalList, GlobalListContents};
  23use wayland_client::protocol::wl_callback::{self, WlCallback};
  24use wayland_client::protocol::wl_data_device_manager::DndAction;
  25use wayland_client::protocol::wl_pointer::{AxisRelativeDirection, AxisSource};
  26use wayland_client::protocol::{
  27    wl_data_device, wl_data_device_manager, wl_data_offer, wl_data_source, wl_output,
  28};
  29use wayland_client::{
  30    delegate_noop,
  31    protocol::{
  32        wl_buffer, wl_compositor, wl_keyboard, wl_pointer, wl_registry, wl_seat, wl_shm,
  33        wl_shm_pool, wl_surface,
  34    },
  35    Connection, Dispatch, Proxy, QueueHandle,
  36};
  37use wayland_protocols::wp::fractional_scale::v1::client::{
  38    wp_fractional_scale_manager_v1, wp_fractional_scale_v1,
  39};
  40use wayland_protocols::wp::viewporter::client::{wp_viewport, wp_viewporter};
  41use wayland_protocols::xdg::decoration::zv1::client::{
  42    zxdg_decoration_manager_v1, zxdg_toplevel_decoration_v1,
  43};
  44use wayland_protocols::xdg::shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base};
  45use xkbcommon::xkb::ffi::XKB_KEYMAP_FORMAT_TEXT_V1;
  46use xkbcommon::xkb::{self, Keycode, KEYMAP_COMPILE_NO_FLAGS};
  47
  48use super::super::{read_fd, DOUBLE_CLICK_INTERVAL};
  49use super::window::{WaylandWindowState, WaylandWindowStatePtr};
  50use crate::platform::linux::is_within_click_distance;
  51use crate::platform::linux::wayland::cursor::Cursor;
  52use crate::platform::linux::wayland::window::WaylandWindow;
  53use crate::platform::linux::LinuxClient;
  54use crate::platform::PlatformWindow;
  55use crate::{point, px, FileDropEvent, ForegroundExecutor, MouseExitEvent};
  56use crate::{
  57    AnyWindowHandle, CursorStyle, DisplayId, KeyDownEvent, KeyUpEvent, Keystroke, Modifiers,
  58    ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
  59    NavigationDirection, Pixels, PlatformDisplay, PlatformInput, Point, ScrollDelta,
  60    ScrollWheelEvent, TouchPhase,
  61};
  62use crate::{LinuxCommon, WindowParams};
  63
  64/// Used to convert evdev scancode to xkb scancode
  65const MIN_KEYCODE: u32 = 8;
  66
  67#[derive(Clone)]
  68pub struct Globals {
  69    pub qh: QueueHandle<WaylandClientStatePtr>,
  70    pub compositor: wl_compositor::WlCompositor,
  71    pub data_device_manager: Option<wl_data_device_manager::WlDataDeviceManager>,
  72    pub wm_base: xdg_wm_base::XdgWmBase,
  73    pub shm: wl_shm::WlShm,
  74    pub viewporter: Option<wp_viewporter::WpViewporter>,
  75    pub fractional_scale_manager:
  76        Option<wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1>,
  77    pub decoration_manager: Option<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1>,
  78    pub executor: ForegroundExecutor,
  79}
  80
  81impl Globals {
  82    fn new(
  83        globals: GlobalList,
  84        executor: ForegroundExecutor,
  85        qh: QueueHandle<WaylandClientStatePtr>,
  86    ) -> Self {
  87        Globals {
  88            compositor: globals
  89                .bind(
  90                    &qh,
  91                    wl_surface::REQ_SET_BUFFER_SCALE_SINCE
  92                        ..=wl_surface::EVT_PREFERRED_BUFFER_SCALE_SINCE,
  93                    (),
  94                )
  95                .unwrap(),
  96            data_device_manager: globals
  97                .bind(
  98                    &qh,
  99                    WL_DATA_DEVICE_MANAGER_VERSION..=WL_DATA_DEVICE_MANAGER_VERSION,
 100                    (),
 101                )
 102                .ok(),
 103            shm: globals.bind(&qh, 1..=1, ()).unwrap(),
 104            wm_base: globals.bind(&qh, 1..=1, ()).unwrap(),
 105            viewporter: globals.bind(&qh, 1..=1, ()).ok(),
 106            fractional_scale_manager: globals.bind(&qh, 1..=1, ()).ok(),
 107            decoration_manager: globals.bind(&qh, 1..=1, ()).ok(),
 108            executor,
 109            qh,
 110        }
 111    }
 112}
 113
 114pub(crate) struct WaylandClientState {
 115    serial: u32,
 116    globals: Globals,
 117    wl_pointer: Option<wl_pointer::WlPointer>,
 118    data_device: Option<wl_data_device::WlDataDevice>,
 119    // Surface to Window mapping
 120    windows: HashMap<ObjectId, WaylandWindowStatePtr>,
 121    // Output to scale mapping
 122    output_scales: HashMap<ObjectId, i32>,
 123    keymap_state: Option<xkb::State>,
 124    drag: DragState,
 125    click: ClickState,
 126    repeat: KeyRepeat,
 127    modifiers: Modifiers,
 128    axis_source: AxisSource,
 129    mouse_location: Option<Point<Pixels>>,
 130    continuous_scroll_delta: Option<Point<Pixels>>,
 131    discrete_scroll_delta: Option<Point<f32>>,
 132    vertical_modifier: f32,
 133    horizontal_modifier: f32,
 134    scroll_event_received: bool,
 135    enter_token: Option<()>,
 136    button_pressed: Option<MouseButton>,
 137    mouse_focused_window: Option<WaylandWindowStatePtr>,
 138    keyboard_focused_window: Option<WaylandWindowStatePtr>,
 139    loop_handle: LoopHandle<'static, WaylandClientStatePtr>,
 140    cursor_icon_name: String,
 141    cursor: Cursor,
 142    clipboard: Option<Clipboard>,
 143    primary: Option<Primary>,
 144    event_loop: Option<EventLoop<'static, WaylandClientStatePtr>>,
 145    common: LinuxCommon,
 146}
 147
 148pub struct DragState {
 149    data_offer: Option<wl_data_offer::WlDataOffer>,
 150    window: Option<WaylandWindowStatePtr>,
 151    position: Point<Pixels>,
 152}
 153
 154pub struct ClickState {
 155    last_click: Instant,
 156    last_location: Point<Pixels>,
 157    current_count: usize,
 158}
 159
 160pub(crate) struct KeyRepeat {
 161    characters_per_second: u32,
 162    delay: Duration,
 163    current_id: u64,
 164    current_keysym: Option<xkb::Keysym>,
 165}
 166
 167/// This struct is required to conform to Rust's orphan rules, so we can dispatch on the state but hand the
 168/// window to GPUI.
 169#[derive(Clone)]
 170pub struct WaylandClientStatePtr(Weak<RefCell<WaylandClientState>>);
 171
 172impl WaylandClientStatePtr {
 173    fn get_client(&self) -> Rc<RefCell<WaylandClientState>> {
 174        self.0
 175            .upgrade()
 176            .expect("The pointer should always be valid when dispatching in wayland")
 177    }
 178
 179    pub fn drop_window(&self, surface_id: &ObjectId) {
 180        let mut client = self.get_client();
 181        let mut state = client.borrow_mut();
 182        let closed_window = state.windows.remove(surface_id).unwrap();
 183        if let Some(window) = state.mouse_focused_window.take() {
 184            if !window.ptr_eq(&closed_window) {
 185                state.mouse_focused_window = Some(window);
 186            }
 187        }
 188        if let Some(window) = state.keyboard_focused_window.take() {
 189            if !window.ptr_eq(&closed_window) {
 190                state.mouse_focused_window = Some(window);
 191            }
 192        }
 193        if state.windows.is_empty() {
 194            // Drop the clipboard to prevent a seg fault after we've closed all Wayland connections.
 195            state.clipboard = None;
 196            state.primary = None;
 197            if let Some(wl_pointer) = &state.wl_pointer {
 198                wl_pointer.release();
 199            }
 200            if let Some(data_device) = &state.data_device {
 201                data_device.release();
 202            }
 203            state.common.signal.stop();
 204        }
 205    }
 206}
 207
 208#[derive(Clone)]
 209pub struct WaylandClient(Rc<RefCell<WaylandClientState>>);
 210
 211const WL_DATA_DEVICE_MANAGER_VERSION: u32 = 3;
 212const WL_OUTPUT_VERSION: u32 = 2;
 213
 214fn wl_seat_version(version: u32) -> u32 {
 215    // We rely on the wl_pointer.frame event
 216    const WL_SEAT_MIN_VERSION: u32 = 5;
 217    const WL_SEAT_MAX_VERSION: u32 = 9;
 218
 219    if version < WL_SEAT_MIN_VERSION {
 220        panic!(
 221            "wl_seat below required version: {} < {}",
 222            version, WL_SEAT_MIN_VERSION
 223        );
 224    }
 225
 226    version.clamp(WL_SEAT_MIN_VERSION, WL_SEAT_MAX_VERSION)
 227}
 228
 229impl WaylandClient {
 230    pub(crate) fn new() -> Self {
 231        let conn = Connection::connect_to_env().unwrap();
 232
 233        let (globals, mut event_queue) =
 234            registry_queue_init::<WaylandClientStatePtr>(&conn).unwrap();
 235        let qh = event_queue.handle();
 236
 237        let mut seat: Option<wl_seat::WlSeat> = None;
 238        let mut outputs = HashMap::default();
 239        globals.contents().with_list(|list| {
 240            for global in list {
 241                match &global.interface[..] {
 242                    "wl_seat" => {
 243                        // TODO: multi-seat support
 244                        seat = Some(globals.registry().bind::<wl_seat::WlSeat, _, _>(
 245                            global.name,
 246                            wl_seat_version(global.version),
 247                            &qh,
 248                            (),
 249                        ));
 250                    }
 251                    "wl_output" => {
 252                        let output = globals.registry().bind::<wl_output::WlOutput, _, _>(
 253                            global.name,
 254                            WL_OUTPUT_VERSION,
 255                            &qh,
 256                            (),
 257                        );
 258                        outputs.insert(output.id(), 1);
 259                    }
 260                    _ => {}
 261                }
 262            }
 263        });
 264
 265        let display = conn.backend().display_ptr() as *mut std::ffi::c_void;
 266
 267        let event_loop = EventLoop::<WaylandClientStatePtr>::try_new().unwrap();
 268
 269        let (common, main_receiver) = LinuxCommon::new(event_loop.get_signal());
 270
 271        let handle = event_loop.handle();
 272        handle.insert_source(main_receiver, |event, _, _: &mut WaylandClientStatePtr| {
 273            if let calloop::channel::Event::Msg(runnable) = event {
 274                runnable.run();
 275            }
 276        });
 277
 278        let seat = seat.unwrap();
 279        let globals = Globals::new(globals, common.foreground_executor.clone(), qh.clone());
 280
 281        let data_device = globals
 282            .data_device_manager
 283            .as_ref()
 284            .map(|data_device_manager| data_device_manager.get_data_device(&seat, &qh, ()));
 285
 286        let (primary, clipboard) = unsafe { create_clipboards_from_external(display) };
 287
 288        let cursor = Cursor::new(&conn, &globals, 24);
 289
 290        let mut state = Rc::new(RefCell::new(WaylandClientState {
 291            serial: 0,
 292            globals,
 293            wl_pointer: None,
 294            data_device,
 295            output_scales: outputs,
 296            windows: HashMap::default(),
 297            common,
 298            keymap_state: None,
 299            drag: DragState {
 300                data_offer: None,
 301                window: None,
 302                position: Point::default(),
 303            },
 304            click: ClickState {
 305                last_click: Instant::now(),
 306                last_location: Point::default(),
 307                current_count: 0,
 308            },
 309            repeat: KeyRepeat {
 310                characters_per_second: 16,
 311                delay: Duration::from_millis(500),
 312                current_id: 0,
 313                current_keysym: None,
 314            },
 315            modifiers: Modifiers {
 316                shift: false,
 317                control: false,
 318                alt: false,
 319                function: false,
 320                platform: false,
 321            },
 322            scroll_event_received: false,
 323            axis_source: AxisSource::Wheel,
 324            mouse_location: None,
 325            continuous_scroll_delta: None,
 326            discrete_scroll_delta: None,
 327            vertical_modifier: -1.0,
 328            horizontal_modifier: -1.0,
 329            button_pressed: None,
 330            mouse_focused_window: None,
 331            keyboard_focused_window: None,
 332            loop_handle: handle.clone(),
 333            cursor_icon_name: "arrow".to_string(),
 334            enter_token: None,
 335            cursor,
 336            clipboard: Some(clipboard),
 337            primary: Some(primary),
 338            event_loop: Some(event_loop),
 339        }));
 340
 341        WaylandSource::new(conn, event_queue).insert(handle);
 342
 343        Self(state)
 344    }
 345}
 346
 347impl LinuxClient for WaylandClient {
 348    fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
 349        Vec::new()
 350    }
 351
 352    fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
 353        unimplemented!()
 354    }
 355
 356    fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>> {
 357        None
 358    }
 359
 360    fn open_window(
 361        &self,
 362        handle: AnyWindowHandle,
 363        params: WindowParams,
 364    ) -> Box<dyn PlatformWindow> {
 365        let mut state = self.0.borrow_mut();
 366
 367        let (window, surface_id) = WaylandWindow::new(
 368            state.globals.clone(),
 369            WaylandClientStatePtr(Rc::downgrade(&self.0)),
 370            params,
 371        );
 372        state.windows.insert(surface_id, window.0.clone());
 373
 374        Box::new(window)
 375    }
 376
 377    fn set_cursor_style(&self, style: CursorStyle) {
 378        // Based on cursor names from https://gitlab.gnome.org/GNOME/adwaita-icon-theme (GNOME)
 379        // and https://github.com/KDE/breeze (KDE). Both of them seem to be also derived from
 380        // Web CSS cursor names: https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#values
 381        let cursor_icon_name = match style {
 382            CursorStyle::Arrow => "arrow",
 383            CursorStyle::IBeam => "text",
 384            CursorStyle::Crosshair => "crosshair",
 385            CursorStyle::ClosedHand => "grabbing",
 386            CursorStyle::OpenHand => "grab",
 387            CursorStyle::PointingHand => "pointer",
 388            CursorStyle::ResizeLeft => "w-resize",
 389            CursorStyle::ResizeRight => "e-resize",
 390            CursorStyle::ResizeLeftRight => "ew-resize",
 391            CursorStyle::ResizeUp => "n-resize",
 392            CursorStyle::ResizeDown => "s-resize",
 393            CursorStyle::ResizeUpDown => "ns-resize",
 394            CursorStyle::DisappearingItem => "grabbing", // todo(linux) - couldn't find equivalent icon in linux
 395            CursorStyle::IBeamCursorForVerticalLayout => "vertical-text",
 396            CursorStyle::OperationNotAllowed => "not-allowed",
 397            CursorStyle::DragLink => "alias",
 398            CursorStyle::DragCopy => "copy",
 399            CursorStyle::ContextualMenu => "context-menu",
 400        }
 401        .to_string();
 402
 403        let mut state = self.0.borrow_mut();
 404        state.cursor_icon_name = cursor_icon_name.clone();
 405        if state.mouse_focused_window.is_some() {
 406            let wl_pointer = state
 407                .wl_pointer
 408                .clone()
 409                .expect("window is focused by pointer");
 410            state.cursor.set_icon(&wl_pointer, &cursor_icon_name);
 411        }
 412    }
 413
 414    fn with_common<R>(&self, f: impl FnOnce(&mut LinuxCommon) -> R) -> R {
 415        f(&mut self.0.borrow_mut().common)
 416    }
 417
 418    fn run(&self) {
 419        let mut event_loop = self
 420            .0
 421            .borrow_mut()
 422            .event_loop
 423            .take()
 424            .expect("App is already running");
 425
 426        event_loop
 427            .run(
 428                None,
 429                &mut WaylandClientStatePtr(Rc::downgrade(&self.0)),
 430                |_| {},
 431            )
 432            .log_err();
 433    }
 434
 435    fn write_to_primary(&self, item: crate::ClipboardItem) {
 436        self.0
 437            .borrow_mut()
 438            .primary
 439            .as_mut()
 440            .unwrap()
 441            .set_contents(item.text);
 442    }
 443
 444    fn write_to_clipboard(&self, item: crate::ClipboardItem) {
 445        self.0
 446            .borrow_mut()
 447            .clipboard
 448            .as_mut()
 449            .unwrap()
 450            .set_contents(item.text);
 451    }
 452
 453    fn read_from_primary(&self) -> Option<crate::ClipboardItem> {
 454        self.0
 455            .borrow_mut()
 456            .primary
 457            .as_mut()
 458            .unwrap()
 459            .get_contents()
 460            .ok()
 461            .map(|s| crate::ClipboardItem {
 462                text: s,
 463                metadata: None,
 464            })
 465    }
 466
 467    fn read_from_clipboard(&self) -> Option<crate::ClipboardItem> {
 468        self.0
 469            .borrow_mut()
 470            .clipboard
 471            .as_mut()
 472            .unwrap()
 473            .get_contents()
 474            .ok()
 475            .map(|s| crate::ClipboardItem {
 476                text: s,
 477                metadata: None,
 478            })
 479    }
 480}
 481
 482impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for WaylandClientStatePtr {
 483    fn event(
 484        this: &mut Self,
 485        registry: &wl_registry::WlRegistry,
 486        event: wl_registry::Event,
 487        _: &GlobalListContents,
 488        _: &Connection,
 489        qh: &QueueHandle<Self>,
 490    ) {
 491        let mut client = this.get_client();
 492        let mut state = client.borrow_mut();
 493
 494        match event {
 495            wl_registry::Event::Global {
 496                name,
 497                interface,
 498                version,
 499            } => match &interface[..] {
 500                "wl_seat" => {
 501                    state.wl_pointer = None;
 502                    registry.bind::<wl_seat::WlSeat, _, _>(name, wl_seat_version(version), qh, ());
 503                }
 504                "wl_output" => {
 505                    let output =
 506                        registry.bind::<wl_output::WlOutput, _, _>(name, WL_OUTPUT_VERSION, qh, ());
 507
 508                    state.output_scales.insert(output.id(), 1);
 509                }
 510                _ => {}
 511            },
 512            wl_registry::Event::GlobalRemove { name: _ } => {}
 513            _ => {}
 514        }
 515    }
 516}
 517
 518delegate_noop!(WaylandClientStatePtr: ignore wl_compositor::WlCompositor);
 519delegate_noop!(WaylandClientStatePtr: ignore wl_data_device_manager::WlDataDeviceManager);
 520delegate_noop!(WaylandClientStatePtr: ignore wl_shm::WlShm);
 521delegate_noop!(WaylandClientStatePtr: ignore wl_shm_pool::WlShmPool);
 522delegate_noop!(WaylandClientStatePtr: ignore wl_buffer::WlBuffer);
 523delegate_noop!(WaylandClientStatePtr: ignore wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1);
 524delegate_noop!(WaylandClientStatePtr: ignore zxdg_decoration_manager_v1::ZxdgDecorationManagerV1);
 525delegate_noop!(WaylandClientStatePtr: ignore wp_viewporter::WpViewporter);
 526delegate_noop!(WaylandClientStatePtr: ignore wp_viewport::WpViewport);
 527
 528impl Dispatch<WlCallback, ObjectId> for WaylandClientStatePtr {
 529    fn event(
 530        state: &mut WaylandClientStatePtr,
 531        _: &wl_callback::WlCallback,
 532        event: wl_callback::Event,
 533        surface_id: &ObjectId,
 534        _: &Connection,
 535        qh: &QueueHandle<Self>,
 536    ) {
 537        let client = state.get_client();
 538        let mut state = client.borrow_mut();
 539        let Some(window) = get_window(&mut state, surface_id) else {
 540            return;
 541        };
 542        drop(state);
 543
 544        match event {
 545            wl_callback::Event::Done { callback_data } => {
 546                window.frame(true);
 547            }
 548            _ => {}
 549        }
 550    }
 551}
 552
 553fn get_window(
 554    mut state: &mut RefMut<WaylandClientState>,
 555    surface_id: &ObjectId,
 556) -> Option<WaylandWindowStatePtr> {
 557    state.windows.get(surface_id).cloned()
 558}
 559
 560impl Dispatch<wl_surface::WlSurface, ()> for WaylandClientStatePtr {
 561    fn event(
 562        this: &mut Self,
 563        surface: &wl_surface::WlSurface,
 564        event: <wl_surface::WlSurface as Proxy>::Event,
 565        _: &(),
 566        _: &Connection,
 567        _: &QueueHandle<Self>,
 568    ) {
 569        let mut client = this.get_client();
 570        let mut state = client.borrow_mut();
 571
 572        let Some(window) = get_window(&mut state, &surface.id()) else {
 573            return;
 574        };
 575        let scales = state.output_scales.clone();
 576        drop(state);
 577
 578        window.handle_surface_event(event, scales);
 579    }
 580}
 581
 582impl Dispatch<wl_output::WlOutput, ()> for WaylandClientStatePtr {
 583    fn event(
 584        this: &mut Self,
 585        output: &wl_output::WlOutput,
 586        event: <wl_output::WlOutput as Proxy>::Event,
 587        _: &(),
 588        _: &Connection,
 589        _: &QueueHandle<Self>,
 590    ) {
 591        let mut client = this.get_client();
 592        let mut state = client.borrow_mut();
 593
 594        let Some(mut output_scale) = state.output_scales.get_mut(&output.id()) else {
 595            return;
 596        };
 597
 598        match event {
 599            wl_output::Event::Scale { factor } => {
 600                *output_scale = factor;
 601            }
 602            _ => {}
 603        }
 604    }
 605}
 606
 607impl Dispatch<xdg_surface::XdgSurface, ObjectId> for WaylandClientStatePtr {
 608    fn event(
 609        state: &mut Self,
 610        xdg_surface: &xdg_surface::XdgSurface,
 611        event: xdg_surface::Event,
 612        surface_id: &ObjectId,
 613        _: &Connection,
 614        _: &QueueHandle<Self>,
 615    ) {
 616        let client = state.get_client();
 617        let mut state = client.borrow_mut();
 618        let Some(window) = get_window(&mut state, surface_id) else {
 619            return;
 620        };
 621        drop(state);
 622        window.handle_xdg_surface_event(event);
 623    }
 624}
 625
 626impl Dispatch<xdg_toplevel::XdgToplevel, ObjectId> for WaylandClientStatePtr {
 627    fn event(
 628        this: &mut Self,
 629        xdg_toplevel: &xdg_toplevel::XdgToplevel,
 630        event: <xdg_toplevel::XdgToplevel as Proxy>::Event,
 631        surface_id: &ObjectId,
 632        _: &Connection,
 633        _: &QueueHandle<Self>,
 634    ) {
 635        let client = this.get_client();
 636        let mut state = client.borrow_mut();
 637        let Some(window) = get_window(&mut state, surface_id) else {
 638            return;
 639        };
 640
 641        drop(state);
 642        let should_close = window.handle_toplevel_event(event);
 643
 644        if should_close {
 645            this.drop_window(surface_id);
 646        }
 647    }
 648}
 649
 650impl Dispatch<xdg_wm_base::XdgWmBase, ()> for WaylandClientStatePtr {
 651    fn event(
 652        this: &mut Self,
 653        wm_base: &xdg_wm_base::XdgWmBase,
 654        event: <xdg_wm_base::XdgWmBase as Proxy>::Event,
 655        _: &(),
 656        _: &Connection,
 657        _: &QueueHandle<Self>,
 658    ) {
 659        if let xdg_wm_base::Event::Ping { serial } = event {
 660            let client = this.get_client();
 661            let mut state = client.borrow_mut();
 662            state.serial = serial;
 663            wm_base.pong(serial);
 664        }
 665    }
 666}
 667
 668impl Dispatch<wl_seat::WlSeat, ()> for WaylandClientStatePtr {
 669    fn event(
 670        state: &mut Self,
 671        seat: &wl_seat::WlSeat,
 672        event: wl_seat::Event,
 673        data: &(),
 674        conn: &Connection,
 675        qh: &QueueHandle<Self>,
 676    ) {
 677        if let wl_seat::Event::Capabilities {
 678            capabilities: WEnum::Value(capabilities),
 679        } = event
 680        {
 681            if capabilities.contains(wl_seat::Capability::Keyboard) {
 682                seat.get_keyboard(qh, ());
 683            }
 684            if capabilities.contains(wl_seat::Capability::Pointer) {
 685                let client = state.get_client();
 686                let mut state = client.borrow_mut();
 687                state.wl_pointer = Some(seat.get_pointer(qh, ()));
 688            }
 689        }
 690    }
 691}
 692
 693impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClientStatePtr {
 694    fn event(
 695        this: &mut Self,
 696        keyboard: &wl_keyboard::WlKeyboard,
 697        event: wl_keyboard::Event,
 698        data: &(),
 699        conn: &Connection,
 700        qh: &QueueHandle<Self>,
 701    ) {
 702        let mut client = this.get_client();
 703        let mut state = client.borrow_mut();
 704        match event {
 705            wl_keyboard::Event::RepeatInfo { rate, delay } => {
 706                state.repeat.characters_per_second = rate as u32;
 707                state.repeat.delay = Duration::from_millis(delay as u64);
 708            }
 709            wl_keyboard::Event::Keymap {
 710                format: WEnum::Value(format),
 711                fd,
 712                size,
 713                ..
 714            } => {
 715                assert_eq!(
 716                    format,
 717                    wl_keyboard::KeymapFormat::XkbV1,
 718                    "Unsupported keymap format"
 719                );
 720                let keymap = unsafe {
 721                    xkb::Keymap::new_from_fd(
 722                        &xkb::Context::new(xkb::CONTEXT_NO_FLAGS),
 723                        fd,
 724                        size as usize,
 725                        XKB_KEYMAP_FORMAT_TEXT_V1,
 726                        KEYMAP_COMPILE_NO_FLAGS,
 727                    )
 728                    .log_err()
 729                    .flatten()
 730                    .expect("Failed to create keymap")
 731                };
 732                state.keymap_state = Some(xkb::State::new(&keymap));
 733            }
 734            wl_keyboard::Event::Enter {
 735                serial, surface, ..
 736            } => {
 737                state.serial = serial;
 738                state.keyboard_focused_window = get_window(&mut state, &surface.id());
 739
 740                if let Some(window) = state.keyboard_focused_window.clone() {
 741                    drop(state);
 742                    window.set_focused(true);
 743                }
 744            }
 745            wl_keyboard::Event::Leave {
 746                serial, surface, ..
 747            } => {
 748                state.serial = serial;
 749                let keyboard_focused_window = get_window(&mut state, &surface.id());
 750                state.keyboard_focused_window = None;
 751
 752                if let Some(window) = keyboard_focused_window {
 753                    drop(state);
 754                    window.set_focused(false);
 755                }
 756            }
 757            wl_keyboard::Event::Modifiers {
 758                serial,
 759                mods_depressed,
 760                mods_latched,
 761                mods_locked,
 762                group,
 763                ..
 764            } => {
 765                state.serial = serial;
 766                let focused_window = state.keyboard_focused_window.clone();
 767                let Some(focused_window) = focused_window else {
 768                    return;
 769                };
 770
 771                let keymap_state = state.keymap_state.as_mut().unwrap();
 772                keymap_state.update_mask(mods_depressed, mods_latched, mods_locked, 0, 0, group);
 773                state.modifiers = Modifiers::from_xkb(keymap_state);
 774
 775                let input = PlatformInput::ModifiersChanged(ModifiersChangedEvent {
 776                    modifiers: state.modifiers,
 777                });
 778
 779                drop(state);
 780                focused_window.handle_input(input);
 781            }
 782            wl_keyboard::Event::Key {
 783                key,
 784                state: WEnum::Value(key_state),
 785                serial,
 786                ..
 787            } => {
 788                state.serial = serial;
 789
 790                let focused_window = state.keyboard_focused_window.clone();
 791                let Some(focused_window) = focused_window else {
 792                    return;
 793                };
 794                let focused_window = focused_window.clone();
 795
 796                let keymap_state = state.keymap_state.as_ref().unwrap();
 797                let keycode = Keycode::from(key + MIN_KEYCODE);
 798                let keysym = keymap_state.key_get_one_sym(keycode);
 799
 800                match key_state {
 801                    wl_keyboard::KeyState::Pressed if !keysym.is_modifier_key() => {
 802                        let input = PlatformInput::KeyDown(KeyDownEvent {
 803                            keystroke: Keystroke::from_xkb(keymap_state, state.modifiers, keycode),
 804                            is_held: false, // todo(linux)
 805                        });
 806
 807                        state.repeat.current_id += 1;
 808                        state.repeat.current_keysym = Some(keysym);
 809
 810                        let rate = state.repeat.characters_per_second;
 811                        let id = state.repeat.current_id;
 812                        state
 813                            .loop_handle
 814                            .insert_source(Timer::from_duration(state.repeat.delay), {
 815                                let input = input.clone();
 816                                move |event, _metadata, this| {
 817                                    let mut client = this.get_client();
 818                                    let mut state = client.borrow_mut();
 819                                    let is_repeating = id == state.repeat.current_id
 820                                        && state.repeat.current_keysym.is_some()
 821                                        && state.keyboard_focused_window.is_some();
 822
 823                                    if !is_repeating {
 824                                        return TimeoutAction::Drop;
 825                                    }
 826
 827                                    let focused_window =
 828                                        state.keyboard_focused_window.as_ref().unwrap().clone();
 829
 830                                    drop(state);
 831                                    focused_window.handle_input(input.clone());
 832
 833                                    TimeoutAction::ToDuration(Duration::from_secs(1) / rate)
 834                                }
 835                            })
 836                            .unwrap();
 837
 838                        drop(state);
 839                        focused_window.handle_input(input);
 840                    }
 841                    wl_keyboard::KeyState::Released if !keysym.is_modifier_key() => {
 842                        let input = PlatformInput::KeyUp(KeyUpEvent {
 843                            keystroke: Keystroke::from_xkb(keymap_state, state.modifiers, keycode),
 844                        });
 845
 846                        state.repeat.current_keysym = None;
 847
 848                        drop(state);
 849                        focused_window.handle_input(input);
 850                    }
 851                    _ => {}
 852                }
 853            }
 854            _ => {}
 855        }
 856    }
 857}
 858
 859fn linux_button_to_gpui(button: u32) -> Option<MouseButton> {
 860    // These values are coming from <linux/input-event-codes.h>.
 861    const BTN_LEFT: u32 = 0x110;
 862    const BTN_RIGHT: u32 = 0x111;
 863    const BTN_MIDDLE: u32 = 0x112;
 864    const BTN_SIDE: u32 = 0x113;
 865    const BTN_EXTRA: u32 = 0x114;
 866    const BTN_FORWARD: u32 = 0x115;
 867    const BTN_BACK: u32 = 0x116;
 868
 869    Some(match button {
 870        BTN_LEFT => MouseButton::Left,
 871        BTN_RIGHT => MouseButton::Right,
 872        BTN_MIDDLE => MouseButton::Middle,
 873        BTN_BACK | BTN_SIDE => MouseButton::Navigate(NavigationDirection::Back),
 874        BTN_FORWARD | BTN_EXTRA => MouseButton::Navigate(NavigationDirection::Forward),
 875        _ => return None,
 876    })
 877}
 878
 879impl Dispatch<wl_pointer::WlPointer, ()> for WaylandClientStatePtr {
 880    fn event(
 881        this: &mut Self,
 882        wl_pointer: &wl_pointer::WlPointer,
 883        event: wl_pointer::Event,
 884        data: &(),
 885        conn: &Connection,
 886        qh: &QueueHandle<Self>,
 887    ) {
 888        let mut client = this.get_client();
 889        let mut state = client.borrow_mut();
 890        let cursor_icon_name = state.cursor_icon_name.clone();
 891
 892        match event {
 893            wl_pointer::Event::Enter {
 894                serial,
 895                surface,
 896                surface_x,
 897                surface_y,
 898                ..
 899            } => {
 900                state.serial = serial;
 901                state.mouse_location = Some(point(px(surface_x as f32), px(surface_y as f32)));
 902
 903                if let Some(window) = get_window(&mut state, &surface.id()) {
 904                    state.enter_token = Some(());
 905                    state.mouse_focused_window = Some(window.clone());
 906                    state.cursor.mark_dirty();
 907                    state.cursor.set_serial_id(serial);
 908                    state
 909                        .cursor
 910                        .set_icon(&wl_pointer, cursor_icon_name.as_str());
 911                    drop(state);
 912                    window.set_focused(true);
 913                }
 914            }
 915            wl_pointer::Event::Leave { surface, .. } => {
 916                if let Some(focused_window) = state.mouse_focused_window.clone() {
 917                    state.enter_token.take();
 918                    let input = PlatformInput::MouseExited(MouseExitEvent {
 919                        position: state.mouse_location.unwrap(),
 920                        pressed_button: state.button_pressed,
 921                        modifiers: state.modifiers,
 922                    });
 923                    state.mouse_focused_window = None;
 924                    state.mouse_location = None;
 925
 926                    drop(state);
 927                    focused_window.handle_input(input);
 928                    focused_window.set_focused(false);
 929                }
 930            }
 931            wl_pointer::Event::Motion {
 932                time,
 933                surface_x,
 934                surface_y,
 935                ..
 936            } => {
 937                if state.mouse_focused_window.is_none() {
 938                    return;
 939                }
 940                state.mouse_location = Some(point(px(surface_x as f32), px(surface_y as f32)));
 941
 942                if let Some(window) = state.mouse_focused_window.clone() {
 943                    let input = PlatformInput::MouseMove(MouseMoveEvent {
 944                        position: state.mouse_location.unwrap(),
 945                        pressed_button: state.button_pressed,
 946                        modifiers: state.modifiers,
 947                    });
 948                    drop(state);
 949                    window.handle_input(input);
 950                }
 951            }
 952            wl_pointer::Event::Button {
 953                serial,
 954                button,
 955                state: WEnum::Value(button_state),
 956                ..
 957            } => {
 958                state.serial = serial;
 959                let button = linux_button_to_gpui(button);
 960                let Some(button) = button else { return };
 961                if state.mouse_focused_window.is_none() {
 962                    return;
 963                }
 964                match button_state {
 965                    wl_pointer::ButtonState::Pressed => {
 966                        let click_elapsed = state.click.last_click.elapsed();
 967
 968                        if click_elapsed < DOUBLE_CLICK_INTERVAL
 969                            && is_within_click_distance(
 970                                state.click.last_location,
 971                                state.mouse_location.unwrap(),
 972                            )
 973                        {
 974                            state.click.current_count += 1;
 975                        } else {
 976                            state.click.current_count = 1;
 977                        }
 978
 979                        state.click.last_click = Instant::now();
 980                        state.click.last_location = state.mouse_location.unwrap();
 981
 982                        state.button_pressed = Some(button);
 983
 984                        if let Some(window) = state.mouse_focused_window.clone() {
 985                            let input = PlatformInput::MouseDown(MouseDownEvent {
 986                                button,
 987                                position: state.mouse_location.unwrap(),
 988                                modifiers: state.modifiers,
 989                                click_count: state.click.current_count,
 990                                first_mouse: state.enter_token.take().is_some(),
 991                            });
 992                            drop(state);
 993                            window.handle_input(input);
 994                        }
 995                    }
 996                    wl_pointer::ButtonState::Released => {
 997                        state.button_pressed = None;
 998
 999                        if let Some(window) = state.mouse_focused_window.clone() {
1000                            let input = PlatformInput::MouseUp(MouseUpEvent {
1001                                button,
1002                                position: state.mouse_location.unwrap(),
1003                                modifiers: state.modifiers,
1004                                click_count: state.click.current_count,
1005                            });
1006                            drop(state);
1007                            window.handle_input(input);
1008                        }
1009                    }
1010                    _ => {}
1011                }
1012            }
1013
1014            // Axis Events
1015            wl_pointer::Event::AxisSource {
1016                axis_source: WEnum::Value(axis_source),
1017            } => {
1018                state.axis_source = axis_source;
1019            }
1020            wl_pointer::Event::Axis {
1021                time,
1022                axis: WEnum::Value(axis),
1023                value,
1024                ..
1025            } => {
1026                let axis_source = state.axis_source;
1027                let axis_modifier = match axis {
1028                    wl_pointer::Axis::VerticalScroll => state.vertical_modifier,
1029                    wl_pointer::Axis::HorizontalScroll => state.horizontal_modifier,
1030                    _ => 1.0,
1031                };
1032                let supports_relative_direction =
1033                    wl_pointer.version() >= wl_pointer::EVT_AXIS_RELATIVE_DIRECTION_SINCE;
1034                state.scroll_event_received = true;
1035                let scroll_delta = state
1036                    .continuous_scroll_delta
1037                    .get_or_insert(point(px(0.0), px(0.0)));
1038                // TODO: Make nice feeling kinetic scrolling that integrates with the platform's scroll settings
1039                let modifier = 3.0;
1040                match axis {
1041                    wl_pointer::Axis::VerticalScroll => {
1042                        scroll_delta.y += px(value as f32 * modifier * axis_modifier);
1043                    }
1044                    wl_pointer::Axis::HorizontalScroll => {
1045                        scroll_delta.x += px(value as f32 * modifier * axis_modifier);
1046                    }
1047                    _ => unreachable!(),
1048                }
1049            }
1050            wl_pointer::Event::AxisDiscrete {
1051                axis: WEnum::Value(axis),
1052                discrete,
1053            } => {
1054                state.scroll_event_received = true;
1055                let axis_modifier = match axis {
1056                    wl_pointer::Axis::VerticalScroll => state.vertical_modifier,
1057                    wl_pointer::Axis::HorizontalScroll => state.horizontal_modifier,
1058                    _ => 1.0,
1059                };
1060
1061                // TODO: Make nice feeling kinetic scrolling that integrates with the platform's scroll settings
1062                let modifier = 3.0;
1063
1064                let scroll_delta = state.discrete_scroll_delta.get_or_insert(point(0.0, 0.0));
1065                match axis {
1066                    wl_pointer::Axis::VerticalScroll => {
1067                        scroll_delta.y += discrete as f32 * axis_modifier * modifier;
1068                    }
1069                    wl_pointer::Axis::HorizontalScroll => {
1070                        scroll_delta.x += discrete as f32 * axis_modifier * modifier;
1071                    }
1072                    _ => unreachable!(),
1073                }
1074            }
1075            wl_pointer::Event::AxisRelativeDirection {
1076                axis: WEnum::Value(axis),
1077                direction: WEnum::Value(direction),
1078            } => match (axis, direction) {
1079                (wl_pointer::Axis::VerticalScroll, AxisRelativeDirection::Identical) => {
1080                    state.vertical_modifier = -1.0
1081                }
1082                (wl_pointer::Axis::VerticalScroll, AxisRelativeDirection::Inverted) => {
1083                    state.vertical_modifier = 1.0
1084                }
1085                (wl_pointer::Axis::HorizontalScroll, AxisRelativeDirection::Identical) => {
1086                    state.horizontal_modifier = -1.0
1087                }
1088                (wl_pointer::Axis::HorizontalScroll, AxisRelativeDirection::Inverted) => {
1089                    state.horizontal_modifier = 1.0
1090                }
1091                _ => unreachable!(),
1092            },
1093            wl_pointer::Event::AxisValue120 {
1094                axis: WEnum::Value(axis),
1095                value120,
1096            } => {
1097                state.scroll_event_received = true;
1098                let axis_modifier = match axis {
1099                    wl_pointer::Axis::VerticalScroll => state.vertical_modifier,
1100                    wl_pointer::Axis::HorizontalScroll => state.horizontal_modifier,
1101                    _ => unreachable!(),
1102                };
1103
1104                let scroll_delta = state.discrete_scroll_delta.get_or_insert(point(0.0, 0.0));
1105                let wheel_percent = value120 as f32 / 120.0;
1106                match axis {
1107                    wl_pointer::Axis::VerticalScroll => {
1108                        scroll_delta.y += wheel_percent * axis_modifier;
1109                    }
1110                    wl_pointer::Axis::HorizontalScroll => {
1111                        scroll_delta.x += wheel_percent * axis_modifier;
1112                    }
1113                    _ => unreachable!(),
1114                }
1115            }
1116            wl_pointer::Event::Frame => {
1117                if state.scroll_event_received {
1118                    state.scroll_event_received = false;
1119                    let continuous = state.continuous_scroll_delta.take();
1120                    let discrete = state.discrete_scroll_delta.take();
1121                    if let Some(continuous) = continuous {
1122                        if let Some(window) = state.mouse_focused_window.clone() {
1123                            let input = PlatformInput::ScrollWheel(ScrollWheelEvent {
1124                                position: state.mouse_location.unwrap(),
1125                                delta: ScrollDelta::Pixels(continuous),
1126                                modifiers: state.modifiers,
1127                                touch_phase: TouchPhase::Moved,
1128                            });
1129                            drop(state);
1130                            window.handle_input(input);
1131                        }
1132                    } else if let Some(discrete) = discrete {
1133                        if let Some(window) = state.mouse_focused_window.clone() {
1134                            let input = PlatformInput::ScrollWheel(ScrollWheelEvent {
1135                                position: state.mouse_location.unwrap(),
1136                                delta: ScrollDelta::Lines(discrete),
1137                                modifiers: state.modifiers,
1138                                touch_phase: TouchPhase::Moved,
1139                            });
1140                            drop(state);
1141                            window.handle_input(input);
1142                        }
1143                    }
1144                }
1145            }
1146            _ => {}
1147        }
1148    }
1149}
1150
1151impl Dispatch<wp_fractional_scale_v1::WpFractionalScaleV1, ObjectId> for WaylandClientStatePtr {
1152    fn event(
1153        this: &mut Self,
1154        _: &wp_fractional_scale_v1::WpFractionalScaleV1,
1155        event: <wp_fractional_scale_v1::WpFractionalScaleV1 as Proxy>::Event,
1156        surface_id: &ObjectId,
1157        _: &Connection,
1158        _: &QueueHandle<Self>,
1159    ) {
1160        let client = this.get_client();
1161        let mut state = client.borrow_mut();
1162
1163        let Some(window) = get_window(&mut state, surface_id) else {
1164            return;
1165        };
1166
1167        drop(state);
1168        window.handle_fractional_scale_event(event);
1169    }
1170}
1171
1172impl Dispatch<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1, ObjectId>
1173    for WaylandClientStatePtr
1174{
1175    fn event(
1176        this: &mut Self,
1177        _: &zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1,
1178        event: zxdg_toplevel_decoration_v1::Event,
1179        surface_id: &ObjectId,
1180        _: &Connection,
1181        _: &QueueHandle<Self>,
1182    ) {
1183        let client = this.get_client();
1184        let mut state = client.borrow_mut();
1185        let Some(window) = get_window(&mut state, surface_id) else {
1186            return;
1187        };
1188
1189        drop(state);
1190        window.handle_toplevel_decoration_event(event);
1191    }
1192}
1193
1194const FILE_LIST_MIME_TYPE: &str = "text/uri-list";
1195
1196impl Dispatch<wl_data_device::WlDataDevice, ()> for WaylandClientStatePtr {
1197    fn event(
1198        this: &mut Self,
1199        _: &wl_data_device::WlDataDevice,
1200        event: wl_data_device::Event,
1201        _: &(),
1202        _: &Connection,
1203        _: &QueueHandle<Self>,
1204    ) {
1205        let client = this.get_client();
1206        let mut state = client.borrow_mut();
1207
1208        match event {
1209            wl_data_device::Event::Enter {
1210                serial,
1211                surface,
1212                x,
1213                y,
1214                id: data_offer,
1215            } => {
1216                state.serial = serial;
1217                if let Some(data_offer) = data_offer {
1218                    let Some(drag_window) = get_window(&mut state, &surface.id()) else {
1219                        return;
1220                    };
1221
1222                    const ACTIONS: DndAction = DndAction::Copy;
1223                    data_offer.set_actions(ACTIONS, ACTIONS);
1224
1225                    let pipe = Pipe::new().unwrap();
1226                    data_offer.receive(FILE_LIST_MIME_TYPE.to_string(), unsafe {
1227                        BorrowedFd::borrow_raw(pipe.write.as_raw_fd())
1228                    });
1229                    let fd = pipe.read;
1230                    drop(pipe.write);
1231
1232                    let read_task = state
1233                        .common
1234                        .background_executor
1235                        .spawn(async { unsafe { read_fd(fd) } });
1236
1237                    let this = this.clone();
1238                    state
1239                        .common
1240                        .foreground_executor
1241                        .spawn(async move {
1242                            let file_list = match read_task.await {
1243                                Ok(list) => list,
1244                                Err(err) => {
1245                                    log::error!("error reading drag and drop pipe: {err:?}");
1246                                    return;
1247                                }
1248                            };
1249
1250                            let paths: SmallVec<[_; 2]> = file_list
1251                                .lines()
1252                                .map(|path| PathBuf::from(path.replace("file://", "")))
1253                                .collect();
1254                            let position = Point::new(x.into(), y.into());
1255
1256                            // Prevent dropping text from other programs.
1257                            if paths.is_empty() {
1258                                data_offer.finish();
1259                                data_offer.destroy();
1260                                return;
1261                            }
1262
1263                            let input = PlatformInput::FileDrop(FileDropEvent::Entered {
1264                                position,
1265                                paths: crate::ExternalPaths(paths),
1266                            });
1267
1268                            let client = this.get_client();
1269                            let mut state = client.borrow_mut();
1270                            state.drag.data_offer = Some(data_offer);
1271                            state.drag.window = Some(drag_window.clone());
1272                            state.drag.position = position;
1273
1274                            drop(state);
1275                            drag_window.handle_input(input);
1276                        })
1277                        .detach();
1278                }
1279            }
1280            wl_data_device::Event::Motion { x, y, .. } => {
1281                let Some(drag_window) = state.drag.window.clone() else {
1282                    return;
1283                };
1284                let position = Point::new(x.into(), y.into());
1285                state.drag.position = position;
1286
1287                let input = PlatformInput::FileDrop(FileDropEvent::Pending { position });
1288                drop(state);
1289                drag_window.handle_input(input);
1290            }
1291            wl_data_device::Event::Leave => {
1292                let Some(drag_window) = state.drag.window.clone() else {
1293                    return;
1294                };
1295                let data_offer = state.drag.data_offer.clone().unwrap();
1296                data_offer.destroy();
1297
1298                state.drag.data_offer = None;
1299                state.drag.window = None;
1300
1301                let input = PlatformInput::FileDrop(FileDropEvent::Exited {});
1302                drop(state);
1303                drag_window.handle_input(input);
1304            }
1305            wl_data_device::Event::Drop => {
1306                let Some(drag_window) = state.drag.window.clone() else {
1307                    return;
1308                };
1309                let data_offer = state.drag.data_offer.clone().unwrap();
1310                data_offer.finish();
1311                data_offer.destroy();
1312
1313                state.drag.data_offer = None;
1314                state.drag.window = None;
1315
1316                let input = PlatformInput::FileDrop(FileDropEvent::Submit {
1317                    position: state.drag.position,
1318                });
1319                drop(state);
1320                drag_window.handle_input(input);
1321            }
1322            _ => {}
1323        }
1324    }
1325
1326    event_created_child!(WaylandClientStatePtr, wl_data_device::WlDataDevice, [
1327        wl_data_device::EVT_DATA_OFFER_OPCODE => (wl_data_offer::WlDataOffer, ()),
1328    ]);
1329}
1330
1331impl Dispatch<wl_data_offer::WlDataOffer, ()> for WaylandClientStatePtr {
1332    fn event(
1333        this: &mut Self,
1334        data_offer: &wl_data_offer::WlDataOffer,
1335        event: wl_data_offer::Event,
1336        _: &(),
1337        _: &Connection,
1338        _: &QueueHandle<Self>,
1339    ) {
1340        let client = this.get_client();
1341        let mut state = client.borrow_mut();
1342
1343        match event {
1344            wl_data_offer::Event::Offer { mime_type } => {
1345                if mime_type == FILE_LIST_MIME_TYPE {
1346                    data_offer.accept(state.serial, Some(mime_type));
1347                }
1348            }
1349            _ => {}
1350        }
1351    }
1352}