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_keycode: Option<xkb::Keycode>,
 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_keycode: 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_keycode = Some(keycode);
 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_keycode.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                        if state.repeat.current_keycode == Some(keycode) {
 847                            state.repeat.current_keycode = None;
 848                        }
 849
 850                        drop(state);
 851                        focused_window.handle_input(input);
 852                    }
 853                    _ => {}
 854                }
 855            }
 856            _ => {}
 857        }
 858    }
 859}
 860
 861fn linux_button_to_gpui(button: u32) -> Option<MouseButton> {
 862    // These values are coming from <linux/input-event-codes.h>.
 863    const BTN_LEFT: u32 = 0x110;
 864    const BTN_RIGHT: u32 = 0x111;
 865    const BTN_MIDDLE: u32 = 0x112;
 866    const BTN_SIDE: u32 = 0x113;
 867    const BTN_EXTRA: u32 = 0x114;
 868    const BTN_FORWARD: u32 = 0x115;
 869    const BTN_BACK: u32 = 0x116;
 870
 871    Some(match button {
 872        BTN_LEFT => MouseButton::Left,
 873        BTN_RIGHT => MouseButton::Right,
 874        BTN_MIDDLE => MouseButton::Middle,
 875        BTN_BACK | BTN_SIDE => MouseButton::Navigate(NavigationDirection::Back),
 876        BTN_FORWARD | BTN_EXTRA => MouseButton::Navigate(NavigationDirection::Forward),
 877        _ => return None,
 878    })
 879}
 880
 881impl Dispatch<wl_pointer::WlPointer, ()> for WaylandClientStatePtr {
 882    fn event(
 883        this: &mut Self,
 884        wl_pointer: &wl_pointer::WlPointer,
 885        event: wl_pointer::Event,
 886        data: &(),
 887        conn: &Connection,
 888        qh: &QueueHandle<Self>,
 889    ) {
 890        let mut client = this.get_client();
 891        let mut state = client.borrow_mut();
 892        let cursor_icon_name = state.cursor_icon_name.clone();
 893
 894        match event {
 895            wl_pointer::Event::Enter {
 896                serial,
 897                surface,
 898                surface_x,
 899                surface_y,
 900                ..
 901            } => {
 902                state.serial = serial;
 903                state.mouse_location = Some(point(px(surface_x as f32), px(surface_y as f32)));
 904
 905                if let Some(window) = get_window(&mut state, &surface.id()) {
 906                    state.enter_token = Some(());
 907                    state.mouse_focused_window = Some(window.clone());
 908                    state.cursor.mark_dirty();
 909                    state.cursor.set_serial_id(serial);
 910                    state
 911                        .cursor
 912                        .set_icon(&wl_pointer, cursor_icon_name.as_str());
 913                    drop(state);
 914                    window.set_focused(true);
 915                }
 916            }
 917            wl_pointer::Event::Leave { surface, .. } => {
 918                if let Some(focused_window) = state.mouse_focused_window.clone() {
 919                    state.enter_token.take();
 920                    let input = PlatformInput::MouseExited(MouseExitEvent {
 921                        position: state.mouse_location.unwrap(),
 922                        pressed_button: state.button_pressed,
 923                        modifiers: state.modifiers,
 924                    });
 925                    state.mouse_focused_window = None;
 926                    state.mouse_location = None;
 927
 928                    drop(state);
 929                    focused_window.handle_input(input);
 930                    focused_window.set_focused(false);
 931                }
 932            }
 933            wl_pointer::Event::Motion {
 934                time,
 935                surface_x,
 936                surface_y,
 937                ..
 938            } => {
 939                if state.mouse_focused_window.is_none() {
 940                    return;
 941                }
 942                state.mouse_location = Some(point(px(surface_x as f32), px(surface_y as f32)));
 943
 944                if let Some(window) = state.mouse_focused_window.clone() {
 945                    let input = PlatformInput::MouseMove(MouseMoveEvent {
 946                        position: state.mouse_location.unwrap(),
 947                        pressed_button: state.button_pressed,
 948                        modifiers: state.modifiers,
 949                    });
 950                    drop(state);
 951                    window.handle_input(input);
 952                }
 953            }
 954            wl_pointer::Event::Button {
 955                serial,
 956                button,
 957                state: WEnum::Value(button_state),
 958                ..
 959            } => {
 960                state.serial = serial;
 961                let button = linux_button_to_gpui(button);
 962                let Some(button) = button else { return };
 963                if state.mouse_focused_window.is_none() {
 964                    return;
 965                }
 966                match button_state {
 967                    wl_pointer::ButtonState::Pressed => {
 968                        let click_elapsed = state.click.last_click.elapsed();
 969
 970                        if click_elapsed < DOUBLE_CLICK_INTERVAL
 971                            && is_within_click_distance(
 972                                state.click.last_location,
 973                                state.mouse_location.unwrap(),
 974                            )
 975                        {
 976                            state.click.current_count += 1;
 977                        } else {
 978                            state.click.current_count = 1;
 979                        }
 980
 981                        state.click.last_click = Instant::now();
 982                        state.click.last_location = state.mouse_location.unwrap();
 983
 984                        state.button_pressed = Some(button);
 985
 986                        if let Some(window) = state.mouse_focused_window.clone() {
 987                            let input = PlatformInput::MouseDown(MouseDownEvent {
 988                                button,
 989                                position: state.mouse_location.unwrap(),
 990                                modifiers: state.modifiers,
 991                                click_count: state.click.current_count,
 992                                first_mouse: state.enter_token.take().is_some(),
 993                            });
 994                            drop(state);
 995                            window.handle_input(input);
 996                        }
 997                    }
 998                    wl_pointer::ButtonState::Released => {
 999                        state.button_pressed = None;
1000
1001                        if let Some(window) = state.mouse_focused_window.clone() {
1002                            let input = PlatformInput::MouseUp(MouseUpEvent {
1003                                button,
1004                                position: state.mouse_location.unwrap(),
1005                                modifiers: state.modifiers,
1006                                click_count: state.click.current_count,
1007                            });
1008                            drop(state);
1009                            window.handle_input(input);
1010                        }
1011                    }
1012                    _ => {}
1013                }
1014            }
1015
1016            // Axis Events
1017            wl_pointer::Event::AxisSource {
1018                axis_source: WEnum::Value(axis_source),
1019            } => {
1020                state.axis_source = axis_source;
1021            }
1022            wl_pointer::Event::Axis {
1023                time,
1024                axis: WEnum::Value(axis),
1025                value,
1026                ..
1027            } => {
1028                let axis_source = state.axis_source;
1029                let axis_modifier = match axis {
1030                    wl_pointer::Axis::VerticalScroll => state.vertical_modifier,
1031                    wl_pointer::Axis::HorizontalScroll => state.horizontal_modifier,
1032                    _ => 1.0,
1033                };
1034                let supports_relative_direction =
1035                    wl_pointer.version() >= wl_pointer::EVT_AXIS_RELATIVE_DIRECTION_SINCE;
1036                state.scroll_event_received = true;
1037                let scroll_delta = state
1038                    .continuous_scroll_delta
1039                    .get_or_insert(point(px(0.0), px(0.0)));
1040                // TODO: Make nice feeling kinetic scrolling that integrates with the platform's scroll settings
1041                let modifier = 3.0;
1042                match axis {
1043                    wl_pointer::Axis::VerticalScroll => {
1044                        scroll_delta.y += px(value as f32 * modifier * axis_modifier);
1045                    }
1046                    wl_pointer::Axis::HorizontalScroll => {
1047                        scroll_delta.x += px(value as f32 * modifier * axis_modifier);
1048                    }
1049                    _ => unreachable!(),
1050                }
1051            }
1052            wl_pointer::Event::AxisDiscrete {
1053                axis: WEnum::Value(axis),
1054                discrete,
1055            } => {
1056                state.scroll_event_received = true;
1057                let axis_modifier = match axis {
1058                    wl_pointer::Axis::VerticalScroll => state.vertical_modifier,
1059                    wl_pointer::Axis::HorizontalScroll => state.horizontal_modifier,
1060                    _ => 1.0,
1061                };
1062
1063                // TODO: Make nice feeling kinetic scrolling that integrates with the platform's scroll settings
1064                let modifier = 3.0;
1065
1066                let scroll_delta = state.discrete_scroll_delta.get_or_insert(point(0.0, 0.0));
1067                match axis {
1068                    wl_pointer::Axis::VerticalScroll => {
1069                        scroll_delta.y += discrete as f32 * axis_modifier * modifier;
1070                    }
1071                    wl_pointer::Axis::HorizontalScroll => {
1072                        scroll_delta.x += discrete as f32 * axis_modifier * modifier;
1073                    }
1074                    _ => unreachable!(),
1075                }
1076            }
1077            wl_pointer::Event::AxisRelativeDirection {
1078                axis: WEnum::Value(axis),
1079                direction: WEnum::Value(direction),
1080            } => match (axis, direction) {
1081                (wl_pointer::Axis::VerticalScroll, AxisRelativeDirection::Identical) => {
1082                    state.vertical_modifier = -1.0
1083                }
1084                (wl_pointer::Axis::VerticalScroll, AxisRelativeDirection::Inverted) => {
1085                    state.vertical_modifier = 1.0
1086                }
1087                (wl_pointer::Axis::HorizontalScroll, AxisRelativeDirection::Identical) => {
1088                    state.horizontal_modifier = -1.0
1089                }
1090                (wl_pointer::Axis::HorizontalScroll, AxisRelativeDirection::Inverted) => {
1091                    state.horizontal_modifier = 1.0
1092                }
1093                _ => unreachable!(),
1094            },
1095            wl_pointer::Event::AxisValue120 {
1096                axis: WEnum::Value(axis),
1097                value120,
1098            } => {
1099                state.scroll_event_received = true;
1100                let axis_modifier = match axis {
1101                    wl_pointer::Axis::VerticalScroll => state.vertical_modifier,
1102                    wl_pointer::Axis::HorizontalScroll => state.horizontal_modifier,
1103                    _ => unreachable!(),
1104                };
1105
1106                let scroll_delta = state.discrete_scroll_delta.get_or_insert(point(0.0, 0.0));
1107                let wheel_percent = value120 as f32 / 120.0;
1108                match axis {
1109                    wl_pointer::Axis::VerticalScroll => {
1110                        scroll_delta.y += wheel_percent * axis_modifier;
1111                    }
1112                    wl_pointer::Axis::HorizontalScroll => {
1113                        scroll_delta.x += wheel_percent * axis_modifier;
1114                    }
1115                    _ => unreachable!(),
1116                }
1117            }
1118            wl_pointer::Event::Frame => {
1119                if state.scroll_event_received {
1120                    state.scroll_event_received = false;
1121                    let continuous = state.continuous_scroll_delta.take();
1122                    let discrete = state.discrete_scroll_delta.take();
1123                    if let Some(continuous) = continuous {
1124                        if let Some(window) = state.mouse_focused_window.clone() {
1125                            let input = PlatformInput::ScrollWheel(ScrollWheelEvent {
1126                                position: state.mouse_location.unwrap(),
1127                                delta: ScrollDelta::Pixels(continuous),
1128                                modifiers: state.modifiers,
1129                                touch_phase: TouchPhase::Moved,
1130                            });
1131                            drop(state);
1132                            window.handle_input(input);
1133                        }
1134                    } else if let Some(discrete) = discrete {
1135                        if let Some(window) = state.mouse_focused_window.clone() {
1136                            let input = PlatformInput::ScrollWheel(ScrollWheelEvent {
1137                                position: state.mouse_location.unwrap(),
1138                                delta: ScrollDelta::Lines(discrete),
1139                                modifiers: state.modifiers,
1140                                touch_phase: TouchPhase::Moved,
1141                            });
1142                            drop(state);
1143                            window.handle_input(input);
1144                        }
1145                    }
1146                }
1147            }
1148            _ => {}
1149        }
1150    }
1151}
1152
1153impl Dispatch<wp_fractional_scale_v1::WpFractionalScaleV1, ObjectId> for WaylandClientStatePtr {
1154    fn event(
1155        this: &mut Self,
1156        _: &wp_fractional_scale_v1::WpFractionalScaleV1,
1157        event: <wp_fractional_scale_v1::WpFractionalScaleV1 as Proxy>::Event,
1158        surface_id: &ObjectId,
1159        _: &Connection,
1160        _: &QueueHandle<Self>,
1161    ) {
1162        let client = this.get_client();
1163        let mut state = client.borrow_mut();
1164
1165        let Some(window) = get_window(&mut state, surface_id) else {
1166            return;
1167        };
1168
1169        drop(state);
1170        window.handle_fractional_scale_event(event);
1171    }
1172}
1173
1174impl Dispatch<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1, ObjectId>
1175    for WaylandClientStatePtr
1176{
1177    fn event(
1178        this: &mut Self,
1179        _: &zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1,
1180        event: zxdg_toplevel_decoration_v1::Event,
1181        surface_id: &ObjectId,
1182        _: &Connection,
1183        _: &QueueHandle<Self>,
1184    ) {
1185        let client = this.get_client();
1186        let mut state = client.borrow_mut();
1187        let Some(window) = get_window(&mut state, surface_id) else {
1188            return;
1189        };
1190
1191        drop(state);
1192        window.handle_toplevel_decoration_event(event);
1193    }
1194}
1195
1196const FILE_LIST_MIME_TYPE: &str = "text/uri-list";
1197
1198impl Dispatch<wl_data_device::WlDataDevice, ()> for WaylandClientStatePtr {
1199    fn event(
1200        this: &mut Self,
1201        _: &wl_data_device::WlDataDevice,
1202        event: wl_data_device::Event,
1203        _: &(),
1204        _: &Connection,
1205        _: &QueueHandle<Self>,
1206    ) {
1207        let client = this.get_client();
1208        let mut state = client.borrow_mut();
1209
1210        match event {
1211            wl_data_device::Event::Enter {
1212                serial,
1213                surface,
1214                x,
1215                y,
1216                id: data_offer,
1217            } => {
1218                state.serial = serial;
1219                if let Some(data_offer) = data_offer {
1220                    let Some(drag_window) = get_window(&mut state, &surface.id()) else {
1221                        return;
1222                    };
1223
1224                    const ACTIONS: DndAction = DndAction::Copy;
1225                    data_offer.set_actions(ACTIONS, ACTIONS);
1226
1227                    let pipe = Pipe::new().unwrap();
1228                    data_offer.receive(FILE_LIST_MIME_TYPE.to_string(), unsafe {
1229                        BorrowedFd::borrow_raw(pipe.write.as_raw_fd())
1230                    });
1231                    let fd = pipe.read;
1232                    drop(pipe.write);
1233
1234                    let read_task = state
1235                        .common
1236                        .background_executor
1237                        .spawn(async { unsafe { read_fd(fd) } });
1238
1239                    let this = this.clone();
1240                    state
1241                        .common
1242                        .foreground_executor
1243                        .spawn(async move {
1244                            let file_list = match read_task.await {
1245                                Ok(list) => list,
1246                                Err(err) => {
1247                                    log::error!("error reading drag and drop pipe: {err:?}");
1248                                    return;
1249                                }
1250                            };
1251
1252                            let paths: SmallVec<[_; 2]> = file_list
1253                                .lines()
1254                                .map(|path| PathBuf::from(path.replace("file://", "")))
1255                                .collect();
1256                            let position = Point::new(x.into(), y.into());
1257
1258                            // Prevent dropping text from other programs.
1259                            if paths.is_empty() {
1260                                data_offer.finish();
1261                                data_offer.destroy();
1262                                return;
1263                            }
1264
1265                            let input = PlatformInput::FileDrop(FileDropEvent::Entered {
1266                                position,
1267                                paths: crate::ExternalPaths(paths),
1268                            });
1269
1270                            let client = this.get_client();
1271                            let mut state = client.borrow_mut();
1272                            state.drag.data_offer = Some(data_offer);
1273                            state.drag.window = Some(drag_window.clone());
1274                            state.drag.position = position;
1275
1276                            drop(state);
1277                            drag_window.handle_input(input);
1278                        })
1279                        .detach();
1280                }
1281            }
1282            wl_data_device::Event::Motion { x, y, .. } => {
1283                let Some(drag_window) = state.drag.window.clone() else {
1284                    return;
1285                };
1286                let position = Point::new(x.into(), y.into());
1287                state.drag.position = position;
1288
1289                let input = PlatformInput::FileDrop(FileDropEvent::Pending { position });
1290                drop(state);
1291                drag_window.handle_input(input);
1292            }
1293            wl_data_device::Event::Leave => {
1294                let Some(drag_window) = state.drag.window.clone() else {
1295                    return;
1296                };
1297                let data_offer = state.drag.data_offer.clone().unwrap();
1298                data_offer.destroy();
1299
1300                state.drag.data_offer = None;
1301                state.drag.window = None;
1302
1303                let input = PlatformInput::FileDrop(FileDropEvent::Exited {});
1304                drop(state);
1305                drag_window.handle_input(input);
1306            }
1307            wl_data_device::Event::Drop => {
1308                let Some(drag_window) = state.drag.window.clone() else {
1309                    return;
1310                };
1311                let data_offer = state.drag.data_offer.clone().unwrap();
1312                data_offer.finish();
1313                data_offer.destroy();
1314
1315                state.drag.data_offer = None;
1316                state.drag.window = None;
1317
1318                let input = PlatformInput::FileDrop(FileDropEvent::Submit {
1319                    position: state.drag.position,
1320                });
1321                drop(state);
1322                drag_window.handle_input(input);
1323            }
1324            _ => {}
1325        }
1326    }
1327
1328    event_created_child!(WaylandClientStatePtr, wl_data_device::WlDataDevice, [
1329        wl_data_device::EVT_DATA_OFFER_OPCODE => (wl_data_offer::WlDataOffer, ()),
1330    ]);
1331}
1332
1333impl Dispatch<wl_data_offer::WlDataOffer, ()> for WaylandClientStatePtr {
1334    fn event(
1335        this: &mut Self,
1336        data_offer: &wl_data_offer::WlDataOffer,
1337        event: wl_data_offer::Event,
1338        _: &(),
1339        _: &Connection,
1340        _: &QueueHandle<Self>,
1341    ) {
1342        let client = this.get_client();
1343        let mut state = client.borrow_mut();
1344
1345        match event {
1346            wl_data_offer::Event::Offer { mime_type } => {
1347                if mime_type == FILE_LIST_MIME_TYPE {
1348                    data_offer.accept(state.serial, Some(mime_type));
1349                }
1350            }
1351            _ => {}
1352        }
1353    }
1354}