client.rs

   1use std::{
   2    cell::{RefCell, RefMut},
   3    hash::Hash,
   4    os::fd::{AsRawFd, BorrowedFd},
   5    path::PathBuf,
   6    rc::{Rc, Weak},
   7    time::{Duration, Instant},
   8};
   9
  10use ashpd::WindowIdentifier;
  11use calloop::{
  12    EventLoop, LoopHandle,
  13    timer::{TimeoutAction, Timer},
  14};
  15use calloop_wayland_source::WaylandSource;
  16use collections::HashMap;
  17use filedescriptor::Pipe;
  18use http_client::Url;
  19use smallvec::SmallVec;
  20use util::ResultExt as _;
  21use wayland_backend::client::ObjectId;
  22use wayland_backend::protocol::WEnum;
  23use wayland_client::event_created_child;
  24use wayland_client::globals::{GlobalList, GlobalListContents, registry_queue_init};
  25use wayland_client::protocol::wl_callback::{self, WlCallback};
  26use wayland_client::protocol::wl_data_device_manager::DndAction;
  27use wayland_client::protocol::wl_data_offer::WlDataOffer;
  28use wayland_client::protocol::wl_pointer::AxisSource;
  29use wayland_client::protocol::{
  30    wl_data_device, wl_data_device_manager, wl_data_offer, wl_data_source, wl_output, wl_region,
  31};
  32use wayland_client::{
  33    Connection, Dispatch, Proxy, QueueHandle, delegate_noop,
  34    protocol::{
  35        wl_buffer, wl_compositor, wl_keyboard, wl_pointer, wl_registry, wl_seat, wl_shm,
  36        wl_shm_pool, wl_surface,
  37    },
  38};
  39use wayland_protocols::wp::primary_selection::zv1::client::zwp_primary_selection_offer_v1::{
  40    self, ZwpPrimarySelectionOfferV1,
  41};
  42use wayland_protocols::wp::primary_selection::zv1::client::{
  43    zwp_primary_selection_device_manager_v1, zwp_primary_selection_device_v1,
  44    zwp_primary_selection_source_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::{
  59    wp::cursor_shape::v1::client::{wp_cursor_shape_device_v1, wp_cursor_shape_manager_v1},
  60    xdg::dialog::v1::client::xdg_wm_dialog_v1::{self, XdgWmDialogV1},
  61};
  62use wayland_protocols::{
  63    wp::fractional_scale::v1::client::{wp_fractional_scale_manager_v1, wp_fractional_scale_v1},
  64    xdg::dialog::v1::client::xdg_dialog_v1::XdgDialogV1,
  65};
  66use wayland_protocols_plasma::blur::client::{org_kde_kwin_blur, org_kde_kwin_blur_manager};
  67use wayland_protocols_wlr::layer_shell::v1::client::{zwlr_layer_shell_v1, zwlr_layer_surface_v1};
  68use xkbcommon::xkb::ffi::XKB_KEYMAP_FORMAT_TEXT_V1;
  69use xkbcommon::xkb::{self, KEYMAP_COMPILE_NO_FLAGS, Keycode};
  70
  71use super::{
  72    display::WaylandDisplay,
  73    window::{ImeInput, WaylandWindowStatePtr},
  74};
  75
  76use crate::platform::{PlatformWindow, blade::BladeContext};
  77use crate::{
  78    AnyWindowHandle, Bounds, Capslock, CursorStyle, DOUBLE_CLICK_INTERVAL, DevicePixels, DisplayId,
  79    FileDropEvent, ForegroundExecutor, KeyDownEvent, KeyUpEvent, Keystroke, LinuxCommon,
  80    LinuxKeyboardLayout, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent,
  81    MouseExitEvent, MouseMoveEvent, MouseUpEvent, NavigationDirection, Pixels, PlatformDisplay,
  82    PlatformInput, PlatformKeyboardLayout, Point, ResultExt as _, SCROLL_LINES, ScrollDelta,
  83    ScrollWheelEvent, Size, TouchPhase, WindowParams, point, px, size,
  84};
  85use crate::{
  86    SharedString,
  87    platform::linux::{
  88        LinuxClient, get_xkb_compose_state, is_within_click_distance, open_uri_internal, read_fd,
  89        reveal_path_internal,
  90        wayland::{
  91            clipboard::{Clipboard, DataOffer, FILE_LIST_MIME_TYPE, TEXT_MIME_TYPES},
  92            cursor::Cursor,
  93            serial::{SerialKind, SerialTracker},
  94            window::WaylandWindow,
  95        },
  96        xdg_desktop_portal::{Event as XDPEvent, XDPEventSource},
  97    },
  98};
  99
 100/// Used to convert evdev scancode to xkb scancode
 101const MIN_KEYCODE: u32 = 8;
 102
 103const UNKNOWN_KEYBOARD_LAYOUT_NAME: SharedString = SharedString::new_static("unknown");
 104
 105#[derive(Clone)]
 106pub struct Globals {
 107    pub qh: QueueHandle<WaylandClientStatePtr>,
 108    pub activation: Option<xdg_activation_v1::XdgActivationV1>,
 109    pub compositor: wl_compositor::WlCompositor,
 110    pub cursor_shape_manager: Option<wp_cursor_shape_manager_v1::WpCursorShapeManagerV1>,
 111    pub data_device_manager: Option<wl_data_device_manager::WlDataDeviceManager>,
 112    pub primary_selection_manager:
 113        Option<zwp_primary_selection_device_manager_v1::ZwpPrimarySelectionDeviceManagerV1>,
 114    pub wm_base: xdg_wm_base::XdgWmBase,
 115    pub shm: wl_shm::WlShm,
 116    pub seat: wl_seat::WlSeat,
 117    pub viewporter: Option<wp_viewporter::WpViewporter>,
 118    pub fractional_scale_manager:
 119        Option<wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1>,
 120    pub decoration_manager: Option<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1>,
 121    pub layer_shell: Option<zwlr_layer_shell_v1::ZwlrLayerShellV1>,
 122    pub blur_manager: Option<org_kde_kwin_blur_manager::OrgKdeKwinBlurManager>,
 123    pub text_input_manager: Option<zwp_text_input_manager_v3::ZwpTextInputManagerV3>,
 124    pub dialog: Option<xdg_wm_dialog_v1::XdgWmDialogV1>,
 125    pub executor: ForegroundExecutor,
 126}
 127
 128impl Globals {
 129    fn new(
 130        globals: GlobalList,
 131        executor: ForegroundExecutor,
 132        qh: QueueHandle<WaylandClientStatePtr>,
 133        seat: wl_seat::WlSeat,
 134    ) -> Self {
 135        let dialog_v = XdgWmDialogV1::interface().version;
 136        Globals {
 137            activation: globals.bind(&qh, 1..=1, ()).ok(),
 138            compositor: globals
 139                .bind(
 140                    &qh,
 141                    wl_surface::REQ_SET_BUFFER_SCALE_SINCE
 142                        ..=wl_surface::EVT_PREFERRED_BUFFER_SCALE_SINCE,
 143                    (),
 144                )
 145                .unwrap(),
 146            cursor_shape_manager: globals.bind(&qh, 1..=1, ()).ok(),
 147            data_device_manager: globals
 148                .bind(
 149                    &qh,
 150                    WL_DATA_DEVICE_MANAGER_VERSION..=WL_DATA_DEVICE_MANAGER_VERSION,
 151                    (),
 152                )
 153                .ok(),
 154            primary_selection_manager: globals.bind(&qh, 1..=1, ()).ok(),
 155            shm: globals.bind(&qh, 1..=1, ()).unwrap(),
 156            seat,
 157            wm_base: globals.bind(&qh, 2..=5, ()).unwrap(),
 158            viewporter: globals.bind(&qh, 1..=1, ()).ok(),
 159            fractional_scale_manager: globals.bind(&qh, 1..=1, ()).ok(),
 160            decoration_manager: globals.bind(&qh, 1..=1, ()).ok(),
 161            layer_shell: globals.bind(&qh, 1..=5, ()).ok(),
 162            blur_manager: globals.bind(&qh, 1..=1, ()).ok(),
 163            text_input_manager: globals.bind(&qh, 1..=1, ()).ok(),
 164            dialog: globals.bind(&qh, dialog_v..=dialog_v, ()).ok(),
 165            executor,
 166            qh,
 167        }
 168    }
 169}
 170
 171#[derive(Default, Debug, Clone, PartialEq, Eq, Hash)]
 172pub struct InProgressOutput {
 173    name: Option<String>,
 174    scale: Option<i32>,
 175    position: Option<Point<DevicePixels>>,
 176    size: Option<Size<DevicePixels>>,
 177}
 178
 179impl InProgressOutput {
 180    fn complete(&self) -> Option<Output> {
 181        if let Some((position, size)) = self.position.zip(self.size) {
 182            let scale = self.scale.unwrap_or(1);
 183            Some(Output {
 184                name: self.name.clone(),
 185                scale,
 186                bounds: Bounds::new(position, size),
 187            })
 188        } else {
 189            None
 190        }
 191    }
 192}
 193
 194#[derive(Debug, Clone, Eq, PartialEq, Hash)]
 195pub struct Output {
 196    pub name: Option<String>,
 197    pub scale: i32,
 198    pub bounds: Bounds<DevicePixels>,
 199}
 200
 201pub(crate) struct WaylandClientState {
 202    serial_tracker: SerialTracker,
 203    globals: Globals,
 204    pub gpu_context: BladeContext,
 205    wl_seat: wl_seat::WlSeat, // TODO: Multi seat support
 206    wl_pointer: Option<wl_pointer::WlPointer>,
 207    wl_keyboard: Option<wl_keyboard::WlKeyboard>,
 208    cursor_shape_device: Option<wp_cursor_shape_device_v1::WpCursorShapeDeviceV1>,
 209    data_device: Option<wl_data_device::WlDataDevice>,
 210    primary_selection: Option<zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1>,
 211    text_input: Option<zwp_text_input_v3::ZwpTextInputV3>,
 212    pre_edit_text: Option<String>,
 213    ime_pre_edit: Option<String>,
 214    composing: bool,
 215    // Surface to Window mapping
 216    windows: HashMap<ObjectId, WaylandWindowStatePtr>,
 217    // Output to scale mapping
 218    outputs: HashMap<ObjectId, Output>,
 219    in_progress_outputs: HashMap<ObjectId, InProgressOutput>,
 220    keyboard_layout: LinuxKeyboardLayout,
 221    keymap_state: Option<xkb::State>,
 222    compose_state: Option<xkb::compose::State>,
 223    drag: DragState,
 224    click: ClickState,
 225    repeat: KeyRepeat,
 226    pub modifiers: Modifiers,
 227    pub capslock: Capslock,
 228    axis_source: AxisSource,
 229    pub mouse_location: Option<Point<Pixels>>,
 230    continuous_scroll_delta: Option<Point<Pixels>>,
 231    discrete_scroll_delta: Option<Point<f32>>,
 232    vertical_modifier: f32,
 233    horizontal_modifier: f32,
 234    scroll_event_received: bool,
 235    enter_token: Option<()>,
 236    button_pressed: Option<MouseButton>,
 237    mouse_focused_window: Option<WaylandWindowStatePtr>,
 238    keyboard_focused_window: Option<WaylandWindowStatePtr>,
 239    loop_handle: LoopHandle<'static, WaylandClientStatePtr>,
 240    cursor_style: Option<CursorStyle>,
 241    clipboard: Clipboard,
 242    data_offers: Vec<DataOffer<WlDataOffer>>,
 243    primary_data_offer: Option<DataOffer<ZwpPrimarySelectionOfferV1>>,
 244    cursor: Cursor,
 245    pending_activation: Option<PendingActivation>,
 246    event_loop: Option<EventLoop<'static, WaylandClientStatePtr>>,
 247    pub common: LinuxCommon,
 248}
 249
 250pub struct DragState {
 251    data_offer: Option<wl_data_offer::WlDataOffer>,
 252    window: Option<WaylandWindowStatePtr>,
 253    position: Point<Pixels>,
 254}
 255
 256pub struct ClickState {
 257    last_mouse_button: Option<MouseButton>,
 258    last_click: Instant,
 259    last_location: Point<Pixels>,
 260    current_count: usize,
 261}
 262
 263pub(crate) struct KeyRepeat {
 264    characters_per_second: u32,
 265    delay: Duration,
 266    current_id: u64,
 267    current_keycode: Option<xkb::Keycode>,
 268}
 269
 270pub(crate) enum PendingActivation {
 271    /// URI to open in the web browser.
 272    Uri(String),
 273    /// Path to open in the file explorer.
 274    Path(PathBuf),
 275    /// A window from ourselves to raise.
 276    Window(ObjectId),
 277}
 278
 279/// This struct is required to conform to Rust's orphan rules, so we can dispatch on the state but hand the
 280/// window to GPUI.
 281#[derive(Clone)]
 282pub struct WaylandClientStatePtr(Weak<RefCell<WaylandClientState>>);
 283
 284impl WaylandClientStatePtr {
 285    pub fn get_client(&self) -> Rc<RefCell<WaylandClientState>> {
 286        self.0
 287            .upgrade()
 288            .expect("The pointer should always be valid when dispatching in wayland")
 289    }
 290
 291    pub fn get_serial(&self, kind: SerialKind) -> u32 {
 292        self.0.upgrade().unwrap().borrow().serial_tracker.get(kind)
 293    }
 294
 295    pub fn set_pending_activation(&self, window: ObjectId) {
 296        self.0.upgrade().unwrap().borrow_mut().pending_activation =
 297            Some(PendingActivation::Window(window));
 298    }
 299
 300    pub fn enable_ime(&self) {
 301        let client = self.get_client();
 302        let mut state = client.borrow_mut();
 303        let Some(mut text_input) = state.text_input.take() else {
 304            return;
 305        };
 306
 307        text_input.enable();
 308        text_input.set_content_type(ContentHint::None, ContentPurpose::Normal);
 309        if let Some(window) = state.keyboard_focused_window.clone() {
 310            drop(state);
 311            if let Some(area) = window.get_ime_area() {
 312                text_input.set_cursor_rectangle(
 313                    area.origin.x.0 as i32,
 314                    area.origin.y.0 as i32,
 315                    area.size.width.0 as i32,
 316                    area.size.height.0 as i32,
 317                );
 318            }
 319            state = client.borrow_mut();
 320        }
 321        text_input.commit();
 322        state.text_input = Some(text_input);
 323    }
 324
 325    pub fn disable_ime(&self) {
 326        let client = self.get_client();
 327        let mut state = client.borrow_mut();
 328        state.composing = false;
 329        if let Some(text_input) = &state.text_input {
 330            text_input.disable();
 331            text_input.commit();
 332        }
 333    }
 334
 335    pub fn update_ime_position(&self, bounds: Bounds<Pixels>) {
 336        let client = self.get_client();
 337        let mut state = client.borrow_mut();
 338        if state.composing || state.text_input.is_none() || state.pre_edit_text.is_some() {
 339            return;
 340        }
 341
 342        let text_input = state.text_input.as_ref().unwrap();
 343        text_input.set_cursor_rectangle(
 344            bounds.origin.x.0 as i32,
 345            bounds.origin.y.0 as i32,
 346            bounds.size.width.0 as i32,
 347            bounds.size.height.0 as i32,
 348        );
 349        text_input.commit();
 350    }
 351
 352    pub fn handle_keyboard_layout_change(&self) {
 353        let client = self.get_client();
 354        let mut state = client.borrow_mut();
 355        let changed = if let Some(keymap_state) = &state.keymap_state {
 356            let layout_idx = keymap_state.serialize_layout(xkbcommon::xkb::STATE_LAYOUT_EFFECTIVE);
 357            let keymap = keymap_state.get_keymap();
 358            let layout_name = keymap.layout_get_name(layout_idx);
 359            let changed = layout_name != state.keyboard_layout.name();
 360            if changed {
 361                state.keyboard_layout = LinuxKeyboardLayout::new(layout_name.to_string().into());
 362            }
 363            changed
 364        } else {
 365            let changed = &UNKNOWN_KEYBOARD_LAYOUT_NAME != state.keyboard_layout.name();
 366            if changed {
 367                state.keyboard_layout = LinuxKeyboardLayout::new(UNKNOWN_KEYBOARD_LAYOUT_NAME);
 368            }
 369            changed
 370        };
 371
 372        if changed && let Some(mut callback) = state.common.callbacks.keyboard_layout_change.take()
 373        {
 374            drop(state);
 375            callback();
 376            state = client.borrow_mut();
 377            state.common.callbacks.keyboard_layout_change = Some(callback);
 378        }
 379    }
 380
 381    pub fn drop_window(&self, surface_id: &ObjectId) {
 382        let mut client = self.get_client();
 383        let mut state = client.borrow_mut();
 384        let closed_window = state.windows.remove(surface_id).unwrap();
 385        if let Some(window) = state.mouse_focused_window.take()
 386            && !window.ptr_eq(&closed_window)
 387        {
 388            state.mouse_focused_window = Some(window);
 389        }
 390        if let Some(window) = state.keyboard_focused_window.take()
 391            && !window.ptr_eq(&closed_window)
 392        {
 393            state.keyboard_focused_window = Some(window);
 394        }
 395    }
 396}
 397
 398#[derive(Clone)]
 399pub struct WaylandClient(Rc<RefCell<WaylandClientState>>);
 400
 401impl Drop for WaylandClient {
 402    fn drop(&mut self) {
 403        let mut state = self.0.borrow_mut();
 404        state.windows.clear();
 405
 406        if let Some(wl_pointer) = &state.wl_pointer {
 407            wl_pointer.release();
 408        }
 409        if let Some(cursor_shape_device) = &state.cursor_shape_device {
 410            cursor_shape_device.destroy();
 411        }
 412        if let Some(data_device) = &state.data_device {
 413            data_device.release();
 414        }
 415        if let Some(text_input) = &state.text_input {
 416            text_input.destroy();
 417        }
 418    }
 419}
 420
 421const WL_DATA_DEVICE_MANAGER_VERSION: u32 = 3;
 422
 423fn wl_seat_version(version: u32) -> u32 {
 424    // We rely on the wl_pointer.frame event
 425    const WL_SEAT_MIN_VERSION: u32 = 5;
 426    const WL_SEAT_MAX_VERSION: u32 = 9;
 427
 428    if version < WL_SEAT_MIN_VERSION {
 429        panic!(
 430            "wl_seat below required version: {} < {}",
 431            version, WL_SEAT_MIN_VERSION
 432        );
 433    }
 434
 435    version.clamp(WL_SEAT_MIN_VERSION, WL_SEAT_MAX_VERSION)
 436}
 437
 438fn wl_output_version(version: u32) -> u32 {
 439    const WL_OUTPUT_MIN_VERSION: u32 = 2;
 440    const WL_OUTPUT_MAX_VERSION: u32 = 4;
 441
 442    if version < WL_OUTPUT_MIN_VERSION {
 443        panic!(
 444            "wl_output below required version: {} < {}",
 445            version, WL_OUTPUT_MIN_VERSION
 446        );
 447    }
 448
 449    version.clamp(WL_OUTPUT_MIN_VERSION, WL_OUTPUT_MAX_VERSION)
 450}
 451
 452impl WaylandClient {
 453    pub(crate) fn new(liveness: std::sync::Weak<()>) -> Self {
 454        let conn = Connection::connect_to_env().unwrap();
 455
 456        let (globals, mut event_queue) =
 457            registry_queue_init::<WaylandClientStatePtr>(&conn).unwrap();
 458        let qh = event_queue.handle();
 459
 460        let mut seat: Option<wl_seat::WlSeat> = None;
 461        #[allow(clippy::mutable_key_type)]
 462        let mut in_progress_outputs = HashMap::default();
 463        globals.contents().with_list(|list| {
 464            for global in list {
 465                match &global.interface[..] {
 466                    "wl_seat" => {
 467                        seat = Some(globals.registry().bind::<wl_seat::WlSeat, _, _>(
 468                            global.name,
 469                            wl_seat_version(global.version),
 470                            &qh,
 471                            (),
 472                        ));
 473                    }
 474                    "wl_output" => {
 475                        let output = globals.registry().bind::<wl_output::WlOutput, _, _>(
 476                            global.name,
 477                            wl_output_version(global.version),
 478                            &qh,
 479                            (),
 480                        );
 481                        in_progress_outputs.insert(output.id(), InProgressOutput::default());
 482                    }
 483                    _ => {}
 484                }
 485            }
 486        });
 487
 488        let event_loop = EventLoop::<WaylandClientStatePtr>::try_new().unwrap();
 489
 490        let (common, main_receiver) = LinuxCommon::new(event_loop.get_signal(), liveness);
 491
 492        let handle = event_loop.handle();
 493        handle
 494            .insert_source(main_receiver, {
 495                let handle = handle.clone();
 496                move |event, _, _: &mut WaylandClientStatePtr| {
 497                    if let calloop::channel::Event::Msg(runnable) = event {
 498                        handle.insert_idle(move |_| {
 499                            runnable.run_and_profile();
 500                        });
 501                    }
 502                }
 503            })
 504            .unwrap();
 505
 506        // This could be unified with the notification handling in zed/main:fail_to_open_window.
 507        let gpu_context = BladeContext::new().notify_err("Unable to init GPU context");
 508
 509        let seat = seat.unwrap();
 510        let globals = Globals::new(
 511            globals,
 512            common.foreground_executor.clone(),
 513            qh.clone(),
 514            seat.clone(),
 515        );
 516
 517        let data_device = globals
 518            .data_device_manager
 519            .as_ref()
 520            .map(|data_device_manager| data_device_manager.get_data_device(&seat, &qh, ()));
 521
 522        let primary_selection = globals
 523            .primary_selection_manager
 524            .as_ref()
 525            .map(|primary_selection_manager| primary_selection_manager.get_device(&seat, &qh, ()));
 526
 527        let mut cursor = Cursor::new(&conn, &globals, 24);
 528
 529        handle
 530            .insert_source(XDPEventSource::new(&common.background_executor), {
 531                move |event, _, client| match event {
 532                    XDPEvent::WindowAppearance(appearance) => {
 533                        if let Some(client) = client.0.upgrade() {
 534                            let mut client = client.borrow_mut();
 535
 536                            client.common.appearance = appearance;
 537
 538                            for window in client.windows.values_mut() {
 539                                window.set_appearance(appearance);
 540                            }
 541                        }
 542                    }
 543                    XDPEvent::CursorTheme(theme) => {
 544                        if let Some(client) = client.0.upgrade() {
 545                            let mut client = client.borrow_mut();
 546                            client.cursor.set_theme(theme);
 547                        }
 548                    }
 549                    XDPEvent::CursorSize(size) => {
 550                        if let Some(client) = client.0.upgrade() {
 551                            let mut client = client.borrow_mut();
 552                            client.cursor.set_size(size);
 553                        }
 554                    }
 555                }
 556            })
 557            .unwrap();
 558
 559        let mut state = Rc::new(RefCell::new(WaylandClientState {
 560            serial_tracker: SerialTracker::new(),
 561            globals,
 562            gpu_context,
 563            wl_seat: seat,
 564            wl_pointer: None,
 565            wl_keyboard: None,
 566            cursor_shape_device: None,
 567            data_device,
 568            primary_selection,
 569            text_input: None,
 570            pre_edit_text: None,
 571            ime_pre_edit: None,
 572            composing: false,
 573            outputs: HashMap::default(),
 574            in_progress_outputs,
 575            windows: HashMap::default(),
 576            common,
 577            keyboard_layout: LinuxKeyboardLayout::new(UNKNOWN_KEYBOARD_LAYOUT_NAME),
 578            keymap_state: None,
 579            compose_state: None,
 580            drag: DragState {
 581                data_offer: None,
 582                window: None,
 583                position: Point::default(),
 584            },
 585            click: ClickState {
 586                last_click: Instant::now(),
 587                last_mouse_button: None,
 588                last_location: Point::default(),
 589                current_count: 0,
 590            },
 591            repeat: KeyRepeat {
 592                characters_per_second: 16,
 593                delay: Duration::from_millis(500),
 594                current_id: 0,
 595                current_keycode: None,
 596            },
 597            modifiers: Modifiers {
 598                shift: false,
 599                control: false,
 600                alt: false,
 601                function: false,
 602                platform: false,
 603            },
 604            capslock: Capslock { on: false },
 605            scroll_event_received: false,
 606            axis_source: AxisSource::Wheel,
 607            mouse_location: None,
 608            continuous_scroll_delta: None,
 609            discrete_scroll_delta: None,
 610            vertical_modifier: -1.0,
 611            horizontal_modifier: -1.0,
 612            button_pressed: None,
 613            mouse_focused_window: None,
 614            keyboard_focused_window: None,
 615            loop_handle: handle.clone(),
 616            enter_token: None,
 617            cursor_style: None,
 618            clipboard: Clipboard::new(conn.clone(), handle.clone()),
 619            data_offers: Vec::new(),
 620            primary_data_offer: None,
 621            cursor,
 622            pending_activation: None,
 623            event_loop: Some(event_loop),
 624        }));
 625
 626        WaylandSource::new(conn, event_queue)
 627            .insert(handle)
 628            .unwrap();
 629
 630        Self(state)
 631    }
 632}
 633
 634impl LinuxClient for WaylandClient {
 635    fn keyboard_layout(&self) -> Box<dyn PlatformKeyboardLayout> {
 636        Box::new(self.0.borrow().keyboard_layout.clone())
 637    }
 638
 639    fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
 640        self.0
 641            .borrow()
 642            .outputs
 643            .iter()
 644            .map(|(id, output)| {
 645                Rc::new(WaylandDisplay {
 646                    id: id.clone(),
 647                    name: output.name.clone(),
 648                    bounds: output.bounds.to_pixels(output.scale as f32),
 649                }) as Rc<dyn PlatformDisplay>
 650            })
 651            .collect()
 652    }
 653
 654    fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
 655        self.0
 656            .borrow()
 657            .outputs
 658            .iter()
 659            .find_map(|(object_id, output)| {
 660                (object_id.protocol_id() == id.0).then(|| {
 661                    Rc::new(WaylandDisplay {
 662                        id: object_id.clone(),
 663                        name: output.name.clone(),
 664                        bounds: output.bounds.to_pixels(output.scale as f32),
 665                    }) as Rc<dyn PlatformDisplay>
 666                })
 667            })
 668    }
 669
 670    fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>> {
 671        None
 672    }
 673
 674    #[cfg(feature = "screen-capture")]
 675    fn is_screen_capture_supported(&self) -> bool {
 676        false
 677    }
 678
 679    #[cfg(feature = "screen-capture")]
 680    fn screen_capture_sources(
 681        &self,
 682    ) -> futures::channel::oneshot::Receiver<anyhow::Result<Vec<Rc<dyn crate::ScreenCaptureSource>>>>
 683    {
 684        // TODO: Get screen capture working on wayland. Be sure to try window resizing as that may
 685        // be tricky.
 686        //
 687        // start_scap_default_target_source()
 688        let (sources_tx, sources_rx) = futures::channel::oneshot::channel();
 689        sources_tx
 690            .send(Err(anyhow::anyhow!(
 691                "Wayland screen capture not yet implemented."
 692            )))
 693            .ok();
 694        sources_rx
 695    }
 696
 697    fn open_window(
 698        &self,
 699        handle: AnyWindowHandle,
 700        params: WindowParams,
 701    ) -> anyhow::Result<Box<dyn PlatformWindow>> {
 702        let mut state = self.0.borrow_mut();
 703
 704        let parent = state.keyboard_focused_window.clone();
 705
 706        let (window, surface_id) = WaylandWindow::new(
 707            handle,
 708            state.globals.clone(),
 709            &state.gpu_context,
 710            WaylandClientStatePtr(Rc::downgrade(&self.0)),
 711            params,
 712            state.common.appearance,
 713            parent,
 714        )?;
 715        state.windows.insert(surface_id, window.0.clone());
 716
 717        Ok(Box::new(window))
 718    }
 719
 720    fn set_cursor_style(&self, style: CursorStyle) {
 721        let mut state = self.0.borrow_mut();
 722
 723        let need_update = state.cursor_style != Some(style)
 724            && (state.mouse_focused_window.is_none()
 725                || state
 726                    .mouse_focused_window
 727                    .as_ref()
 728                    .is_some_and(|w| !w.is_blocked()));
 729
 730        if need_update {
 731            let serial = state.serial_tracker.get(SerialKind::MouseEnter);
 732            state.cursor_style = Some(style);
 733
 734            if let CursorStyle::None = style {
 735                let wl_pointer = state
 736                    .wl_pointer
 737                    .clone()
 738                    .expect("window is focused by pointer");
 739                wl_pointer.set_cursor(serial, None, 0, 0);
 740            } else if let Some(cursor_shape_device) = &state.cursor_shape_device {
 741                cursor_shape_device.set_shape(serial, style.to_shape());
 742            } else if let Some(focused_window) = &state.mouse_focused_window {
 743                // cursor-shape-v1 isn't supported, set the cursor using a surface.
 744                let wl_pointer = state
 745                    .wl_pointer
 746                    .clone()
 747                    .expect("window is focused by pointer");
 748                let scale = focused_window.primary_output_scale();
 749                state
 750                    .cursor
 751                    .set_icon(&wl_pointer, serial, style.to_icon_names(), scale);
 752            }
 753        }
 754    }
 755
 756    fn open_uri(&self, uri: &str) {
 757        let mut state = self.0.borrow_mut();
 758        if let (Some(activation), Some(window)) = (
 759            state.globals.activation.clone(),
 760            state.mouse_focused_window.clone(),
 761        ) {
 762            state.pending_activation = Some(PendingActivation::Uri(uri.to_string()));
 763            let token = activation.get_activation_token(&state.globals.qh, ());
 764            let serial = state.serial_tracker.get(SerialKind::MousePress);
 765            token.set_serial(serial, &state.wl_seat);
 766            token.set_surface(&window.surface());
 767            token.commit();
 768        } else {
 769            let executor = state.common.background_executor.clone();
 770            open_uri_internal(executor, uri, None);
 771        }
 772    }
 773
 774    fn reveal_path(&self, path: PathBuf) {
 775        let mut state = self.0.borrow_mut();
 776        if let (Some(activation), Some(window)) = (
 777            state.globals.activation.clone(),
 778            state.mouse_focused_window.clone(),
 779        ) {
 780            state.pending_activation = Some(PendingActivation::Path(path));
 781            let token = activation.get_activation_token(&state.globals.qh, ());
 782            let serial = state.serial_tracker.get(SerialKind::MousePress);
 783            token.set_serial(serial, &state.wl_seat);
 784            token.set_surface(&window.surface());
 785            token.commit();
 786        } else {
 787            let executor = state.common.background_executor.clone();
 788            reveal_path_internal(executor, path, None);
 789        }
 790    }
 791
 792    fn with_common<R>(&self, f: impl FnOnce(&mut LinuxCommon) -> R) -> R {
 793        f(&mut self.0.borrow_mut().common)
 794    }
 795
 796    fn run(&self) {
 797        let mut event_loop = self
 798            .0
 799            .borrow_mut()
 800            .event_loop
 801            .take()
 802            .expect("App is already running");
 803
 804        event_loop
 805            .run(
 806                None,
 807                &mut WaylandClientStatePtr(Rc::downgrade(&self.0)),
 808                |_| {},
 809            )
 810            .log_err();
 811    }
 812
 813    fn write_to_primary(&self, item: crate::ClipboardItem) {
 814        let mut state = self.0.borrow_mut();
 815        let (Some(primary_selection_manager), Some(primary_selection)) = (
 816            state.globals.primary_selection_manager.clone(),
 817            state.primary_selection.clone(),
 818        ) else {
 819            return;
 820        };
 821        if state.mouse_focused_window.is_some() || state.keyboard_focused_window.is_some() {
 822            state.clipboard.set_primary(item);
 823            let serial = state.serial_tracker.get(SerialKind::KeyPress);
 824            let data_source = primary_selection_manager.create_source(&state.globals.qh, ());
 825            for mime_type in TEXT_MIME_TYPES {
 826                data_source.offer(mime_type.to_string());
 827            }
 828            data_source.offer(state.clipboard.self_mime());
 829            primary_selection.set_selection(Some(&data_source), serial);
 830        }
 831    }
 832
 833    fn write_to_clipboard(&self, item: crate::ClipboardItem) {
 834        let mut state = self.0.borrow_mut();
 835        let (Some(data_device_manager), Some(data_device)) = (
 836            state.globals.data_device_manager.clone(),
 837            state.data_device.clone(),
 838        ) else {
 839            return;
 840        };
 841        if state.mouse_focused_window.is_some() || state.keyboard_focused_window.is_some() {
 842            state.clipboard.set(item);
 843            let serial = state.serial_tracker.get(SerialKind::KeyPress);
 844            let data_source = data_device_manager.create_data_source(&state.globals.qh, ());
 845            for mime_type in TEXT_MIME_TYPES {
 846                data_source.offer(mime_type.to_string());
 847            }
 848            data_source.offer(state.clipboard.self_mime());
 849            data_device.set_selection(Some(&data_source), serial);
 850        }
 851    }
 852
 853    fn read_from_primary(&self) -> Option<crate::ClipboardItem> {
 854        self.0.borrow_mut().clipboard.read_primary()
 855    }
 856
 857    fn read_from_clipboard(&self) -> Option<crate::ClipboardItem> {
 858        self.0.borrow_mut().clipboard.read()
 859    }
 860
 861    fn active_window(&self) -> Option<AnyWindowHandle> {
 862        self.0
 863            .borrow_mut()
 864            .keyboard_focused_window
 865            .as_ref()
 866            .map(|window| window.handle())
 867    }
 868
 869    fn window_stack(&self) -> Option<Vec<AnyWindowHandle>> {
 870        None
 871    }
 872
 873    fn compositor_name(&self) -> &'static str {
 874        "Wayland"
 875    }
 876
 877    fn window_identifier(&self) -> impl Future<Output = Option<WindowIdentifier>> + Send + 'static {
 878        async fn inner(surface: Option<wl_surface::WlSurface>) -> Option<WindowIdentifier> {
 879            if let Some(surface) = surface {
 880                ashpd::WindowIdentifier::from_wayland(&surface).await
 881            } else {
 882                None
 883            }
 884        }
 885
 886        let client_state = self.0.borrow();
 887        let active_window = client_state.keyboard_focused_window.as_ref();
 888        inner(active_window.map(|aw| aw.surface()))
 889    }
 890}
 891
 892impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for WaylandClientStatePtr {
 893    fn event(
 894        this: &mut Self,
 895        registry: &wl_registry::WlRegistry,
 896        event: wl_registry::Event,
 897        _: &GlobalListContents,
 898        _: &Connection,
 899        qh: &QueueHandle<Self>,
 900    ) {
 901        let mut client = this.get_client();
 902        let mut state = client.borrow_mut();
 903
 904        match event {
 905            wl_registry::Event::Global {
 906                name,
 907                interface,
 908                version,
 909            } => match &interface[..] {
 910                "wl_seat" => {
 911                    if let Some(wl_pointer) = state.wl_pointer.take() {
 912                        wl_pointer.release();
 913                    }
 914                    if let Some(wl_keyboard) = state.wl_keyboard.take() {
 915                        wl_keyboard.release();
 916                    }
 917                    state.wl_seat.release();
 918                    state.wl_seat = registry.bind::<wl_seat::WlSeat, _, _>(
 919                        name,
 920                        wl_seat_version(version),
 921                        qh,
 922                        (),
 923                    );
 924                }
 925                "wl_output" => {
 926                    let output = registry.bind::<wl_output::WlOutput, _, _>(
 927                        name,
 928                        wl_output_version(version),
 929                        qh,
 930                        (),
 931                    );
 932
 933                    state
 934                        .in_progress_outputs
 935                        .insert(output.id(), InProgressOutput::default());
 936                }
 937                _ => {}
 938            },
 939            wl_registry::Event::GlobalRemove { name: _ } => {
 940                // TODO: handle global removal
 941            }
 942            _ => {}
 943        }
 944    }
 945}
 946
 947delegate_noop!(WaylandClientStatePtr: ignore xdg_activation_v1::XdgActivationV1);
 948delegate_noop!(WaylandClientStatePtr: ignore wl_compositor::WlCompositor);
 949delegate_noop!(WaylandClientStatePtr: ignore wp_cursor_shape_device_v1::WpCursorShapeDeviceV1);
 950delegate_noop!(WaylandClientStatePtr: ignore wp_cursor_shape_manager_v1::WpCursorShapeManagerV1);
 951delegate_noop!(WaylandClientStatePtr: ignore wl_data_device_manager::WlDataDeviceManager);
 952delegate_noop!(WaylandClientStatePtr: ignore zwp_primary_selection_device_manager_v1::ZwpPrimarySelectionDeviceManagerV1);
 953delegate_noop!(WaylandClientStatePtr: ignore wl_shm::WlShm);
 954delegate_noop!(WaylandClientStatePtr: ignore wl_shm_pool::WlShmPool);
 955delegate_noop!(WaylandClientStatePtr: ignore wl_buffer::WlBuffer);
 956delegate_noop!(WaylandClientStatePtr: ignore wl_region::WlRegion);
 957delegate_noop!(WaylandClientStatePtr: ignore wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1);
 958delegate_noop!(WaylandClientStatePtr: ignore zxdg_decoration_manager_v1::ZxdgDecorationManagerV1);
 959delegate_noop!(WaylandClientStatePtr: ignore zwlr_layer_shell_v1::ZwlrLayerShellV1);
 960delegate_noop!(WaylandClientStatePtr: ignore org_kde_kwin_blur_manager::OrgKdeKwinBlurManager);
 961delegate_noop!(WaylandClientStatePtr: ignore zwp_text_input_manager_v3::ZwpTextInputManagerV3);
 962delegate_noop!(WaylandClientStatePtr: ignore org_kde_kwin_blur::OrgKdeKwinBlur);
 963delegate_noop!(WaylandClientStatePtr: ignore wp_viewporter::WpViewporter);
 964delegate_noop!(WaylandClientStatePtr: ignore wp_viewport::WpViewport);
 965
 966impl Dispatch<WlCallback, ObjectId> for WaylandClientStatePtr {
 967    fn event(
 968        state: &mut WaylandClientStatePtr,
 969        _: &wl_callback::WlCallback,
 970        event: wl_callback::Event,
 971        surface_id: &ObjectId,
 972        _: &Connection,
 973        _: &QueueHandle<Self>,
 974    ) {
 975        let client = state.get_client();
 976        let mut state = client.borrow_mut();
 977        let Some(window) = get_window(&mut state, surface_id) else {
 978            return;
 979        };
 980        drop(state);
 981
 982        if let wl_callback::Event::Done { .. } = event {
 983            window.frame();
 984        }
 985    }
 986}
 987
 988pub(crate) fn get_window(
 989    mut state: &mut RefMut<WaylandClientState>,
 990    surface_id: &ObjectId,
 991) -> Option<WaylandWindowStatePtr> {
 992    state.windows.get(surface_id).cloned()
 993}
 994
 995impl Dispatch<wl_surface::WlSurface, ()> for WaylandClientStatePtr {
 996    fn event(
 997        this: &mut Self,
 998        surface: &wl_surface::WlSurface,
 999        event: <wl_surface::WlSurface as Proxy>::Event,
1000        _: &(),
1001        _: &Connection,
1002        _: &QueueHandle<Self>,
1003    ) {
1004        let mut client = this.get_client();
1005        let mut state = client.borrow_mut();
1006
1007        let Some(window) = get_window(&mut state, &surface.id()) else {
1008            return;
1009        };
1010        #[allow(clippy::mutable_key_type)]
1011        let outputs = state.outputs.clone();
1012        drop(state);
1013
1014        window.handle_surface_event(event, outputs);
1015    }
1016}
1017
1018impl Dispatch<wl_output::WlOutput, ()> for WaylandClientStatePtr {
1019    fn event(
1020        this: &mut Self,
1021        output: &wl_output::WlOutput,
1022        event: <wl_output::WlOutput as Proxy>::Event,
1023        _: &(),
1024        _: &Connection,
1025        _: &QueueHandle<Self>,
1026    ) {
1027        let mut client = this.get_client();
1028        let mut state = client.borrow_mut();
1029
1030        let Some(mut in_progress_output) = state.in_progress_outputs.get_mut(&output.id()) else {
1031            return;
1032        };
1033
1034        match event {
1035            wl_output::Event::Name { name } => {
1036                in_progress_output.name = Some(name);
1037            }
1038            wl_output::Event::Scale { factor } => {
1039                in_progress_output.scale = Some(factor);
1040            }
1041            wl_output::Event::Geometry { x, y, .. } => {
1042                in_progress_output.position = Some(point(DevicePixels(x), DevicePixels(y)))
1043            }
1044            wl_output::Event::Mode { width, height, .. } => {
1045                in_progress_output.size = Some(size(DevicePixels(width), DevicePixels(height)))
1046            }
1047            wl_output::Event::Done => {
1048                if let Some(complete) = in_progress_output.complete() {
1049                    state.outputs.insert(output.id(), complete);
1050                }
1051                state.in_progress_outputs.remove(&output.id());
1052            }
1053            _ => {}
1054        }
1055    }
1056}
1057
1058impl Dispatch<xdg_surface::XdgSurface, ObjectId> for WaylandClientStatePtr {
1059    fn event(
1060        state: &mut Self,
1061        _: &xdg_surface::XdgSurface,
1062        event: xdg_surface::Event,
1063        surface_id: &ObjectId,
1064        _: &Connection,
1065        _: &QueueHandle<Self>,
1066    ) {
1067        let client = state.get_client();
1068        let mut state = client.borrow_mut();
1069        let Some(window) = get_window(&mut state, surface_id) else {
1070            return;
1071        };
1072        drop(state);
1073        window.handle_xdg_surface_event(event);
1074    }
1075}
1076
1077impl Dispatch<xdg_toplevel::XdgToplevel, ObjectId> for WaylandClientStatePtr {
1078    fn event(
1079        this: &mut Self,
1080        _: &xdg_toplevel::XdgToplevel,
1081        event: <xdg_toplevel::XdgToplevel as Proxy>::Event,
1082        surface_id: &ObjectId,
1083        _: &Connection,
1084        _: &QueueHandle<Self>,
1085    ) {
1086        let client = this.get_client();
1087        let mut state = client.borrow_mut();
1088        let Some(window) = get_window(&mut state, surface_id) else {
1089            return;
1090        };
1091
1092        drop(state);
1093        let should_close = window.handle_toplevel_event(event);
1094
1095        if should_close {
1096            // The close logic will be handled in drop_window()
1097            window.close();
1098        }
1099    }
1100}
1101
1102impl Dispatch<zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, ObjectId> for WaylandClientStatePtr {
1103    fn event(
1104        this: &mut Self,
1105        _: &zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,
1106        event: <zwlr_layer_surface_v1::ZwlrLayerSurfaceV1 as Proxy>::Event,
1107        surface_id: &ObjectId,
1108        _: &Connection,
1109        _: &QueueHandle<Self>,
1110    ) {
1111        let client = this.get_client();
1112        let mut state = client.borrow_mut();
1113        let Some(window) = get_window(&mut state, surface_id) else {
1114            return;
1115        };
1116
1117        drop(state);
1118        let should_close = window.handle_layersurface_event(event);
1119
1120        if should_close {
1121            // The close logic will be handled in drop_window()
1122            window.close();
1123        }
1124    }
1125}
1126
1127impl Dispatch<xdg_wm_base::XdgWmBase, ()> for WaylandClientStatePtr {
1128    fn event(
1129        _: &mut Self,
1130        wm_base: &xdg_wm_base::XdgWmBase,
1131        event: <xdg_wm_base::XdgWmBase as Proxy>::Event,
1132        _: &(),
1133        _: &Connection,
1134        _: &QueueHandle<Self>,
1135    ) {
1136        if let xdg_wm_base::Event::Ping { serial } = event {
1137            wm_base.pong(serial);
1138        }
1139    }
1140}
1141
1142impl Dispatch<xdg_activation_token_v1::XdgActivationTokenV1, ()> for WaylandClientStatePtr {
1143    fn event(
1144        this: &mut Self,
1145        token: &xdg_activation_token_v1::XdgActivationTokenV1,
1146        event: <xdg_activation_token_v1::XdgActivationTokenV1 as Proxy>::Event,
1147        _: &(),
1148        _: &Connection,
1149        _: &QueueHandle<Self>,
1150    ) {
1151        let client = this.get_client();
1152        let mut state = client.borrow_mut();
1153
1154        if let xdg_activation_token_v1::Event::Done { token } = event {
1155            let executor = state.common.background_executor.clone();
1156            match state.pending_activation.take() {
1157                Some(PendingActivation::Uri(uri)) => open_uri_internal(executor, &uri, Some(token)),
1158                Some(PendingActivation::Path(path)) => {
1159                    reveal_path_internal(executor, path, Some(token))
1160                }
1161                Some(PendingActivation::Window(window)) => {
1162                    let Some(window) = get_window(&mut state, &window) else {
1163                        return;
1164                    };
1165                    let activation = state.globals.activation.as_ref().unwrap();
1166                    activation.activate(token, &window.surface());
1167                }
1168                None => log::error!("activation token received with no pending activation"),
1169            }
1170        }
1171
1172        token.destroy();
1173    }
1174}
1175
1176impl Dispatch<wl_seat::WlSeat, ()> for WaylandClientStatePtr {
1177    fn event(
1178        state: &mut Self,
1179        seat: &wl_seat::WlSeat,
1180        event: wl_seat::Event,
1181        _: &(),
1182        _: &Connection,
1183        qh: &QueueHandle<Self>,
1184    ) {
1185        if let wl_seat::Event::Capabilities {
1186            capabilities: WEnum::Value(capabilities),
1187        } = event
1188        {
1189            let client = state.get_client();
1190            let mut state = client.borrow_mut();
1191            if capabilities.contains(wl_seat::Capability::Keyboard) {
1192                let keyboard = seat.get_keyboard(qh, ());
1193
1194                state.text_input = state
1195                    .globals
1196                    .text_input_manager
1197                    .as_ref()
1198                    .map(|text_input_manager| text_input_manager.get_text_input(seat, qh, ()));
1199
1200                if let Some(wl_keyboard) = &state.wl_keyboard {
1201                    wl_keyboard.release();
1202                }
1203
1204                state.wl_keyboard = Some(keyboard);
1205            }
1206            if capabilities.contains(wl_seat::Capability::Pointer) {
1207                let pointer = seat.get_pointer(qh, ());
1208                state.cursor_shape_device = state
1209                    .globals
1210                    .cursor_shape_manager
1211                    .as_ref()
1212                    .map(|cursor_shape_manager| cursor_shape_manager.get_pointer(&pointer, qh, ()));
1213
1214                if let Some(wl_pointer) = &state.wl_pointer {
1215                    wl_pointer.release();
1216                }
1217
1218                state.wl_pointer = Some(pointer);
1219            }
1220        }
1221    }
1222}
1223
1224impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClientStatePtr {
1225    fn event(
1226        this: &mut Self,
1227        _: &wl_keyboard::WlKeyboard,
1228        event: wl_keyboard::Event,
1229        _: &(),
1230        _: &Connection,
1231        _: &QueueHandle<Self>,
1232    ) {
1233        let mut client = this.get_client();
1234        let mut state = client.borrow_mut();
1235        match event {
1236            wl_keyboard::Event::RepeatInfo { rate, delay } => {
1237                state.repeat.characters_per_second = rate as u32;
1238                state.repeat.delay = Duration::from_millis(delay as u64);
1239            }
1240            wl_keyboard::Event::Keymap {
1241                format: WEnum::Value(format),
1242                fd,
1243                size,
1244                ..
1245            } => {
1246                if format != wl_keyboard::KeymapFormat::XkbV1 {
1247                    log::error!("Received keymap format {:?}, expected XkbV1", format);
1248                    return;
1249                }
1250                let xkb_context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
1251                let keymap = unsafe {
1252                    xkb::Keymap::new_from_fd(
1253                        &xkb_context,
1254                        fd,
1255                        size as usize,
1256                        XKB_KEYMAP_FORMAT_TEXT_V1,
1257                        KEYMAP_COMPILE_NO_FLAGS,
1258                    )
1259                    .log_err()
1260                    .flatten()
1261                    .expect("Failed to create keymap")
1262                };
1263                state.keymap_state = Some(xkb::State::new(&keymap));
1264                state.compose_state = get_xkb_compose_state(&xkb_context);
1265                drop(state);
1266
1267                this.handle_keyboard_layout_change();
1268            }
1269            wl_keyboard::Event::Enter { surface, .. } => {
1270                state.keyboard_focused_window = get_window(&mut state, &surface.id());
1271                state.enter_token = Some(());
1272
1273                if let Some(window) = state.keyboard_focused_window.clone() {
1274                    drop(state);
1275                    window.set_focused(true);
1276                }
1277            }
1278            wl_keyboard::Event::Leave { surface, .. } => {
1279                let keyboard_focused_window = get_window(&mut state, &surface.id());
1280                state.keyboard_focused_window = None;
1281                state.enter_token.take();
1282                // Prevent keyboard events from repeating after opening e.g. a file chooser and closing it quickly
1283                state.repeat.current_id += 1;
1284
1285                if let Some(window) = keyboard_focused_window {
1286                    if let Some(ref mut compose) = state.compose_state {
1287                        compose.reset();
1288                    }
1289                    state.pre_edit_text.take();
1290                    drop(state);
1291                    window.handle_ime(ImeInput::DeleteText);
1292                    window.set_focused(false);
1293                }
1294            }
1295            wl_keyboard::Event::Modifiers {
1296                mods_depressed,
1297                mods_latched,
1298                mods_locked,
1299                group,
1300                ..
1301            } => {
1302                let focused_window = state.keyboard_focused_window.clone();
1303
1304                let keymap_state = state.keymap_state.as_mut().unwrap();
1305                let old_layout =
1306                    keymap_state.serialize_layout(xkbcommon::xkb::STATE_LAYOUT_EFFECTIVE);
1307                keymap_state.update_mask(mods_depressed, mods_latched, mods_locked, 0, 0, group);
1308                state.modifiers = Modifiers::from_xkb(keymap_state);
1309                let keymap_state = state.keymap_state.as_mut().unwrap();
1310                state.capslock = Capslock::from_xkb(keymap_state);
1311
1312                let input = PlatformInput::ModifiersChanged(ModifiersChangedEvent {
1313                    modifiers: state.modifiers,
1314                    capslock: state.capslock,
1315                });
1316                drop(state);
1317
1318                if let Some(focused_window) = focused_window {
1319                    focused_window.handle_input(input);
1320                }
1321
1322                if group != old_layout {
1323                    this.handle_keyboard_layout_change();
1324                }
1325            }
1326            wl_keyboard::Event::Key {
1327                serial,
1328                key,
1329                state: WEnum::Value(key_state),
1330                ..
1331            } => {
1332                state.serial_tracker.update(SerialKind::KeyPress, serial);
1333
1334                let focused_window = state.keyboard_focused_window.clone();
1335                let Some(focused_window) = focused_window else {
1336                    return;
1337                };
1338
1339                let keymap_state = state.keymap_state.as_ref().unwrap();
1340                let keycode = Keycode::from(key + MIN_KEYCODE);
1341                let keysym = keymap_state.key_get_one_sym(keycode);
1342
1343                match key_state {
1344                    wl_keyboard::KeyState::Pressed if !keysym.is_modifier_key() => {
1345                        let mut keystroke =
1346                            Keystroke::from_xkb(keymap_state, state.modifiers, keycode);
1347                        if let Some(mut compose) = state.compose_state.take() {
1348                            compose.feed(keysym);
1349                            match compose.status() {
1350                                xkb::Status::Composing => {
1351                                    keystroke.key_char = None;
1352                                    state.pre_edit_text =
1353                                        compose.utf8().or(Keystroke::underlying_dead_key(keysym));
1354                                    let pre_edit =
1355                                        state.pre_edit_text.clone().unwrap_or(String::default());
1356                                    drop(state);
1357                                    focused_window.handle_ime(ImeInput::SetMarkedText(pre_edit));
1358                                    state = client.borrow_mut();
1359                                }
1360
1361                                xkb::Status::Composed => {
1362                                    state.pre_edit_text.take();
1363                                    keystroke.key_char = compose.utf8();
1364                                    if let Some(keysym) = compose.keysym() {
1365                                        keystroke.key = xkb::keysym_get_name(keysym);
1366                                    }
1367                                }
1368                                xkb::Status::Cancelled => {
1369                                    let pre_edit = state.pre_edit_text.take();
1370                                    let new_pre_edit = Keystroke::underlying_dead_key(keysym);
1371                                    state.pre_edit_text = new_pre_edit.clone();
1372                                    drop(state);
1373                                    if let Some(pre_edit) = pre_edit {
1374                                        focused_window.handle_ime(ImeInput::InsertText(pre_edit));
1375                                    }
1376                                    if let Some(current_key) = new_pre_edit {
1377                                        focused_window
1378                                            .handle_ime(ImeInput::SetMarkedText(current_key));
1379                                    }
1380                                    compose.feed(keysym);
1381                                    state = client.borrow_mut();
1382                                }
1383                                _ => {}
1384                            }
1385                            state.compose_state = Some(compose);
1386                        }
1387                        let input = PlatformInput::KeyDown(KeyDownEvent {
1388                            keystroke: keystroke.clone(),
1389                            is_held: false,
1390                            prefer_character_input: false,
1391                        });
1392
1393                        state.repeat.current_id += 1;
1394                        state.repeat.current_keycode = Some(keycode);
1395
1396                        let rate = state.repeat.characters_per_second;
1397                        let repeat_interval = Duration::from_secs(1) / rate.max(1);
1398                        let id = state.repeat.current_id;
1399                        state
1400                            .loop_handle
1401                            .insert_source(Timer::from_duration(state.repeat.delay), {
1402                                let input = PlatformInput::KeyDown(KeyDownEvent {
1403                                    keystroke,
1404                                    is_held: true,
1405                                    prefer_character_input: false,
1406                                });
1407                                move |event_timestamp, _metadata, this| {
1408                                    let mut client = this.get_client();
1409                                    let mut state = client.borrow_mut();
1410                                    let is_repeating = id == state.repeat.current_id
1411                                        && state.repeat.current_keycode.is_some()
1412                                        && state.keyboard_focused_window.is_some();
1413
1414                                    if !is_repeating || rate == 0 {
1415                                        return TimeoutAction::Drop;
1416                                    }
1417
1418                                    let focused_window =
1419                                        state.keyboard_focused_window.as_ref().unwrap().clone();
1420
1421                                    drop(state);
1422                                    focused_window.handle_input(input.clone());
1423
1424                                    // If the new scheduled time is in the past the event will repeat as soon as possible
1425                                    TimeoutAction::ToInstant(event_timestamp + repeat_interval)
1426                                }
1427                            })
1428                            .unwrap();
1429
1430                        drop(state);
1431                        focused_window.handle_input(input);
1432                    }
1433                    wl_keyboard::KeyState::Released if !keysym.is_modifier_key() => {
1434                        let input = PlatformInput::KeyUp(KeyUpEvent {
1435                            keystroke: Keystroke::from_xkb(keymap_state, state.modifiers, keycode),
1436                        });
1437
1438                        if state.repeat.current_keycode == Some(keycode) {
1439                            state.repeat.current_keycode = None;
1440                        }
1441
1442                        drop(state);
1443                        focused_window.handle_input(input);
1444                    }
1445                    _ => {}
1446                }
1447            }
1448            _ => {}
1449        }
1450    }
1451}
1452
1453impl Dispatch<zwp_text_input_v3::ZwpTextInputV3, ()> for WaylandClientStatePtr {
1454    fn event(
1455        this: &mut Self,
1456        text_input: &zwp_text_input_v3::ZwpTextInputV3,
1457        event: <zwp_text_input_v3::ZwpTextInputV3 as Proxy>::Event,
1458        _: &(),
1459        _: &Connection,
1460        _: &QueueHandle<Self>,
1461    ) {
1462        let client = this.get_client();
1463        let mut state = client.borrow_mut();
1464        match event {
1465            zwp_text_input_v3::Event::Enter { .. } => {
1466                drop(state);
1467                this.enable_ime();
1468            }
1469            zwp_text_input_v3::Event::Leave { .. } => {
1470                drop(state);
1471                this.disable_ime();
1472            }
1473            zwp_text_input_v3::Event::CommitString { text } => {
1474                state.composing = false;
1475                let Some(window) = state.keyboard_focused_window.clone() else {
1476                    return;
1477                };
1478
1479                if let Some(commit_text) = text {
1480                    drop(state);
1481                    // IBus Intercepts keys like `a`, `b`, but those keys are needed for vim mode.
1482                    // We should only send ASCII characters to Zed, otherwise a user could remap a letter like `か` or `相`.
1483                    if commit_text.len() == 1 {
1484                        window.handle_input(PlatformInput::KeyDown(KeyDownEvent {
1485                            keystroke: Keystroke {
1486                                modifiers: Modifiers::default(),
1487                                key: commit_text.clone(),
1488                                key_char: Some(commit_text),
1489                            },
1490                            is_held: false,
1491                            prefer_character_input: false,
1492                        }));
1493                    } else {
1494                        window.handle_ime(ImeInput::InsertText(commit_text));
1495                    }
1496                }
1497            }
1498            zwp_text_input_v3::Event::PreeditString { text, .. } => {
1499                state.composing = true;
1500                state.ime_pre_edit = text;
1501            }
1502            zwp_text_input_v3::Event::Done { serial } => {
1503                let last_serial = state.serial_tracker.get(SerialKind::InputMethod);
1504                state.serial_tracker.update(SerialKind::InputMethod, serial);
1505                let Some(window) = state.keyboard_focused_window.clone() else {
1506                    return;
1507                };
1508
1509                if let Some(text) = state.ime_pre_edit.take() {
1510                    drop(state);
1511                    window.handle_ime(ImeInput::SetMarkedText(text));
1512                    if let Some(area) = window.get_ime_area() {
1513                        text_input.set_cursor_rectangle(
1514                            area.origin.x.0 as i32,
1515                            area.origin.y.0 as i32,
1516                            area.size.width.0 as i32,
1517                            area.size.height.0 as i32,
1518                        );
1519                        if last_serial == serial {
1520                            text_input.commit();
1521                        }
1522                    }
1523                } else {
1524                    state.composing = false;
1525                    drop(state);
1526                    window.handle_ime(ImeInput::DeleteText);
1527                }
1528            }
1529            _ => {}
1530        }
1531    }
1532}
1533
1534fn linux_button_to_gpui(button: u32) -> Option<MouseButton> {
1535    // These values are coming from <linux/input-event-codes.h>.
1536    const BTN_LEFT: u32 = 0x110;
1537    const BTN_RIGHT: u32 = 0x111;
1538    const BTN_MIDDLE: u32 = 0x112;
1539    const BTN_SIDE: u32 = 0x113;
1540    const BTN_EXTRA: u32 = 0x114;
1541    const BTN_FORWARD: u32 = 0x115;
1542    const BTN_BACK: u32 = 0x116;
1543
1544    Some(match button {
1545        BTN_LEFT => MouseButton::Left,
1546        BTN_RIGHT => MouseButton::Right,
1547        BTN_MIDDLE => MouseButton::Middle,
1548        BTN_BACK | BTN_SIDE => MouseButton::Navigate(NavigationDirection::Back),
1549        BTN_FORWARD | BTN_EXTRA => MouseButton::Navigate(NavigationDirection::Forward),
1550        _ => return None,
1551    })
1552}
1553
1554impl Dispatch<wl_pointer::WlPointer, ()> for WaylandClientStatePtr {
1555    fn event(
1556        this: &mut Self,
1557        wl_pointer: &wl_pointer::WlPointer,
1558        event: wl_pointer::Event,
1559        _: &(),
1560        _: &Connection,
1561        _: &QueueHandle<Self>,
1562    ) {
1563        let mut client = this.get_client();
1564        let mut state = client.borrow_mut();
1565
1566        match event {
1567            wl_pointer::Event::Enter {
1568                serial,
1569                surface,
1570                surface_x,
1571                surface_y,
1572                ..
1573            } => {
1574                state.serial_tracker.update(SerialKind::MouseEnter, serial);
1575                state.mouse_location = Some(point(px(surface_x as f32), px(surface_y as f32)));
1576                state.button_pressed = None;
1577
1578                if let Some(window) = get_window(&mut state, &surface.id()) {
1579                    state.mouse_focused_window = Some(window.clone());
1580
1581                    if state.enter_token.is_some() {
1582                        state.enter_token = None;
1583                    }
1584                    if let Some(style) = state.cursor_style {
1585                        if let CursorStyle::None = style {
1586                            let wl_pointer = state
1587                                .wl_pointer
1588                                .clone()
1589                                .expect("window is focused by pointer");
1590                            wl_pointer.set_cursor(serial, None, 0, 0);
1591                        } else if let Some(cursor_shape_device) = &state.cursor_shape_device {
1592                            cursor_shape_device.set_shape(serial, style.to_shape());
1593                        } else {
1594                            let scale = window.primary_output_scale();
1595                            state
1596                                .cursor
1597                                .set_icon(wl_pointer, serial, style.to_icon_names(), scale);
1598                        }
1599                    }
1600                    drop(state);
1601                    window.set_hovered(true);
1602                }
1603            }
1604            wl_pointer::Event::Leave { .. } => {
1605                if let Some(focused_window) = state.mouse_focused_window.clone() {
1606                    let input = PlatformInput::MouseExited(MouseExitEvent {
1607                        position: state.mouse_location.unwrap(),
1608                        pressed_button: state.button_pressed,
1609                        modifiers: state.modifiers,
1610                    });
1611                    state.mouse_focused_window = None;
1612                    state.mouse_location = None;
1613                    state.button_pressed = None;
1614
1615                    drop(state);
1616                    focused_window.handle_input(input);
1617                    focused_window.set_hovered(false);
1618                }
1619            }
1620            wl_pointer::Event::Motion {
1621                surface_x,
1622                surface_y,
1623                ..
1624            } => {
1625                if state.mouse_focused_window.is_none() {
1626                    return;
1627                }
1628                state.mouse_location = Some(point(px(surface_x as f32), px(surface_y as f32)));
1629
1630                if let Some(window) = state.mouse_focused_window.clone() {
1631                    if window.is_blocked() {
1632                        let default_style = CursorStyle::Arrow;
1633                        if state.cursor_style != Some(default_style) {
1634                            let serial = state.serial_tracker.get(SerialKind::MouseEnter);
1635                            state.cursor_style = Some(default_style);
1636
1637                            if let Some(cursor_shape_device) = &state.cursor_shape_device {
1638                                cursor_shape_device.set_shape(serial, default_style.to_shape());
1639                            } else {
1640                                // cursor-shape-v1 isn't supported, set the cursor using a surface.
1641                                let wl_pointer = state
1642                                    .wl_pointer
1643                                    .clone()
1644                                    .expect("window is focused by pointer");
1645                                let scale = window.primary_output_scale();
1646                                state.cursor.set_icon(
1647                                    &wl_pointer,
1648                                    serial,
1649                                    default_style.to_icon_names(),
1650                                    scale,
1651                                );
1652                            }
1653                        }
1654                    }
1655                    if state
1656                        .keyboard_focused_window
1657                        .as_ref()
1658                        .is_some_and(|keyboard_window| window.ptr_eq(keyboard_window))
1659                    {
1660                        state.enter_token = None;
1661                    }
1662                    let input = PlatformInput::MouseMove(MouseMoveEvent {
1663                        position: state.mouse_location.unwrap(),
1664                        pressed_button: state.button_pressed,
1665                        modifiers: state.modifiers,
1666                    });
1667                    drop(state);
1668                    window.handle_input(input);
1669                }
1670            }
1671            wl_pointer::Event::Button {
1672                serial,
1673                button,
1674                state: WEnum::Value(button_state),
1675                ..
1676            } => {
1677                state.serial_tracker.update(SerialKind::MousePress, serial);
1678                let button = linux_button_to_gpui(button);
1679                let Some(button) = button else { return };
1680                if state.mouse_focused_window.is_none() {
1681                    return;
1682                }
1683                match button_state {
1684                    wl_pointer::ButtonState::Pressed => {
1685                        if let Some(window) = state.keyboard_focused_window.clone() {
1686                            if state.composing && state.text_input.is_some() {
1687                                drop(state);
1688                                // text_input_v3 don't have something like a reset function
1689                                this.disable_ime();
1690                                this.enable_ime();
1691                                window.handle_ime(ImeInput::UnmarkText);
1692                                state = client.borrow_mut();
1693                            } else if let (Some(text), Some(compose)) =
1694                                (state.pre_edit_text.take(), state.compose_state.as_mut())
1695                            {
1696                                compose.reset();
1697                                drop(state);
1698                                window.handle_ime(ImeInput::InsertText(text));
1699                                state = client.borrow_mut();
1700                            }
1701                        }
1702                        let click_elapsed = state.click.last_click.elapsed();
1703
1704                        if click_elapsed < DOUBLE_CLICK_INTERVAL
1705                            && state
1706                                .click
1707                                .last_mouse_button
1708                                .is_some_and(|prev_button| prev_button == button)
1709                            && is_within_click_distance(
1710                                state.click.last_location,
1711                                state.mouse_location.unwrap(),
1712                            )
1713                        {
1714                            state.click.current_count += 1;
1715                        } else {
1716                            state.click.current_count = 1;
1717                        }
1718
1719                        state.click.last_click = Instant::now();
1720                        state.click.last_mouse_button = Some(button);
1721                        state.click.last_location = state.mouse_location.unwrap();
1722
1723                        state.button_pressed = Some(button);
1724
1725                        if let Some(window) = state.mouse_focused_window.clone() {
1726                            let input = PlatformInput::MouseDown(MouseDownEvent {
1727                                button,
1728                                position: state.mouse_location.unwrap(),
1729                                modifiers: state.modifiers,
1730                                click_count: state.click.current_count,
1731                                first_mouse: state.enter_token.take().is_some(),
1732                            });
1733                            drop(state);
1734                            window.handle_input(input);
1735                        }
1736                    }
1737                    wl_pointer::ButtonState::Released => {
1738                        state.button_pressed = None;
1739
1740                        if let Some(window) = state.mouse_focused_window.clone() {
1741                            let input = PlatformInput::MouseUp(MouseUpEvent {
1742                                button,
1743                                position: state.mouse_location.unwrap(),
1744                                modifiers: state.modifiers,
1745                                click_count: state.click.current_count,
1746                            });
1747                            drop(state);
1748                            window.handle_input(input);
1749                        }
1750                    }
1751                    _ => {}
1752                }
1753            }
1754
1755            // Axis Events
1756            wl_pointer::Event::AxisSource {
1757                axis_source: WEnum::Value(axis_source),
1758            } => {
1759                state.axis_source = axis_source;
1760            }
1761            wl_pointer::Event::Axis {
1762                axis: WEnum::Value(axis),
1763                value,
1764                ..
1765            } => {
1766                if state.axis_source == AxisSource::Wheel {
1767                    return;
1768                }
1769                let axis = if state.modifiers.shift {
1770                    wl_pointer::Axis::HorizontalScroll
1771                } else {
1772                    axis
1773                };
1774                let axis_modifier = match axis {
1775                    wl_pointer::Axis::VerticalScroll => state.vertical_modifier,
1776                    wl_pointer::Axis::HorizontalScroll => state.horizontal_modifier,
1777                    _ => 1.0,
1778                };
1779                state.scroll_event_received = true;
1780                let scroll_delta = state
1781                    .continuous_scroll_delta
1782                    .get_or_insert(point(px(0.0), px(0.0)));
1783                let modifier = 3.0;
1784                match axis {
1785                    wl_pointer::Axis::VerticalScroll => {
1786                        scroll_delta.y += px(value as f32 * modifier * axis_modifier);
1787                    }
1788                    wl_pointer::Axis::HorizontalScroll => {
1789                        scroll_delta.x += px(value as f32 * modifier * axis_modifier);
1790                    }
1791                    _ => unreachable!(),
1792                }
1793            }
1794            wl_pointer::Event::AxisDiscrete {
1795                axis: WEnum::Value(axis),
1796                discrete,
1797            } => {
1798                state.scroll_event_received = true;
1799                let axis = if state.modifiers.shift {
1800                    wl_pointer::Axis::HorizontalScroll
1801                } else {
1802                    axis
1803                };
1804                let axis_modifier = match axis {
1805                    wl_pointer::Axis::VerticalScroll => state.vertical_modifier,
1806                    wl_pointer::Axis::HorizontalScroll => state.horizontal_modifier,
1807                    _ => 1.0,
1808                };
1809
1810                let scroll_delta = state.discrete_scroll_delta.get_or_insert(point(0.0, 0.0));
1811                match axis {
1812                    wl_pointer::Axis::VerticalScroll => {
1813                        scroll_delta.y += discrete as f32 * axis_modifier * SCROLL_LINES;
1814                    }
1815                    wl_pointer::Axis::HorizontalScroll => {
1816                        scroll_delta.x += discrete as f32 * axis_modifier * SCROLL_LINES;
1817                    }
1818                    _ => unreachable!(),
1819                }
1820            }
1821            wl_pointer::Event::AxisValue120 {
1822                axis: WEnum::Value(axis),
1823                value120,
1824            } => {
1825                state.scroll_event_received = true;
1826                let axis = if state.modifiers.shift {
1827                    wl_pointer::Axis::HorizontalScroll
1828                } else {
1829                    axis
1830                };
1831                let axis_modifier = match axis {
1832                    wl_pointer::Axis::VerticalScroll => state.vertical_modifier,
1833                    wl_pointer::Axis::HorizontalScroll => state.horizontal_modifier,
1834                    _ => unreachable!(),
1835                };
1836
1837                let scroll_delta = state.discrete_scroll_delta.get_or_insert(point(0.0, 0.0));
1838                let wheel_percent = value120 as f32 / 120.0;
1839                match axis {
1840                    wl_pointer::Axis::VerticalScroll => {
1841                        scroll_delta.y += wheel_percent * axis_modifier * SCROLL_LINES;
1842                    }
1843                    wl_pointer::Axis::HorizontalScroll => {
1844                        scroll_delta.x += wheel_percent * axis_modifier * SCROLL_LINES;
1845                    }
1846                    _ => unreachable!(),
1847                }
1848            }
1849            wl_pointer::Event::Frame => {
1850                if state.scroll_event_received {
1851                    state.scroll_event_received = false;
1852                    let continuous = state.continuous_scroll_delta.take();
1853                    let discrete = state.discrete_scroll_delta.take();
1854                    if let Some(continuous) = continuous {
1855                        if let Some(window) = state.mouse_focused_window.clone() {
1856                            let input = PlatformInput::ScrollWheel(ScrollWheelEvent {
1857                                position: state.mouse_location.unwrap(),
1858                                delta: ScrollDelta::Pixels(continuous),
1859                                modifiers: state.modifiers,
1860                                touch_phase: TouchPhase::Moved,
1861                            });
1862                            drop(state);
1863                            window.handle_input(input);
1864                        }
1865                    } else if let Some(discrete) = discrete
1866                        && let Some(window) = state.mouse_focused_window.clone()
1867                    {
1868                        let input = PlatformInput::ScrollWheel(ScrollWheelEvent {
1869                            position: state.mouse_location.unwrap(),
1870                            delta: ScrollDelta::Lines(discrete),
1871                            modifiers: state.modifiers,
1872                            touch_phase: TouchPhase::Moved,
1873                        });
1874                        drop(state);
1875                        window.handle_input(input);
1876                    }
1877                }
1878            }
1879            _ => {}
1880        }
1881    }
1882}
1883
1884impl Dispatch<wp_fractional_scale_v1::WpFractionalScaleV1, ObjectId> for WaylandClientStatePtr {
1885    fn event(
1886        this: &mut Self,
1887        _: &wp_fractional_scale_v1::WpFractionalScaleV1,
1888        event: <wp_fractional_scale_v1::WpFractionalScaleV1 as Proxy>::Event,
1889        surface_id: &ObjectId,
1890        _: &Connection,
1891        _: &QueueHandle<Self>,
1892    ) {
1893        let client = this.get_client();
1894        let mut state = client.borrow_mut();
1895
1896        let Some(window) = get_window(&mut state, surface_id) else {
1897            return;
1898        };
1899
1900        drop(state);
1901        window.handle_fractional_scale_event(event);
1902    }
1903}
1904
1905impl Dispatch<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1, ObjectId>
1906    for WaylandClientStatePtr
1907{
1908    fn event(
1909        this: &mut Self,
1910        _: &zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1,
1911        event: zxdg_toplevel_decoration_v1::Event,
1912        surface_id: &ObjectId,
1913        _: &Connection,
1914        _: &QueueHandle<Self>,
1915    ) {
1916        let client = this.get_client();
1917        let mut state = client.borrow_mut();
1918        let Some(window) = get_window(&mut state, surface_id) else {
1919            return;
1920        };
1921
1922        drop(state);
1923        window.handle_toplevel_decoration_event(event);
1924    }
1925}
1926
1927impl Dispatch<wl_data_device::WlDataDevice, ()> for WaylandClientStatePtr {
1928    fn event(
1929        this: &mut Self,
1930        _: &wl_data_device::WlDataDevice,
1931        event: wl_data_device::Event,
1932        _: &(),
1933        _: &Connection,
1934        _: &QueueHandle<Self>,
1935    ) {
1936        let client = this.get_client();
1937        let mut state = client.borrow_mut();
1938
1939        match event {
1940            // Clipboard
1941            wl_data_device::Event::DataOffer { id: data_offer } => {
1942                state.data_offers.push(DataOffer::new(data_offer));
1943                if state.data_offers.len() > 2 {
1944                    // At most we store a clipboard offer and a drag and drop offer.
1945                    state.data_offers.remove(0).inner.destroy();
1946                }
1947            }
1948            wl_data_device::Event::Selection { id: data_offer } => {
1949                if let Some(offer) = data_offer {
1950                    let offer = state
1951                        .data_offers
1952                        .iter()
1953                        .find(|wrapper| wrapper.inner.id() == offer.id());
1954                    let offer = offer.cloned();
1955                    state.clipboard.set_offer(offer);
1956                } else {
1957                    state.clipboard.set_offer(None);
1958                }
1959            }
1960
1961            // Drag and drop
1962            wl_data_device::Event::Enter {
1963                serial,
1964                surface,
1965                x,
1966                y,
1967                id: data_offer,
1968            } => {
1969                state.serial_tracker.update(SerialKind::DataDevice, serial);
1970                if let Some(data_offer) = data_offer {
1971                    let Some(drag_window) = get_window(&mut state, &surface.id()) else {
1972                        return;
1973                    };
1974
1975                    const ACTIONS: DndAction = DndAction::Copy;
1976                    data_offer.set_actions(ACTIONS, ACTIONS);
1977
1978                    let pipe = Pipe::new().unwrap();
1979                    data_offer.receive(FILE_LIST_MIME_TYPE.to_string(), unsafe {
1980                        BorrowedFd::borrow_raw(pipe.write.as_raw_fd())
1981                    });
1982                    let fd = pipe.read;
1983                    drop(pipe.write);
1984
1985                    let read_task = state.common.background_executor.spawn(async {
1986                        let buffer = unsafe { read_fd(fd)? };
1987                        let text = String::from_utf8(buffer)?;
1988                        anyhow::Ok(text)
1989                    });
1990
1991                    let this = this.clone();
1992                    state
1993                        .common
1994                        .foreground_executor
1995                        .spawn(async move {
1996                            let file_list = match read_task.await {
1997                                Ok(list) => list,
1998                                Err(err) => {
1999                                    log::error!("error reading drag and drop pipe: {err:?}");
2000                                    return;
2001                                }
2002                            };
2003
2004                            let paths: SmallVec<[_; 2]> = file_list
2005                                .lines()
2006                                .filter_map(|path| Url::parse(path).log_err())
2007                                .filter_map(|url| url.to_file_path().log_err())
2008                                .collect();
2009                            let position = Point::new(x.into(), y.into());
2010
2011                            // Prevent dropping text from other programs.
2012                            if paths.is_empty() {
2013                                data_offer.destroy();
2014                                return;
2015                            }
2016
2017                            let input = PlatformInput::FileDrop(FileDropEvent::Entered {
2018                                position,
2019                                paths: crate::ExternalPaths(paths),
2020                            });
2021
2022                            let client = this.get_client();
2023                            let mut state = client.borrow_mut();
2024                            state.drag.data_offer = Some(data_offer);
2025                            state.drag.window = Some(drag_window.clone());
2026                            state.drag.position = position;
2027
2028                            drop(state);
2029                            drag_window.handle_input(input);
2030                        })
2031                        .detach();
2032                }
2033            }
2034            wl_data_device::Event::Motion { x, y, .. } => {
2035                let Some(drag_window) = state.drag.window.clone() else {
2036                    return;
2037                };
2038                let position = Point::new(x.into(), y.into());
2039                state.drag.position = position;
2040
2041                let input = PlatformInput::FileDrop(FileDropEvent::Pending { position });
2042                drop(state);
2043                drag_window.handle_input(input);
2044            }
2045            wl_data_device::Event::Leave => {
2046                let Some(drag_window) = state.drag.window.clone() else {
2047                    return;
2048                };
2049                let data_offer = state.drag.data_offer.clone().unwrap();
2050                data_offer.destroy();
2051
2052                state.drag.data_offer = None;
2053                state.drag.window = None;
2054
2055                let input = PlatformInput::FileDrop(FileDropEvent::Exited {});
2056                drop(state);
2057                drag_window.handle_input(input);
2058            }
2059            wl_data_device::Event::Drop => {
2060                let Some(drag_window) = state.drag.window.clone() else {
2061                    return;
2062                };
2063                let data_offer = state.drag.data_offer.clone().unwrap();
2064                data_offer.finish();
2065                data_offer.destroy();
2066
2067                state.drag.data_offer = None;
2068                state.drag.window = None;
2069
2070                let input = PlatformInput::FileDrop(FileDropEvent::Submit {
2071                    position: state.drag.position,
2072                });
2073                drop(state);
2074                drag_window.handle_input(input);
2075            }
2076            _ => {}
2077        }
2078    }
2079
2080    event_created_child!(WaylandClientStatePtr, wl_data_device::WlDataDevice, [
2081        wl_data_device::EVT_DATA_OFFER_OPCODE => (wl_data_offer::WlDataOffer, ()),
2082    ]);
2083}
2084
2085impl Dispatch<wl_data_offer::WlDataOffer, ()> for WaylandClientStatePtr {
2086    fn event(
2087        this: &mut Self,
2088        data_offer: &wl_data_offer::WlDataOffer,
2089        event: wl_data_offer::Event,
2090        _: &(),
2091        _: &Connection,
2092        _: &QueueHandle<Self>,
2093    ) {
2094        let client = this.get_client();
2095        let mut state = client.borrow_mut();
2096
2097        if let wl_data_offer::Event::Offer { mime_type } = event {
2098            // Drag and drop
2099            if mime_type == FILE_LIST_MIME_TYPE {
2100                let serial = state.serial_tracker.get(SerialKind::DataDevice);
2101                let mime_type = mime_type.clone();
2102                data_offer.accept(serial, Some(mime_type));
2103            }
2104
2105            // Clipboard
2106            if let Some(offer) = state
2107                .data_offers
2108                .iter_mut()
2109                .find(|wrapper| wrapper.inner.id() == data_offer.id())
2110            {
2111                offer.add_mime_type(mime_type);
2112            }
2113        }
2114    }
2115}
2116
2117impl Dispatch<wl_data_source::WlDataSource, ()> for WaylandClientStatePtr {
2118    fn event(
2119        this: &mut Self,
2120        data_source: &wl_data_source::WlDataSource,
2121        event: wl_data_source::Event,
2122        _: &(),
2123        _: &Connection,
2124        _: &QueueHandle<Self>,
2125    ) {
2126        let client = this.get_client();
2127        let mut state = client.borrow_mut();
2128
2129        match event {
2130            wl_data_source::Event::Send { mime_type, fd } => {
2131                state.clipboard.send(mime_type, fd);
2132            }
2133            wl_data_source::Event::Cancelled => {
2134                data_source.destroy();
2135            }
2136            _ => {}
2137        }
2138    }
2139}
2140
2141impl Dispatch<zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1, ()>
2142    for WaylandClientStatePtr
2143{
2144    fn event(
2145        this: &mut Self,
2146        _: &zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1,
2147        event: zwp_primary_selection_device_v1::Event,
2148        _: &(),
2149        _: &Connection,
2150        _: &QueueHandle<Self>,
2151    ) {
2152        let client = this.get_client();
2153        let mut state = client.borrow_mut();
2154
2155        match event {
2156            zwp_primary_selection_device_v1::Event::DataOffer { offer } => {
2157                let old_offer = state.primary_data_offer.replace(DataOffer::new(offer));
2158                if let Some(old_offer) = old_offer {
2159                    old_offer.inner.destroy();
2160                }
2161            }
2162            zwp_primary_selection_device_v1::Event::Selection { id: data_offer } => {
2163                if data_offer.is_some() {
2164                    let offer = state.primary_data_offer.clone();
2165                    state.clipboard.set_primary_offer(offer);
2166                } else {
2167                    state.clipboard.set_primary_offer(None);
2168                }
2169            }
2170            _ => {}
2171        }
2172    }
2173
2174    event_created_child!(WaylandClientStatePtr, zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1, [
2175        zwp_primary_selection_device_v1::EVT_DATA_OFFER_OPCODE => (zwp_primary_selection_offer_v1::ZwpPrimarySelectionOfferV1, ()),
2176    ]);
2177}
2178
2179impl Dispatch<zwp_primary_selection_offer_v1::ZwpPrimarySelectionOfferV1, ()>
2180    for WaylandClientStatePtr
2181{
2182    fn event(
2183        this: &mut Self,
2184        _data_offer: &zwp_primary_selection_offer_v1::ZwpPrimarySelectionOfferV1,
2185        event: zwp_primary_selection_offer_v1::Event,
2186        _: &(),
2187        _: &Connection,
2188        _: &QueueHandle<Self>,
2189    ) {
2190        let client = this.get_client();
2191        let mut state = client.borrow_mut();
2192
2193        if let zwp_primary_selection_offer_v1::Event::Offer { mime_type } = event
2194            && let Some(offer) = state.primary_data_offer.as_mut()
2195        {
2196            offer.add_mime_type(mime_type);
2197        }
2198    }
2199}
2200
2201impl Dispatch<zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1, ()>
2202    for WaylandClientStatePtr
2203{
2204    fn event(
2205        this: &mut Self,
2206        selection_source: &zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1,
2207        event: zwp_primary_selection_source_v1::Event,
2208        _: &(),
2209        _: &Connection,
2210        _: &QueueHandle<Self>,
2211    ) {
2212        let client = this.get_client();
2213        let mut state = client.borrow_mut();
2214
2215        match event {
2216            zwp_primary_selection_source_v1::Event::Send { mime_type, fd } => {
2217                state.clipboard.send_primary(mime_type, fd);
2218            }
2219            zwp_primary_selection_source_v1::Event::Cancelled => {
2220                selection_source.destroy();
2221            }
2222            _ => {}
2223        }
2224    }
2225}
2226
2227impl Dispatch<XdgWmDialogV1, ()> for WaylandClientStatePtr {
2228    fn event(
2229        _: &mut Self,
2230        _: &XdgWmDialogV1,
2231        _: <XdgWmDialogV1 as Proxy>::Event,
2232        _: &(),
2233        _: &Connection,
2234        _: &QueueHandle<Self>,
2235    ) {
2236    }
2237}
2238
2239impl Dispatch<XdgDialogV1, ()> for WaylandClientStatePtr {
2240    fn event(
2241        _state: &mut Self,
2242        _proxy: &XdgDialogV1,
2243        _event: <XdgDialogV1 as Proxy>::Event,
2244        _data: &(),
2245        _conn: &Connection,
2246        _qhandle: &QueueHandle<Self>,
2247    ) {
2248    }
2249}