client.rs

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