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