Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T04:00:54.622Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

client.rs

2840 lines · 107.4 KB · rust
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 gpui_util::ResultExt as _;
19use http_client::Url;
20use smallvec::SmallVec;
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::pointer_gestures::zv1::client::{
40    zwp_pointer_gesture_pinch_v1, zwp_pointer_gestures_v1,
41};
42use wayland_protocols::wp::primary_selection::zv1::client::zwp_primary_selection_offer_v1::{
43    self, ZwpPrimarySelectionOfferV1,
44};
45use wayland_protocols::wp::primary_selection::zv1::client::{
46    zwp_primary_selection_device_manager_v1, zwp_primary_selection_device_v1,
47    zwp_primary_selection_source_v1,
48};
49use wayland_protocols::wp::text_input::zv3::client::zwp_text_input_v3::{
50    ContentHint, ContentPurpose,
51};
52use wayland_protocols::wp::text_input::zv3::client::{
53    zwp_text_input_manager_v3, zwp_text_input_v3,
54};
55use wayland_protocols::wp::viewporter::client::{wp_viewport, wp_viewporter};
56use wayland_protocols::xdg::activation::v1::client::{xdg_activation_token_v1, xdg_activation_v1};
57use wayland_protocols::xdg::decoration::zv1::client::{
58    zxdg_decoration_manager_v1, zxdg_toplevel_decoration_v1,
59};
60use wayland_protocols::xdg::shell::client::{
61    xdg_popup, xdg_positioner, xdg_surface, xdg_toplevel, xdg_wm_base,
62};
63use wayland_protocols::xdg::system_bell::v1::client::xdg_system_bell_v1;
64use wayland_protocols::{
65    wp::cursor_shape::v1::client::{wp_cursor_shape_device_v1, wp_cursor_shape_manager_v1},
66    xdg::dialog::v1::client::xdg_wm_dialog_v1::{self, XdgWmDialogV1},
67};
68use wayland_protocols::{
69    wp::fractional_scale::v1::client::{wp_fractional_scale_manager_v1, wp_fractional_scale_v1},
70    xdg::dialog::v1::client::xdg_dialog_v1::XdgDialogV1,
71};
72use wayland_protocols_plasma::blur::client::{org_kde_kwin_blur, org_kde_kwin_blur_manager};
73use wayland_protocols_wlr::layer_shell::v1::client::{zwlr_layer_shell_v1, zwlr_layer_surface_v1};
74use xkbcommon::xkb::ffi::XKB_KEYMAP_FORMAT_TEXT_V1;
75use xkbcommon::xkb::{self, KEYMAP_COMPILE_NO_FLAGS, Keycode};
76
77use super::{
78    display::WaylandDisplay,
79    window::{ImeInput, WaylandWindowStatePtr},
80};
81
82use crate::linux::{
83    DOUBLE_CLICK_INTERVAL, LinuxClient, LinuxCommon, LinuxKeyboardLayout, PIPE_READ_TIMEOUT,
84    SCROLL_LINES, capslock_from_xkb, cursor_style_to_icon_names, get_xkb_compose_state,
85    is_within_click_distance, keystroke_from_xkb, keystroke_underlying_dead_key,
86    modifiers_from_xkb, open_uri_internal, read_fd_with_timeout, reveal_path_internal,
87    wayland::{
88        clipboard::{Clipboard, DataOffer, FILE_LIST_MIME_TYPE, TEXT_MIME_TYPES},
89        cursor::Cursor,
90        serial::{Serial, SerialKind, SerialTracker},
91        to_shape,
92        window::WaylandWindow,
93    },
94    xdg_desktop_portal::{Event as XDPEvent, XDPEventSource},
95};
96use gpui::{
97    AnyWindowHandle, Bounds, Capslock, CursorStyle, DevicePixels, DisplayId, FileDropEvent,
98    ForegroundExecutor, KeyDownEvent, KeyUpEvent, Keystroke, Modifiers, ModifiersChangedEvent,
99    MouseButton, MouseDownEvent, MouseExitEvent, MouseMoveEvent, MouseUpEvent, NavigationDirection,
100    Pixels, PlatformDisplay, PlatformInput, PlatformKeyboardLayout, PlatformWindow, Point,
101    ScrollDelta, ScrollWheelEvent, SharedString, Size, TouchPhase, WindowButtonLayout, WindowKind,
102    WindowParams, point, profiler, px, size,
103};
104use gpui_wgpu::{CompositorGpuHint, GpuContext};
105use wayland_protocols::wp::linux_dmabuf::zv1::client::{
106    zwp_linux_dmabuf_feedback_v1, zwp_linux_dmabuf_v1,
107};
108
109/// Used to convert evdev scancode to xkb scancode
110const MIN_KEYCODE: u32 = 8;
111
112const UNKNOWN_KEYBOARD_LAYOUT_NAME: SharedString = SharedString::new_static("unknown");
113const XDG_ACTIVATION_TOKEN_ENV_VAR: &str = "XDG_ACTIVATION_TOKEN";
114
115#[derive(Clone, Copy, Debug, PartialEq, Eq)]
116struct ImeCursorRectangle {
117    x: i32,
118    y: i32,
119    width: i32,
120    height: i32,
121}
122
123impl From<Bounds<Pixels>> for ImeCursorRectangle {
124    fn from(bounds: Bounds<Pixels>) -> Self {
125        Self {
126            x: bounds.origin.x.as_f32() as i32,
127            y: bounds.origin.y.as_f32() as i32,
128            width: bounds.size.width.as_f32() as i32,
129            height: bounds.size.height.as_f32() as i32,
130        }
131    }
132}
133
134trait ImeCursorRectangleSink {
135    fn set_ime_cursor_rectangle(&self, x: i32, y: i32, width: i32, height: i32);
136    fn commit_ime_state(&self);
137}
138
139impl ImeCursorRectangleSink for zwp_text_input_v3::ZwpTextInputV3 {
140    fn set_ime_cursor_rectangle(&self, x: i32, y: i32, width: i32, height: i32) {
141        self.set_cursor_rectangle(x, y, width, height);
142    }
143
144    fn commit_ime_state(&self) {
145        self.commit();
146    }
147}
148
149fn set_ime_cursor_rectangle(
150    text_input: &impl ImeCursorRectangleSink,
151    cursor_rectangle: ImeCursorRectangle,
152) {
153    text_input.set_ime_cursor_rectangle(
154        cursor_rectangle.x,
155        cursor_rectangle.y,
156        cursor_rectangle.width,
157        cursor_rectangle.height,
158    );
159}
160
161fn update_ime_cursor_rectangle(
162    text_input: &impl ImeCursorRectangleSink,
163    last_ime_cursor_rectangle: &mut Option<ImeCursorRectangle>,
164    bounds: Bounds<Pixels>,
165) {
166    let cursor_rectangle = ImeCursorRectangle::from(bounds);
167    if *last_ime_cursor_rectangle == Some(cursor_rectangle) {
168        return;
169    }
170
171    *last_ime_cursor_rectangle = Some(cursor_rectangle);
172    set_ime_cursor_rectangle(text_input, cursor_rectangle);
173    text_input.commit_ime_state();
174}
175
176fn set_ime_cursor_rectangle_after_done(
177    text_input: &impl ImeCursorRectangleSink,
178    last_ime_cursor_rectangle: &mut Option<ImeCursorRectangle>,
179    bounds: Bounds<Pixels>,
180    should_commit: bool,
181) {
182    if should_commit {
183        update_ime_cursor_rectangle(text_input, last_ime_cursor_rectangle, bounds);
184    } else {
185        set_ime_cursor_rectangle(text_input, ImeCursorRectangle::from(bounds));
186    }
187}
188
189fn take_startup_activation_token_from_environment() -> Option<String> {
190    let startup_activation_token = std::env::var(XDG_ACTIVATION_TOKEN_ENV_VAR)
191        .ok()
192        .filter(|token| !token.is_empty());
193    // The token must be removed from the environment so it isn't inherited by child
194    // processes we spawn, per the xdg-activation spec: https://wayland.app/protocols/xdg-activation-v1
195    // SAFETY: This runs during Wayland platform initialization before GPUI starts
196    // concurrent environment access or spawning child processes.
197    unsafe { std::env::remove_var(XDG_ACTIVATION_TOKEN_ENV_VAR) };
198    startup_activation_token
199}
200
201#[derive(Clone)]
202pub struct Globals {
203    pub qh: QueueHandle<WaylandClientStatePtr>,
204    pub activation: Option<xdg_activation_v1::XdgActivationV1>,
205    pub compositor: wl_compositor::WlCompositor,
206    pub cursor_shape_manager: Option<wp_cursor_shape_manager_v1::WpCursorShapeManagerV1>,
207    pub data_device_manager: Option<wl_data_device_manager::WlDataDeviceManager>,
208    pub primary_selection_manager:
209        Option<zwp_primary_selection_device_manager_v1::ZwpPrimarySelectionDeviceManagerV1>,
210    pub wm_base: xdg_wm_base::XdgWmBase,
211    pub shm: wl_shm::WlShm,
212    pub seat: wl_seat::WlSeat,
213    pub viewporter: Option<wp_viewporter::WpViewporter>,
214    pub fractional_scale_manager:
215        Option<wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1>,
216    pub decoration_manager: Option<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1>,
217    pub layer_shell: Option<zwlr_layer_shell_v1::ZwlrLayerShellV1>,
218    pub blur_manager: Option<org_kde_kwin_blur_manager::OrgKdeKwinBlurManager>,
219    pub text_input_manager: Option<zwp_text_input_manager_v3::ZwpTextInputManagerV3>,
220    pub gesture_manager: Option<zwp_pointer_gestures_v1::ZwpPointerGesturesV1>,
221    pub dialog: Option<xdg_wm_dialog_v1::XdgWmDialogV1>,
222    pub system_bell: Option<xdg_system_bell_v1::XdgSystemBellV1>,
223    pub executor: ForegroundExecutor,
224}
225
226impl Globals {
227    fn new(
228        globals: GlobalList,
229        executor: ForegroundExecutor,
230        qh: QueueHandle<WaylandClientStatePtr>,
231        seat: wl_seat::WlSeat,
232    ) -> Self {
233        let dialog_v = XdgWmDialogV1::interface().version;
234        Globals {
235            activation: globals.bind(&qh, 1..=1, ()).ok(),
236            compositor: globals
237                .bind(
238                    &qh,
239                    wl_surface::REQ_SET_BUFFER_SCALE_SINCE
240                        ..=wl_surface::EVT_PREFERRED_BUFFER_SCALE_SINCE,
241                    (),
242                )
243                .unwrap(),
244            cursor_shape_manager: globals.bind(&qh, 1..=1, ()).ok(),
245            data_device_manager: globals
246                .bind(
247                    &qh,
248                    WL_DATA_DEVICE_MANAGER_VERSION..=WL_DATA_DEVICE_MANAGER_VERSION,
249                    (),
250                )
251                .ok(),
252            primary_selection_manager: globals.bind(&qh, 1..=1, ()).ok(),
253            shm: globals.bind(&qh, 1..=1, ()).unwrap(),
254            seat,
255            wm_base: globals.bind(&qh, 1..=5, ()).unwrap(),
256            viewporter: globals.bind(&qh, 1..=1, ()).ok(),
257            fractional_scale_manager: globals.bind(&qh, 1..=1, ()).ok(),
258            decoration_manager: globals.bind(&qh, 1..=1, ()).ok(),
259            layer_shell: globals.bind(&qh, 1..=5, ()).ok(),
260            blur_manager: globals.bind(&qh, 1..=1, ()).ok(),
261            text_input_manager: globals.bind(&qh, 1..=1, ()).ok(),
262            gesture_manager: globals.bind(&qh, 1..=3, ()).ok(),
263            dialog: globals.bind(&qh, dialog_v..=dialog_v, ()).ok(),
264            system_bell: globals.bind(&qh, 1..=1, ()).ok(),
265            executor,
266            qh,
267        }
268    }
269}
270
271#[derive(Default, Debug, Clone, PartialEq, Eq, Hash)]
272pub struct InProgressOutput {
273    name: Option<String>,
274    scale: Option<i32>,
275    position: Option<Point<DevicePixels>>,
276    size: Option<Size<DevicePixels>>,
277    subpixel: Option<wl_output::Subpixel>,
278}
279
280impl InProgressOutput {
281    fn complete(&self) -> Option<Output> {
282        if let Some((position, size)) = self.position.zip(self.size) {
283            let scale = self.scale.unwrap_or(1);
284            Some(Output {
285                name: self.name.clone(),
286                scale,
287                bounds: Bounds::new(position, size),
288                subpixel: self.subpixel,
289            })
290        } else {
291            None
292        }
293    }
294}
295
296#[derive(Debug, Clone, Eq, PartialEq, Hash)]
297pub struct Output {
298    pub name: Option<String>,
299    pub scale: i32,
300    pub bounds: Bounds<DevicePixels>,
301    pub subpixel: Option<wl_output::Subpixel>,
302}
303
304pub(crate) struct WaylandClientState {
305    serial_tracker: SerialTracker,
306    globals: Globals,
307    pub gpu_context: GpuContext,
308    pub compositor_gpu: Option<CompositorGpuHint>,
309    wl_seat: wl_seat::WlSeat, // TODO: Multi seat support
310    wl_pointer: Option<wl_pointer::WlPointer>,
311    pinch_gesture: Option<zwp_pointer_gesture_pinch_v1::ZwpPointerGesturePinchV1>,
312    pinch_scale: f32,
313    wl_keyboard: Option<wl_keyboard::WlKeyboard>,
314    cursor_shape_device: Option<wp_cursor_shape_device_v1::WpCursorShapeDeviceV1>,
315    data_device: Option<wl_data_device::WlDataDevice>,
316    primary_selection: Option<zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1>,
317    text_input: Option<zwp_text_input_v3::ZwpTextInputV3>,
318    pre_edit_text: Option<String>,
319    ime_pre_edit: Option<String>,
320    composing: bool,
321    last_ime_cursor_rectangle: Option<ImeCursorRectangle>,
322    // Surface to Window mapping
323    windows: HashMap<ObjectId, WaylandWindowStatePtr>,
324    // Output to scale mapping
325    outputs: HashMap<ObjectId, Output>,
326    in_progress_outputs: HashMap<ObjectId, InProgressOutput>,
327    wl_outputs: HashMap<ObjectId, wl_output::WlOutput>,
328    keyboard_layout: LinuxKeyboardLayout,
329    keymap_state: Option<xkb::State>,
330    compose_state: Option<xkb::compose::State>,
331    drag: DragState,
332    click: ClickState,
333    repeat: KeyRepeat,
334    pub modifiers: Modifiers,
335    pub capslock: Capslock,
336    axis_source: AxisSource,
337    pub mouse_location: Option<Point<Pixels>>,
338    continuous_scroll_delta: Option<Point<Pixels>>,
339    discrete_scroll_delta: Option<Point<f32>>,
340    vertical_modifier: f32,
341    horizontal_modifier: f32,
342    scroll_event_received: bool,
343    enter_token: Option<()>,
344    button_pressed: Option<MouseButton>,
345    mouse_focused_window: Option<WaylandWindowStatePtr>,
346    keyboard_focused_window: Option<WaylandWindowStatePtr>,
347    loop_handle: LoopHandle<'static, WaylandClientStatePtr>,
348    cursor_style: Option<CursorStyle>,
349    cursor_hidden_window: Option<WaylandWindowStatePtr>,
350    clipboard: Clipboard,
351    data_offers: Vec<DataOffer<WlDataOffer>>,
352    primary_data_offer: Option<DataOffer<ZwpPrimarySelectionOfferV1>>,
353    cursor: Cursor,
354    pending_activation: Option<PendingActivation>,
355    startup_activation_token: Option<String>,
356    event_loop: Option<EventLoop<'static, WaylandClientStatePtr>>,
357    pub common: LinuxCommon,
358    ime_enabled: Option<bool>,
359}
360
361pub struct DragState {
362    data_offer: Option<wl_data_offer::WlDataOffer>,
363    window: Option<WaylandWindowStatePtr>,
364    position: Point<Pixels>,
365}
366
367pub struct ClickState {
368    last_mouse_button: Option<MouseButton>,
369    last_click: Instant,
370    last_location: Point<Pixels>,
371    current_count: usize,
372}
373
374pub(crate) struct KeyRepeat {
375    characters_per_second: u32,
376    delay: Duration,
377    current_id: u64,
378    current_keycode: Option<xkb::Keycode>,
379}
380
381pub(crate) enum PendingActivation {
382    /// URI to open in the web browser.
383    Uri(String),
384    /// Path to open in the file explorer.
385    Path(PathBuf),
386    /// A window from ourselves to raise.
387    Window(ObjectId),
388}
389
390impl WaylandClientState {
391    fn consume_startup_activation_token(&mut self, surface: &wl_surface::WlSurface) {
392        let Some(startup_activation_token) = self.startup_activation_token.take() else {
393            return;
394        };
395        let Some(activation) = self.globals.activation.as_ref() else {
396            return;
397        };
398        activation.activate(startup_activation_token, surface);
399    }
400}
401
402/// This struct is required to conform to Rust's orphan rules, so we can dispatch on the state but hand the
403/// window to GPUI.
404#[derive(Clone)]
405pub struct WaylandClientStatePtr(Weak<RefCell<WaylandClientState>>);
406
407impl WaylandClientStatePtr {
408    pub fn get_client(&self) -> Rc<RefCell<WaylandClientState>> {
409        self.0
410            .upgrade()
411            .expect("The pointer should always be valid when dispatching in wayland")
412    }
413
414    pub fn get_serial(&self, kind: SerialKind) -> Serial {
415        self.0.upgrade().unwrap().borrow().serial_tracker.get(kind)
416    }
417
418    pub fn set_pending_activation(&self, window: ObjectId) {
419        self.0.upgrade().unwrap().borrow_mut().pending_activation =
420            Some(PendingActivation::Window(window));
421    }
422
423    pub fn enable_ime(&self) {
424        let client = self.get_client();
425        let mut state = client.borrow_mut();
426        state.ime_enabled = Some(true);
427        state.last_ime_cursor_rectangle = None;
428        let Some(text_input) = state.text_input.take() else {
429            return;
430        };
431
432        text_input.enable();
433        text_input.set_content_type(ContentHint::None, ContentPurpose::Normal);
434        let mut cursor_rectangle = None;
435        if let Some(window) = state.keyboard_focused_window.clone() {
436            drop(state);
437            if let Some(area) = window.get_ime_area() {
438                let area = ImeCursorRectangle::from(area);
439                set_ime_cursor_rectangle(&text_input, area);
440                cursor_rectangle = Some(area);
441            }
442            state = client.borrow_mut();
443        }
444        text_input.commit();
445        state.last_ime_cursor_rectangle = cursor_rectangle;
446        state.text_input = Some(text_input);
447    }
448
449    pub fn disable_ime(&self) {
450        let client = self.get_client();
451        let mut state = client.borrow_mut();
452        state.ime_enabled = Some(false);
453        state.composing = false;
454        if let Some(text_input) = &state.text_input {
455            text_input.disable();
456            text_input.commit();
457        }
458    }
459
460    pub fn ime_enabled(&self) -> Option<bool> {
461        let client = self.get_client();
462        client.borrow().ime_enabled
463    }
464
465    pub fn update_ime_position(&self, bounds: Bounds<Pixels>) {
466        let client = self.get_client();
467        let mut state = client.borrow_mut();
468        if state.pre_edit_text.is_some() {
469            return;
470        }
471        let Some(text_input) = state.text_input.clone() else {
472            return;
473        };
474        update_ime_cursor_rectangle(&text_input, &mut state.last_ime_cursor_rectangle, bounds);
475    }
476
477    pub fn handle_keyboard_layout_change(&self) {
478        let client = self.get_client();
479        let mut state = client.borrow_mut();
480        let changed = if let Some(keymap_state) = &state.keymap_state {
481            let layout_idx = keymap_state.serialize_layout(xkbcommon::xkb::STATE_LAYOUT_EFFECTIVE);
482            let keymap = keymap_state.get_keymap();
483            let layout_name = keymap.layout_get_name(layout_idx);
484            let changed = layout_name != state.keyboard_layout.name();
485            if changed {
486                state.keyboard_layout = LinuxKeyboardLayout::new(layout_name.to_string().into());
487            }
488            changed
489        } else {
490            let changed = &UNKNOWN_KEYBOARD_LAYOUT_NAME != state.keyboard_layout.name();
491            if changed {
492                state.keyboard_layout = LinuxKeyboardLayout::new(UNKNOWN_KEYBOARD_LAYOUT_NAME);
493            }
494            changed
495        };
496
497        if changed && let Some(mut callback) = state.common.callbacks.keyboard_layout_change.take()
498        {
499            drop(state);
500            callback();
501            state = client.borrow_mut();
502            state.common.callbacks.keyboard_layout_change = Some(callback);
503        }
504    }
505
506    pub fn drop_window(&self, surface_id: &ObjectId) {
507        let client = self.get_client();
508        let mut state = client.borrow_mut();
509        let closed_window = state.windows.remove(surface_id).unwrap();
510        if let Some(window) = state.mouse_focused_window.take()
511            && !window.ptr_eq(&closed_window)
512        {
513            state.mouse_focused_window = Some(window);
514        }
515        if let Some(window) = state.keyboard_focused_window.take()
516            && !window.ptr_eq(&closed_window)
517        {
518            state.keyboard_focused_window = Some(window);
519        }
520        if let Some(window) = state.cursor_hidden_window.take()
521            && !window.ptr_eq(&closed_window)
522        {
523            state.cursor_hidden_window = Some(window);
524        }
525    }
526}
527
528impl WaylandClientState {
529    fn hide_cursor_until_mouse_moves(&mut self) {
530        if self.cursor_hidden_window.is_some() {
531            return;
532        }
533        let Some(focused_window) = self.mouse_focused_window.clone() else {
534            // No surface to apply the hidden cursor to.
535            return;
536        };
537        let Some(wl_pointer) = self.wl_pointer.clone() else {
538            // Seat lost its pointer capability; nothing to hide.
539            return;
540        };
541        let serial = self.serial_tracker.get(SerialKind::MouseEnter);
542        wl_pointer.set_cursor(serial.as_raw(), None, 0, 0);
543        self.cursor_hidden_window = Some(focused_window);
544    }
545
546    fn restore_cursor_after_hide(&mut self) {
547        if self.cursor_hidden_window.take().is_none() {
548            return;
549        }
550        let Some(style) = self.cursor_style else {
551            return;
552        };
553        let serial = self.serial_tracker.get(SerialKind::MouseEnter);
554        if let Some(cursor_shape_device) = &self.cursor_shape_device {
555            cursor_shape_device.set_shape(serial.as_raw(), to_shape(style));
556            return;
557        }
558        let Some(focused_window) = self.mouse_focused_window.clone() else {
559            log::warn!(
560                "wayland: no focused surface to restore cursor style {:?} after hide; cursor may stay invisible",
561                style
562            );
563            return;
564        };
565        let Some(wl_pointer) = self.wl_pointer.clone() else {
566            log::warn!(
567                "wayland: no wl_pointer to restore cursor style {:?} after hide; cursor may stay invisible",
568                style
569            );
570            return;
571        };
572        let scale = focused_window.primary_output_scale();
573        self.cursor.set_icon(
574            &wl_pointer,
575            serial.as_raw(),
576            cursor_style_to_icon_names(style),
577            scale,
578        );
579    }
580}
581
582#[derive(Clone)]
583pub struct WaylandClient(Rc<RefCell<WaylandClientState>>);
584
585impl Drop for WaylandClient {
586    fn drop(&mut self) {
587        let mut state = self.0.borrow_mut();
588        state.windows.clear();
589
590        if let Some(wl_pointer) = &state.wl_pointer {
591            wl_pointer.release();
592        }
593        if let Some(cursor_shape_device) = &state.cursor_shape_device {
594            cursor_shape_device.destroy();
595        }
596        if let Some(data_device) = &state.data_device {
597            data_device.release();
598        }
599        if let Some(text_input) = &state.text_input {
600            text_input.destroy();
601        }
602    }
603}
604
605const WL_DATA_DEVICE_MANAGER_VERSION: u32 = 3;
606
607fn wl_seat_version(version: u32) -> u32 {
608    // We rely on the wl_pointer.frame event
609    const WL_SEAT_MIN_VERSION: u32 = 5;
610    const WL_SEAT_MAX_VERSION: u32 = 9;
611
612    if version < WL_SEAT_MIN_VERSION {
613        panic!(
614            "wl_seat below required version: {} < {}",
615            version, WL_SEAT_MIN_VERSION
616        );
617    }
618
619    version.clamp(WL_SEAT_MIN_VERSION, WL_SEAT_MAX_VERSION)
620}
621
622fn wl_output_version(version: u32) -> u32 {
623    const WL_OUTPUT_MIN_VERSION: u32 = 2;
624    const WL_OUTPUT_MAX_VERSION: u32 = 4;
625
626    if version < WL_OUTPUT_MIN_VERSION {
627        panic!(
628            "wl_output below required version: {} < {}",
629            version, WL_OUTPUT_MIN_VERSION
630        );
631    }
632
633    version.clamp(WL_OUTPUT_MIN_VERSION, WL_OUTPUT_MAX_VERSION)
634}
635
636impl WaylandClient {
637    pub(crate) fn new() -> Self {
638        let startup_activation_token = take_startup_activation_token_from_environment();
639        let conn = Connection::connect_to_env().unwrap();
640
641        let (globals, event_queue) = registry_queue_init::<WaylandClientStatePtr>(&conn).unwrap();
642        let qh = event_queue.handle();
643
644        let mut seat: Option<wl_seat::WlSeat> = None;
645        #[allow(clippy::mutable_key_type)]
646        let mut in_progress_outputs = HashMap::default();
647        #[allow(clippy::mutable_key_type)]
648        let mut wl_outputs: HashMap<ObjectId, wl_output::WlOutput> = HashMap::default();
649        globals.contents().with_list(|list| {
650            for global in list {
651                match &global.interface[..] {
652                    "wl_seat" => {
653                        seat = Some(globals.registry().bind::<wl_seat::WlSeat, _, _>(
654                            global.name,
655                            wl_seat_version(global.version),
656                            &qh,
657                            (),
658                        ));
659                    }
660                    "wl_output" => {
661                        let output = globals.registry().bind::<wl_output::WlOutput, _, _>(
662                            global.name,
663                            wl_output_version(global.version),
664                            &qh,
665                            (),
666                        );
667                        in_progress_outputs.insert(output.id(), InProgressOutput::default());
668                        wl_outputs.insert(output.id(), output);
669                    }
670                    _ => {}
671                }
672            }
673        });
674
675        let event_loop = EventLoop::<WaylandClientStatePtr>::try_new().unwrap();
676
677        let (common, main_receiver, wake_receiver) = LinuxCommon::new(event_loop.get_signal());
678
679        let handle = event_loop.handle();
680        handle
681            .insert_source(main_receiver, {
682                let handle = handle.clone();
683                move |event, _, _: &mut WaylandClientStatePtr| {
684                    if let calloop::channel::Event::Msg(runnable) = event {
685                        handle.insert_idle(|_| {
686                            let location = runnable.metadata().location;
687                            let spawned = runnable.metadata().spawned;
688                            profiler::update_running_task(spawned, location);
689                            runnable.run();
690                            profiler::save_task_timing();
691                        });
692                    }
693                }
694            })
695            .unwrap();
696
697        handle
698            .insert_source(
699                wake_receiver,
700                |event, _, client: &mut WaylandClientStatePtr| {
701                    if let calloop::channel::Event::Msg(()) = event {
702                        client.get_client().borrow_mut().common.handle_system_wake();
703                    }
704                },
705            )
706            .unwrap();
707
708        let compositor_gpu = detect_compositor_gpu();
709        let gpu_context = Rc::new(RefCell::new(None));
710
711        let seat = seat.unwrap();
712        let globals = Globals::new(
713            globals,
714            common.foreground_executor.clone(),
715            qh.clone(),
716            seat.clone(),
717        );
718
719        let data_device = globals
720            .data_device_manager
721            .as_ref()
722            .map(|data_device_manager| data_device_manager.get_data_device(&seat, &qh, ()));
723
724        let primary_selection = globals
725            .primary_selection_manager
726            .as_ref()
727            .map(|primary_selection_manager| primary_selection_manager.get_device(&seat, &qh, ()));
728
729        let cursor = Cursor::new(&conn, &globals, 24);
730
731        handle
732            .insert_source(XDPEventSource::new(&common.background_executor), {
733                move |event, _, client| match event {
734                    XDPEvent::WindowAppearance(appearance) => {
735                        if let Some(client) = client.0.upgrade() {
736                            let mut client = client.borrow_mut();
737
738                            client.common.appearance = appearance;
739
740                            for window in client.windows.values_mut() {
741                                window.set_appearance(appearance);
742                            }
743                        }
744                    }
745                    XDPEvent::ButtonLayout(layout_str) => {
746                        if let Some(client) = client.0.upgrade() {
747                            let layout = WindowButtonLayout::parse(&layout_str)
748                                .log_err()
749                                .unwrap_or_else(WindowButtonLayout::linux_default);
750                            let mut client = client.borrow_mut();
751                            client.common.button_layout = layout;
752
753                            for window in client.windows.values_mut() {
754                                window.set_button_layout();
755                            }
756                        }
757                    }
758                    XDPEvent::CursorTheme(theme) => {
759                        if let Some(client) = client.0.upgrade() {
760                            let mut client = client.borrow_mut();
761                            client.cursor.set_theme(theme);
762                        }
763                    }
764                    XDPEvent::CursorSize(size) => {
765                        if let Some(client) = client.0.upgrade() {
766                            let mut client = client.borrow_mut();
767                            client.cursor.set_size(size);
768                        }
769                    }
770                }
771            })
772            .unwrap();
773
774        let state = Rc::new(RefCell::new(WaylandClientState {
775            serial_tracker: SerialTracker::new(),
776            globals,
777            gpu_context,
778            compositor_gpu,
779            wl_seat: seat,
780            wl_pointer: None,
781            wl_keyboard: None,
782            pinch_gesture: None,
783            pinch_scale: 1.0,
784            cursor_shape_device: None,
785            data_device,
786            primary_selection,
787            text_input: None,
788            pre_edit_text: None,
789            ime_pre_edit: None,
790            composing: false,
791            last_ime_cursor_rectangle: None,
792            outputs: HashMap::default(),
793            in_progress_outputs,
794            wl_outputs,
795            windows: HashMap::default(),
796            common,
797            keyboard_layout: LinuxKeyboardLayout::new(UNKNOWN_KEYBOARD_LAYOUT_NAME),
798            keymap_state: None,
799            compose_state: None,
800            drag: DragState {
801                data_offer: None,
802                window: None,
803                position: Point::default(),
804            },
805            click: ClickState {
806                last_click: Instant::now(),
807                last_mouse_button: None,
808                last_location: Point::default(),
809                current_count: 0,
810            },
811            repeat: KeyRepeat {
812                characters_per_second: 16,
813                delay: Duration::from_millis(500),
814                current_id: 0,
815                current_keycode: None,
816            },
817            modifiers: Modifiers {
818                shift: false,
819                control: false,
820                alt: false,
821                function: false,
822                platform: false,
823            },
824            capslock: Capslock { on: false },
825            scroll_event_received: false,
826            axis_source: AxisSource::Wheel,
827            mouse_location: None,
828            continuous_scroll_delta: None,
829            discrete_scroll_delta: None,
830            vertical_modifier: -1.0,
831            horizontal_modifier: -1.0,
832            button_pressed: None,
833            mouse_focused_window: None,
834            keyboard_focused_window: None,
835            loop_handle: handle.clone(),
836            enter_token: None,
837            cursor_style: None,
838            cursor_hidden_window: None,
839            clipboard: Clipboard::new(conn.clone(), handle.clone()),
840            data_offers: Vec::new(),
841            primary_data_offer: None,
842            cursor,
843            pending_activation: None,
844            startup_activation_token,
845            event_loop: Some(event_loop),
846            ime_enabled: None,
847        }));
848
849        WaylandSource::new(conn, event_queue)
850            .insert(handle)
851            .unwrap();
852
853        Self(state)
854    }
855}
856
857impl LinuxClient for WaylandClient {
858    fn keyboard_layout(&self) -> Box<dyn PlatformKeyboardLayout> {
859        Box::new(self.0.borrow().keyboard_layout.clone())
860    }
861
862    fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
863        self.0
864            .borrow()
865            .outputs
866            .iter()
867            .map(|(id, output)| {
868                Rc::new(WaylandDisplay {
869                    id: id.clone(),
870                    name: output.name.clone(),
871                    bounds: output.bounds.to_pixels(output.scale as f32),
872                }) as Rc<dyn PlatformDisplay>
873            })
874            .collect()
875    }
876
877    fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
878        self.0
879            .borrow()
880            .outputs
881            .iter()
882            .find_map(|(object_id, output)| {
883                (object_id.protocol_id() as u64 == u64::from(id)).then(|| {
884                    Rc::new(WaylandDisplay {
885                        id: object_id.clone(),
886                        name: output.name.clone(),
887                        bounds: output.bounds.to_pixels(output.scale as f32),
888                    }) as Rc<dyn PlatformDisplay>
889                })
890            })
891    }
892
893    fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>> {
894        None
895    }
896
897    #[cfg(feature = "screen-capture")]
898    fn screen_capture_sources(
899        &self,
900    ) -> futures::channel::oneshot::Receiver<anyhow::Result<Vec<Rc<dyn gpui::ScreenCaptureSource>>>>
901    {
902        // TODO: Get screen capture working on wayland. Be sure to try window resizing as that may
903        // be tricky.
904        //
905        // start_scap_default_target_source()
906        let (sources_tx, sources_rx) = futures::channel::oneshot::channel();
907        sources_tx
908            .send(Err(anyhow::anyhow!(
909                "Wayland screen capture not yet implemented."
910            )))
911            .ok();
912        sources_rx
913    }
914
915    fn open_window(
916        &self,
917        handle: AnyWindowHandle,
918        params: WindowParams,
919    ) -> anyhow::Result<Box<dyn PlatformWindow>> {
920        let mut state = self.0.borrow_mut();
921
922        // Popups name their parent explicitly. Other kinds are parented to the focused window.
923        let (parent, popup_grab) = match &params.kind {
924            WindowKind::AnchoredPopup(options) => {
925                let parent = state
926                    .windows
927                    .values()
928                    .find(|window| window.handle() == options.parent)
929                    .cloned()
930                    .ok_or_else(|| anyhow::anyhow!("popup parent window not found"))?;
931                // A popup grab must reference a press event or the compositor declines it and
932                // immediately dismisses the popup, so use the most recent press serial, or no
933                // grab before any press.
934                let popup_grab = options.grab.then(|| {
935                    let serial = state
936                        .serial_tracker
937                        .get(SerialKind::MousePress)
938                        .as_raw()
939                        .max(state.serial_tracker.get(SerialKind::KeyPress).as_raw());
940                    (serial != 0).then(|| (serial, state.wl_seat.clone()))
941                });
942                (Some(parent), popup_grab.flatten())
943            }
944            _ => (state.keyboard_focused_window.clone(), None),
945        };
946
947        let target_output = params.display_id.and_then(|display_id| {
948            let target_protocol_id: u64 = display_id.into();
949            state
950                .wl_outputs
951                .iter()
952                .find(|(id, _)| id.protocol_id() as u64 == target_protocol_id)
953                .map(|(_, output)| output.clone())
954        });
955
956        let appearance = state.common.appearance;
957        let compositor_gpu = state.compositor_gpu.take();
958
959        let (window, surface_id) = WaylandWindow::new(
960            handle,
961            state.globals.clone(),
962            state.gpu_context.clone(),
963            compositor_gpu,
964            WaylandClientStatePtr(Rc::downgrade(&self.0)),
965            params,
966            appearance,
967            parent,
968            popup_grab,
969            target_output,
970        )?;
971
972        if window.0.toplevel().is_some() {
973            state.consume_startup_activation_token(&window.0.surface());
974        }
975        state.windows.insert(surface_id, window.0.clone());
976
977        Ok(Box::new(window))
978    }
979
980    fn set_cursor_style(&self, style: CursorStyle) {
981        let mut state = self.0.borrow_mut();
982
983        let need_update = state.cursor_style != Some(style)
984            && (state.mouse_focused_window.is_none()
985                || state
986                    .mouse_focused_window
987                    .as_ref()
988                    .is_some_and(|w| !w.is_blocked()));
989
990        if !need_update {
991            return;
992        }
993
994        state.cursor_style = Some(style);
995
996        // Don't clobber the invisible cursor; restore reads back from `cursor_style`.
997        if state.cursor_hidden_window.is_some() {
998            return;
999        }
1000
1001        let serial = state.serial_tracker.get(SerialKind::MouseEnter);
1002        if let Some(cursor_shape_device) = &state.cursor_shape_device {
1003            cursor_shape_device.set_shape(serial.as_raw(), to_shape(style));
1004        } else if let Some(focused_window) = &state.mouse_focused_window {
1005            // cursor-shape-v1 isn't supported, set the cursor using a surface.
1006            let wl_pointer = state
1007                .wl_pointer
1008                .clone()
1009                .expect("window is focused by pointer");
1010            let scale = focused_window.primary_output_scale();
1011            state.cursor.set_icon(
1012                &wl_pointer,
1013                serial.as_raw(),
1014                cursor_style_to_icon_names(style),
1015                scale,
1016            );
1017        }
1018    }
1019
1020    fn hide_cursor_until_mouse_moves(&self) {
1021        self.0.borrow_mut().hide_cursor_until_mouse_moves();
1022    }
1023
1024    fn is_cursor_visible(&self) -> bool {
1025        self.0.borrow().cursor_hidden_window.is_none()
1026    }
1027
1028    fn open_uri(&self, uri: &str) {
1029        let mut state = self.0.borrow_mut();
1030        if let (Some(activation), Some(window)) = (
1031            state.globals.activation.clone(),
1032            state.mouse_focused_window.clone(),
1033        ) {
1034            state.pending_activation = Some(PendingActivation::Uri(uri.to_string()));
1035            let token = activation.get_activation_token(&state.globals.qh, ());
1036            let serial = state.serial_tracker.get(SerialKind::MousePress);
1037            token.set_serial(serial.as_raw(), &state.wl_seat);
1038            token.set_surface(&window.surface());
1039            token.commit();
1040        } else {
1041            let executor = state.common.background_executor.clone();
1042            open_uri_internal(executor, uri, None);
1043        }
1044    }
1045
1046    fn reveal_path(&self, path: PathBuf) {
1047        let mut state = self.0.borrow_mut();
1048        if let (Some(activation), Some(window)) = (
1049            state.globals.activation.clone(),
1050            state.mouse_focused_window.clone(),
1051        ) {
1052            state.pending_activation = Some(PendingActivation::Path(path));
1053            let token = activation.get_activation_token(&state.globals.qh, ());
1054            let serial = state.serial_tracker.get(SerialKind::MousePress);
1055            token.set_serial(serial.as_raw(), &state.wl_seat);
1056            token.set_surface(&window.surface());
1057            token.commit();
1058        } else {
1059            let executor = state.common.background_executor.clone();
1060            reveal_path_internal(executor, path, None);
1061        }
1062    }
1063
1064    fn with_common<R>(&self, f: impl FnOnce(&mut LinuxCommon) -> R) -> R {
1065        f(&mut self.0.borrow_mut().common)
1066    }
1067
1068    fn run(&self) {
1069        let mut event_loop = self
1070            .0
1071            .borrow_mut()
1072            .event_loop
1073            .take()
1074            .expect("App is already running");
1075
1076        event_loop
1077            .run(
1078                None,
1079                &mut WaylandClientStatePtr(Rc::downgrade(&self.0)),
1080                |_| {},
1081            )
1082            .log_err();
1083    }
1084
1085    fn write_to_primary(&self, item: gpui::ClipboardItem) {
1086        let mut state = self.0.borrow_mut();
1087        let (Some(primary_selection_manager), Some(primary_selection)) = (
1088            state.globals.primary_selection_manager.clone(),
1089            state.primary_selection.clone(),
1090        ) else {
1091            return;
1092        };
1093        if state.mouse_focused_window.is_some() || state.keyboard_focused_window.is_some() {
1094            state.clipboard.set_primary(item);
1095            let Some(serial) = state.serial_tracker.selection_serial() else {
1096                log::warn!(
1097                    "Skipping Wayland primary selection ownership request because no keyboard or pointer press serial has been received"
1098                );
1099                return;
1100            };
1101            let data_source = primary_selection_manager.create_source(&state.globals.qh, ());
1102            for mime_type in TEXT_MIME_TYPES {
1103                data_source.offer(mime_type.to_string());
1104            }
1105            data_source.offer(state.clipboard.self_mime());
1106            primary_selection.set_selection(Some(&data_source), serial.as_raw());
1107        }
1108    }
1109
1110    fn write_to_clipboard(&self, item: gpui::ClipboardItem) {
1111        let mut state = self.0.borrow_mut();
1112        let (Some(data_device_manager), Some(data_device)) = (
1113            state.globals.data_device_manager.clone(),
1114            state.data_device.clone(),
1115        ) else {
1116            return;
1117        };
1118        if state.mouse_focused_window.is_some() || state.keyboard_focused_window.is_some() {
1119            state.clipboard.set(item);
1120            let Some(serial) = state.serial_tracker.selection_serial() else {
1121                log::warn!(
1122                    "Skipping Wayland clipboard ownership request because no keyboard or pointer press serial has been received"
1123                );
1124                return;
1125            };
1126            let data_source = data_device_manager.create_data_source(&state.globals.qh, ());
1127            for mime_type in TEXT_MIME_TYPES {
1128                data_source.offer(mime_type.to_string());
1129            }
1130            data_source.offer(state.clipboard.self_mime());
1131            data_device.set_selection(Some(&data_source), serial.as_raw());
1132        }
1133    }
1134
1135    fn read_from_primary(&self) -> Option<gpui::ClipboardItem> {
1136        self.0.borrow_mut().clipboard.read_primary()
1137    }
1138
1139    fn read_from_clipboard(&self) -> Option<gpui::ClipboardItem> {
1140        self.0.borrow_mut().clipboard.read()
1141    }
1142
1143    fn active_window(&self) -> Option<AnyWindowHandle> {
1144        self.0
1145            .borrow_mut()
1146            .keyboard_focused_window
1147            .as_ref()
1148            .map(|window| window.handle())
1149    }
1150
1151    fn window_stack(&self) -> Option<Vec<AnyWindowHandle>> {
1152        None
1153    }
1154
1155    fn compositor_name(&self) -> &'static str {
1156        "Wayland"
1157    }
1158
1159    fn window_identifier(&self) -> impl Future<Output = Option<WindowIdentifier>> + Send + 'static {
1160        async fn inner(surface: Option<wl_surface::WlSurface>) -> Option<WindowIdentifier> {
1161            if let Some(surface) = surface {
1162                ashpd::WindowIdentifier::from_wayland(&surface).await
1163            } else {
1164                None
1165            }
1166        }
1167
1168        let client_state = self.0.borrow();
1169        let active_window = client_state.keyboard_focused_window.as_ref();
1170        inner(active_window.map(|aw| aw.surface()))
1171    }
1172}
1173
1174struct DmabufProbeState {
1175    device: Option<u64>,
1176}
1177
1178impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for DmabufProbeState {
1179    fn event(
1180        _: &mut Self,
1181        _: &wl_registry::WlRegistry,
1182        _: wl_registry::Event,
1183        _: &GlobalListContents,
1184        _: &Connection,
1185        _: &QueueHandle<Self>,
1186    ) {
1187    }
1188}
1189
1190impl Dispatch<zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1, ()> for DmabufProbeState {
1191    fn event(
1192        _: &mut Self,
1193        _: &zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1,
1194        _: zwp_linux_dmabuf_v1::Event,
1195        _: &(),
1196        _: &Connection,
1197        _: &QueueHandle<Self>,
1198    ) {
1199    }
1200}
1201
1202impl Dispatch<zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1, ()> for DmabufProbeState {
1203    fn event(
1204        state: &mut Self,
1205        _: &zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1,
1206        event: zwp_linux_dmabuf_feedback_v1::Event,
1207        _: &(),
1208        _: &Connection,
1209        _: &QueueHandle<Self>,
1210    ) {
1211        if let zwp_linux_dmabuf_feedback_v1::Event::MainDevice { device } = event {
1212            if let Ok(bytes) = <[u8; 8]>::try_from(device.as_slice()) {
1213                state.device = Some(u64::from_ne_bytes(bytes));
1214            }
1215        }
1216    }
1217}
1218
1219fn detect_compositor_gpu() -> Option<CompositorGpuHint> {
1220    let connection = Connection::connect_to_env().ok()?;
1221    let (globals, mut event_queue) = registry_queue_init::<DmabufProbeState>(&connection).ok()?;
1222    let queue_handle = event_queue.handle();
1223
1224    let dmabuf: zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1 =
1225        globals.bind(&queue_handle, 4..=4, ()).ok()?;
1226    let feedback = dmabuf.get_default_feedback(&queue_handle, ());
1227
1228    let mut state = DmabufProbeState { device: None };
1229
1230    event_queue.roundtrip(&mut state).ok()?;
1231
1232    feedback.destroy();
1233    dmabuf.destroy();
1234
1235    crate::linux::compositor_gpu_hint_from_dev_t(state.device?)
1236}
1237
1238impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for WaylandClientStatePtr {
1239    fn event(
1240        this: &mut Self,
1241        registry: &wl_registry::WlRegistry,
1242        event: wl_registry::Event,
1243        _: &GlobalListContents,
1244        _: &Connection,
1245        qh: &QueueHandle<Self>,
1246    ) {
1247        let client = this.get_client();
1248        let mut state = client.borrow_mut();
1249
1250        match event {
1251            wl_registry::Event::Global {
1252                name,
1253                interface,
1254                version,
1255            } => match &interface[..] {
1256                "wl_seat" => {
1257                    if let Some(wl_pointer) = state.wl_pointer.take() {
1258                        wl_pointer.release();
1259                    }
1260                    if let Some(wl_keyboard) = state.wl_keyboard.take() {
1261                        wl_keyboard.release();
1262                    }
1263                    state.wl_seat.release();
1264                    state.wl_seat = registry.bind::<wl_seat::WlSeat, _, _>(
1265                        name,
1266                        wl_seat_version(version),
1267                        qh,
1268                        (),
1269                    );
1270                }
1271                "wl_output" => {
1272                    let output = registry.bind::<wl_output::WlOutput, _, _>(
1273                        name,
1274                        wl_output_version(version),
1275                        qh,
1276                        (),
1277                    );
1278
1279                    state
1280                        .in_progress_outputs
1281                        .insert(output.id(), InProgressOutput::default());
1282                    state.wl_outputs.insert(output.id(), output);
1283                }
1284                _ => {}
1285            },
1286            wl_registry::Event::GlobalRemove { name: _ } => {
1287                // TODO: handle global removal
1288            }
1289            _ => {}
1290        }
1291    }
1292}
1293
1294delegate_noop!(WaylandClientStatePtr: ignore xdg_activation_v1::XdgActivationV1);
1295delegate_noop!(WaylandClientStatePtr: ignore xdg_system_bell_v1::XdgSystemBellV1);
1296delegate_noop!(WaylandClientStatePtr: ignore wl_compositor::WlCompositor);
1297delegate_noop!(WaylandClientStatePtr: ignore wp_cursor_shape_device_v1::WpCursorShapeDeviceV1);
1298delegate_noop!(WaylandClientStatePtr: ignore wp_cursor_shape_manager_v1::WpCursorShapeManagerV1);
1299delegate_noop!(WaylandClientStatePtr: ignore wl_data_device_manager::WlDataDeviceManager);
1300delegate_noop!(WaylandClientStatePtr: ignore zwp_primary_selection_device_manager_v1::ZwpPrimarySelectionDeviceManagerV1);
1301delegate_noop!(WaylandClientStatePtr: ignore wl_shm::WlShm);
1302delegate_noop!(WaylandClientStatePtr: ignore wl_shm_pool::WlShmPool);
1303delegate_noop!(WaylandClientStatePtr: ignore wl_buffer::WlBuffer);
1304delegate_noop!(WaylandClientStatePtr: ignore wl_region::WlRegion);
1305delegate_noop!(WaylandClientStatePtr: ignore wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1);
1306delegate_noop!(WaylandClientStatePtr: ignore zxdg_decoration_manager_v1::ZxdgDecorationManagerV1);
1307delegate_noop!(WaylandClientStatePtr: ignore zwlr_layer_shell_v1::ZwlrLayerShellV1);
1308delegate_noop!(WaylandClientStatePtr: ignore xdg_positioner::XdgPositioner);
1309delegate_noop!(WaylandClientStatePtr: ignore org_kde_kwin_blur_manager::OrgKdeKwinBlurManager);
1310delegate_noop!(WaylandClientStatePtr: ignore zwp_text_input_manager_v3::ZwpTextInputManagerV3);
1311delegate_noop!(WaylandClientStatePtr: ignore org_kde_kwin_blur::OrgKdeKwinBlur);
1312delegate_noop!(WaylandClientStatePtr: ignore wp_viewporter::WpViewporter);
1313delegate_noop!(WaylandClientStatePtr: ignore wp_viewport::WpViewport);
1314
1315impl Dispatch<WlCallback, ObjectId> for WaylandClientStatePtr {
1316    fn event(
1317        state: &mut WaylandClientStatePtr,
1318        _: &wl_callback::WlCallback,
1319        event: wl_callback::Event,
1320        surface_id: &ObjectId,
1321        _: &Connection,
1322        _: &QueueHandle<Self>,
1323    ) {
1324        let client = state.get_client();
1325        let mut state = client.borrow_mut();
1326        let Some(window) = get_window(&mut state, surface_id) else {
1327            return;
1328        };
1329        drop(state);
1330
1331        if let wl_callback::Event::Done { .. } = event {
1332            window.frame();
1333        }
1334    }
1335}
1336
1337pub(crate) fn get_window(
1338    state: &mut RefMut<WaylandClientState>,
1339    surface_id: &ObjectId,
1340) -> Option<WaylandWindowStatePtr> {
1341    state.windows.get(surface_id).cloned()
1342}
1343
1344impl Dispatch<wl_surface::WlSurface, ()> for WaylandClientStatePtr {
1345    fn event(
1346        this: &mut Self,
1347        surface: &wl_surface::WlSurface,
1348        event: <wl_surface::WlSurface as Proxy>::Event,
1349        _: &(),
1350        _: &Connection,
1351        _: &QueueHandle<Self>,
1352    ) {
1353        let client = this.get_client();
1354        let mut state = client.borrow_mut();
1355
1356        let Some(window) = get_window(&mut state, &surface.id()) else {
1357            return;
1358        };
1359        #[allow(clippy::mutable_key_type)]
1360        let outputs = state.outputs.clone();
1361        drop(state);
1362
1363        window.handle_surface_event(event, outputs);
1364    }
1365}
1366
1367impl Dispatch<wl_output::WlOutput, ()> for WaylandClientStatePtr {
1368    fn event(
1369        this: &mut Self,
1370        output: &wl_output::WlOutput,
1371        event: <wl_output::WlOutput as Proxy>::Event,
1372        _: &(),
1373        _: &Connection,
1374        _: &QueueHandle<Self>,
1375    ) {
1376        let client = this.get_client();
1377        let mut state = client.borrow_mut();
1378
1379        let Some(in_progress_output) = state.in_progress_outputs.get_mut(&output.id()) else {
1380            return;
1381        };
1382
1383        match event {
1384            wl_output::Event::Name { name } => {
1385                in_progress_output.name = Some(name);
1386            }
1387            wl_output::Event::Scale { factor } => {
1388                in_progress_output.scale = Some(factor);
1389            }
1390            wl_output::Event::Geometry { x, y, subpixel, .. } => {
1391                in_progress_output.position = Some(point(DevicePixels(x), DevicePixels(y)));
1392                if let WEnum::Value(subpixel) = subpixel {
1393                    in_progress_output.subpixel = Some(subpixel);
1394                }
1395            }
1396            wl_output::Event::Mode { width, height, .. } => {
1397                in_progress_output.size = Some(size(DevicePixels(width), DevicePixels(height)))
1398            }
1399            wl_output::Event::Done => {
1400                if let Some(complete) = in_progress_output.complete() {
1401                    state.outputs.insert(output.id(), complete);
1402                }
1403                state.in_progress_outputs.remove(&output.id());
1404            }
1405            _ => {}
1406        }
1407    }
1408}
1409
1410impl Dispatch<xdg_surface::XdgSurface, ObjectId> for WaylandClientStatePtr {
1411    fn event(
1412        state: &mut Self,
1413        _: &xdg_surface::XdgSurface,
1414        event: xdg_surface::Event,
1415        surface_id: &ObjectId,
1416        _: &Connection,
1417        _: &QueueHandle<Self>,
1418    ) {
1419        let client = state.get_client();
1420        let mut state = client.borrow_mut();
1421        let Some(window) = get_window(&mut state, surface_id) else {
1422            return;
1423        };
1424        drop(state);
1425        window.handle_xdg_surface_event(event);
1426    }
1427}
1428
1429impl Dispatch<xdg_toplevel::XdgToplevel, ObjectId> for WaylandClientStatePtr {
1430    fn event(
1431        this: &mut Self,
1432        _: &xdg_toplevel::XdgToplevel,
1433        event: <xdg_toplevel::XdgToplevel as Proxy>::Event,
1434        surface_id: &ObjectId,
1435        _: &Connection,
1436        _: &QueueHandle<Self>,
1437    ) {
1438        let client = this.get_client();
1439        let mut state = client.borrow_mut();
1440        let Some(window) = get_window(&mut state, surface_id) else {
1441            return;
1442        };
1443
1444        drop(state);
1445        let should_close = window.handle_toplevel_event(event);
1446
1447        if should_close {
1448            // The close logic will be handled in drop_window()
1449            window.close();
1450        }
1451    }
1452}
1453
1454impl Dispatch<zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, ObjectId> for WaylandClientStatePtr {
1455    fn event(
1456        this: &mut Self,
1457        _: &zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,
1458        event: <zwlr_layer_surface_v1::ZwlrLayerSurfaceV1 as Proxy>::Event,
1459        surface_id: &ObjectId,
1460        _: &Connection,
1461        _: &QueueHandle<Self>,
1462    ) {
1463        let client = this.get_client();
1464        let mut state = client.borrow_mut();
1465        let Some(window) = get_window(&mut state, surface_id) else {
1466            return;
1467        };
1468
1469        drop(state);
1470        let should_close = window.handle_layersurface_event(event);
1471
1472        if should_close {
1473            // Close logic will be handled in drop_window()
1474            window.close();
1475        }
1476    }
1477}
1478
1479impl Dispatch<xdg_popup::XdgPopup, ObjectId> for WaylandClientStatePtr {
1480    fn event(
1481        this: &mut Self,
1482        _: &xdg_popup::XdgPopup,
1483        event: <xdg_popup::XdgPopup as Proxy>::Event,
1484        surface_id: &ObjectId,
1485        _: &Connection,
1486        _: &QueueHandle<Self>,
1487    ) {
1488        let client = this.get_client();
1489        let mut state = client.borrow_mut();
1490        let Some(window) = get_window(&mut state, surface_id) else {
1491            return;
1492        };
1493
1494        drop(state);
1495        let should_close = window.handle_popup_event(event);
1496
1497        if should_close {
1498            // The close logic will be handled in drop_window()
1499            window.close();
1500        }
1501    }
1502}
1503
1504impl Dispatch<xdg_wm_base::XdgWmBase, ()> for WaylandClientStatePtr {
1505    fn event(
1506        _: &mut Self,
1507        wm_base: &xdg_wm_base::XdgWmBase,
1508        event: <xdg_wm_base::XdgWmBase as Proxy>::Event,
1509        _: &(),
1510        _: &Connection,
1511        _: &QueueHandle<Self>,
1512    ) {
1513        if let xdg_wm_base::Event::Ping { serial } = event {
1514            wm_base.pong(serial);
1515        }
1516    }
1517}
1518
1519impl Dispatch<xdg_activation_token_v1::XdgActivationTokenV1, ()> for WaylandClientStatePtr {
1520    fn event(
1521        this: &mut Self,
1522        token: &xdg_activation_token_v1::XdgActivationTokenV1,
1523        event: <xdg_activation_token_v1::XdgActivationTokenV1 as Proxy>::Event,
1524        _: &(),
1525        _: &Connection,
1526        _: &QueueHandle<Self>,
1527    ) {
1528        let client = this.get_client();
1529        let mut state = client.borrow_mut();
1530
1531        if let xdg_activation_token_v1::Event::Done { token } = event {
1532            let executor = state.common.background_executor.clone();
1533            match state.pending_activation.take() {
1534                Some(PendingActivation::Uri(uri)) => open_uri_internal(executor, &uri, Some(token)),
1535                Some(PendingActivation::Path(path)) => {
1536                    reveal_path_internal(executor, path, Some(token))
1537                }
1538                Some(PendingActivation::Window(window)) => {
1539                    let Some(window) = get_window(&mut state, &window) else {
1540                        return;
1541                    };
1542                    let activation = state.globals.activation.as_ref().unwrap();
1543                    activation.activate(token, &window.surface());
1544                }
1545                None => log::error!("activation token received with no pending activation"),
1546            }
1547        }
1548
1549        token.destroy();
1550    }
1551}
1552
1553impl Dispatch<wl_seat::WlSeat, ()> for WaylandClientStatePtr {
1554    fn event(
1555        state: &mut Self,
1556        seat: &wl_seat::WlSeat,
1557        event: wl_seat::Event,
1558        _: &(),
1559        _: &Connection,
1560        qh: &QueueHandle<Self>,
1561    ) {
1562        if let wl_seat::Event::Capabilities {
1563            capabilities: WEnum::Value(capabilities),
1564        } = event
1565        {
1566            let client = state.get_client();
1567            let mut state = client.borrow_mut();
1568            if capabilities.contains(wl_seat::Capability::Keyboard) {
1569                let keyboard = seat.get_keyboard(qh, ());
1570
1571                if let Some(text_input) = state.text_input.take() {
1572                    text_input.destroy();
1573                    state.ime_pre_edit = None;
1574                    state.composing = false;
1575                }
1576
1577                state.text_input = state
1578                    .globals
1579                    .text_input_manager
1580                    .as_ref()
1581                    .map(|text_input_manager| text_input_manager.get_text_input(seat, qh, ()));
1582
1583                if let Some(wl_keyboard) = &state.wl_keyboard {
1584                    wl_keyboard.release();
1585                }
1586
1587                state.wl_keyboard = Some(keyboard);
1588            }
1589            if capabilities.contains(wl_seat::Capability::Pointer) {
1590                let pointer = seat.get_pointer(qh, ());
1591
1592                if let Some(cursor_shape_device) = state.cursor_shape_device.take() {
1593                    cursor_shape_device.destroy();
1594                }
1595
1596                state.cursor_shape_device = state
1597                    .globals
1598                    .cursor_shape_manager
1599                    .as_ref()
1600                    .map(|cursor_shape_manager| cursor_shape_manager.get_pointer(&pointer, qh, ()));
1601
1602                state.pinch_gesture = state.globals.gesture_manager.as_ref().map(
1603                    |gesture_manager: &zwp_pointer_gestures_v1::ZwpPointerGesturesV1| {
1604                        gesture_manager.get_pinch_gesture(&pointer, qh, ())
1605                    },
1606                );
1607
1608                if let Some(wl_pointer) = &state.wl_pointer {
1609                    wl_pointer.release();
1610                }
1611
1612                state.wl_pointer = Some(pointer);
1613            }
1614        }
1615    }
1616}
1617
1618impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClientStatePtr {
1619    fn event(
1620        this: &mut Self,
1621        _: &wl_keyboard::WlKeyboard,
1622        event: wl_keyboard::Event,
1623        _: &(),
1624        _: &Connection,
1625        _: &QueueHandle<Self>,
1626    ) {
1627        let client = this.get_client();
1628        let mut state = client.borrow_mut();
1629        match event {
1630            wl_keyboard::Event::RepeatInfo { rate, delay } => {
1631                state.repeat.characters_per_second = rate as u32;
1632                state.repeat.delay = Duration::from_millis(delay as u64);
1633            }
1634            wl_keyboard::Event::Keymap {
1635                format: WEnum::Value(format),
1636                fd,
1637                size,
1638                ..
1639            } => {
1640                if format != wl_keyboard::KeymapFormat::XkbV1 {
1641                    log::error!("Received keymap format {:?}, expected XkbV1", format);
1642                    return;
1643                }
1644                let xkb_context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
1645                let keymap = unsafe {
1646                    xkb::Keymap::new_from_fd(
1647                        &xkb_context,
1648                        fd,
1649                        size as usize,
1650                        XKB_KEYMAP_FORMAT_TEXT_V1,
1651                        KEYMAP_COMPILE_NO_FLAGS,
1652                    )
1653                    .log_err()
1654                    .flatten()
1655                    .expect("Failed to create keymap")
1656                };
1657                state.keymap_state = Some(xkb::State::new(&keymap));
1658                state.compose_state = get_xkb_compose_state(&xkb_context);
1659                drop(state);
1660
1661                this.handle_keyboard_layout_change();
1662            }
1663            wl_keyboard::Event::Enter { surface, .. } => {
1664                state.keyboard_focused_window = get_window(&mut state, &surface.id());
1665                state.enter_token = Some(());
1666
1667                if let Some(window) = state.keyboard_focused_window.clone() {
1668                    drop(state);
1669                    window.set_focused(true);
1670                }
1671            }
1672            wl_keyboard::Event::Leave { surface, .. } => {
1673                let keyboard_focused_window = get_window(&mut state, &surface.id());
1674                state.keyboard_focused_window = None;
1675                state.enter_token.take();
1676                // Prevent keyboard events from repeating after opening e.g. a file chooser and closing it quickly
1677                state.repeat.current_id += 1;
1678                state.restore_cursor_after_hide();
1679
1680                if let Some(window) = keyboard_focused_window {
1681                    if let Some(ref mut compose) = state.compose_state {
1682                        compose.reset();
1683                    }
1684                    state.pre_edit_text.take();
1685                    drop(state);
1686                    window.handle_ime(ImeInput::DeleteText);
1687                    window.set_focused(false);
1688                }
1689            }
1690            wl_keyboard::Event::Modifiers {
1691                mods_depressed,
1692                mods_latched,
1693                mods_locked,
1694                group,
1695                ..
1696            } => {
1697                let focused_window = state.keyboard_focused_window.clone();
1698
1699                let keymap_state = state.keymap_state.as_mut().unwrap();
1700                let old_layout =
1701                    keymap_state.serialize_layout(xkbcommon::xkb::STATE_LAYOUT_EFFECTIVE);
1702                keymap_state.update_mask(mods_depressed, mods_latched, mods_locked, 0, 0, group);
1703                state.modifiers = modifiers_from_xkb(keymap_state);
1704                let keymap_state = state.keymap_state.as_mut().unwrap();
1705                state.capslock = capslock_from_xkb(keymap_state);
1706
1707                let input = PlatformInput::ModifiersChanged(ModifiersChangedEvent {
1708                    modifiers: state.modifiers,
1709                    capslock: state.capslock,
1710                });
1711                drop(state);
1712
1713                if let Some(focused_window) = focused_window {
1714                    focused_window.handle_input(input);
1715                }
1716
1717                if group != old_layout {
1718                    this.handle_keyboard_layout_change();
1719                }
1720            }
1721            wl_keyboard::Event::Key {
1722                serial,
1723                key,
1724                state: WEnum::Value(key_state),
1725                ..
1726            } => {
1727                if key_state == wl_keyboard::KeyState::Pressed {
1728                    state.serial_tracker.update(SerialKind::KeyPress, serial);
1729                }
1730
1731                let focused_window = state.keyboard_focused_window.clone();
1732                let Some(focused_window) = focused_window else {
1733                    return;
1734                };
1735
1736                let keymap_state = state.keymap_state.as_ref().unwrap();
1737                let keycode = Keycode::from(key + MIN_KEYCODE);
1738                let keysym = keymap_state.key_get_one_sym(keycode);
1739
1740                match key_state {
1741                    wl_keyboard::KeyState::Pressed if !keysym.is_modifier_key() => {
1742                        let mut keystroke =
1743                            keystroke_from_xkb(keymap_state, state.modifiers, keycode);
1744                        if let Some(mut compose) = state.compose_state.take() {
1745                            compose.feed(keysym);
1746                            match compose.status() {
1747                                xkb::Status::Composing => {
1748                                    keystroke.key_char = None;
1749                                    state.pre_edit_text =
1750                                        compose.utf8().or(keystroke_underlying_dead_key(keysym));
1751                                    let pre_edit =
1752                                        state.pre_edit_text.clone().unwrap_or(String::default());
1753                                    drop(state);
1754                                    focused_window.handle_ime(ImeInput::SetMarkedText(pre_edit));
1755                                    state = client.borrow_mut();
1756                                }
1757
1758                                xkb::Status::Composed => {
1759                                    state.pre_edit_text.take();
1760                                    keystroke.key_char = compose.utf8();
1761                                    if let Some(keysym) = compose.keysym() {
1762                                        keystroke.key = xkb::keysym_get_name(keysym);
1763                                    }
1764                                }
1765                                xkb::Status::Cancelled => {
1766                                    let pre_edit = state.pre_edit_text.take();
1767                                    let new_pre_edit = keystroke_underlying_dead_key(keysym);
1768                                    state.pre_edit_text = new_pre_edit.clone();
1769                                    drop(state);
1770                                    if let Some(pre_edit) = pre_edit {
1771                                        focused_window.handle_ime(ImeInput::InsertText(pre_edit));
1772                                    }
1773                                    if let Some(current_key) = new_pre_edit {
1774                                        focused_window
1775                                            .handle_ime(ImeInput::SetMarkedText(current_key));
1776                                    }
1777                                    compose.feed(keysym);
1778                                    state = client.borrow_mut();
1779                                }
1780                                _ => {}
1781                            }
1782                            state.compose_state = Some(compose);
1783                        }
1784                        let input = PlatformInput::KeyDown(KeyDownEvent {
1785                            keystroke: keystroke.clone(),
1786                            is_held: false,
1787                            prefer_character_input: false,
1788                        });
1789
1790                        state.repeat.current_id += 1;
1791                        state.repeat.current_keycode = Some(keycode);
1792
1793                        let rate = state.repeat.characters_per_second;
1794                        let repeat_interval = Duration::from_secs(1) / rate.max(1);
1795                        let id = state.repeat.current_id;
1796                        state
1797                            .loop_handle
1798                            .insert_source(Timer::from_duration(state.repeat.delay), {
1799                                let input = PlatformInput::KeyDown(KeyDownEvent {
1800                                    keystroke,
1801                                    is_held: true,
1802                                    prefer_character_input: false,
1803                                });
1804                                move |event_timestamp, _metadata, this| {
1805                                    let client = this.get_client();
1806                                    let state = client.borrow();
1807                                    let is_repeating = id == state.repeat.current_id
1808                                        && state.repeat.current_keycode.is_some()
1809                                        && state.keyboard_focused_window.is_some();
1810
1811                                    if !is_repeating || rate == 0 {
1812                                        return TimeoutAction::Drop;
1813                                    }
1814
1815                                    let focused_window =
1816                                        state.keyboard_focused_window.as_ref().unwrap().clone();
1817
1818                                    drop(state);
1819                                    focused_window.handle_input(input.clone());
1820
1821                                    // If the new scheduled time is in the past the event will repeat as soon as possible
1822                                    TimeoutAction::ToInstant(event_timestamp + repeat_interval)
1823                                }
1824                            })
1825                            .unwrap();
1826
1827                        drop(state);
1828                        focused_window.handle_input(input);
1829                    }
1830                    wl_keyboard::KeyState::Released if !keysym.is_modifier_key() => {
1831                        let input = PlatformInput::KeyUp(KeyUpEvent {
1832                            keystroke: keystroke_from_xkb(keymap_state, state.modifiers, keycode),
1833                        });
1834
1835                        if state.repeat.current_keycode == Some(keycode) {
1836                            state.repeat.current_keycode = None;
1837                        }
1838
1839                        drop(state);
1840                        focused_window.handle_input(input);
1841                    }
1842                    _ => {}
1843                }
1844            }
1845            _ => {}
1846        }
1847    }
1848}
1849
1850impl Dispatch<zwp_text_input_v3::ZwpTextInputV3, ()> for WaylandClientStatePtr {
1851    fn event(
1852        this: &mut Self,
1853        text_input: &zwp_text_input_v3::ZwpTextInputV3,
1854        event: <zwp_text_input_v3::ZwpTextInputV3 as Proxy>::Event,
1855        _: &(),
1856        _: &Connection,
1857        _: &QueueHandle<Self>,
1858    ) {
1859        let client = this.get_client();
1860        let mut state = client.borrow_mut();
1861        match event {
1862            zwp_text_input_v3::Event::Enter { .. } => {
1863                drop(state);
1864                this.enable_ime();
1865            }
1866            zwp_text_input_v3::Event::Leave { .. } => {
1867                drop(state);
1868                this.disable_ime();
1869            }
1870            zwp_text_input_v3::Event::CommitString { text } => {
1871                state.composing = false;
1872                let Some(window) = state.keyboard_focused_window.clone() else {
1873                    return;
1874                };
1875
1876                if let Some(commit_text) = text {
1877                    drop(state);
1878                    // IBus Intercepts keys like `a`, `b`, but those keys are needed for vim mode.
1879                    // We should only send ASCII characters to Zed, otherwise a user could remap a letter like `か` or `相`.
1880                    if commit_text.len() == 1 {
1881                        window.handle_input(PlatformInput::KeyDown(KeyDownEvent {
1882                            keystroke: Keystroke {
1883                                modifiers: Modifiers::default(),
1884                                key: commit_text.clone(),
1885                                key_char: Some(commit_text),
1886                            },
1887                            is_held: false,
1888                            prefer_character_input: false,
1889                        }));
1890                    } else {
1891                        window.handle_ime(ImeInput::InsertText(commit_text));
1892                    }
1893                }
1894            }
1895            zwp_text_input_v3::Event::PreeditString { text, .. } => {
1896                state.composing = true;
1897                state.ime_pre_edit = text;
1898            }
1899            zwp_text_input_v3::Event::Done { serial } => {
1900                let last_serial = state.serial_tracker.get(SerialKind::InputMethod);
1901                state.serial_tracker.update(SerialKind::InputMethod, serial);
1902                let Some(window) = state.keyboard_focused_window.clone() else {
1903                    return;
1904                };
1905
1906                if let Some(text) = state.ime_pre_edit.take() {
1907                    drop(state);
1908                    window.handle_ime(ImeInput::SetMarkedText(text));
1909                    if let Some(area) = window.get_ime_area() {
1910                        let mut state = client.borrow_mut();
1911                        set_ime_cursor_rectangle_after_done(
1912                            text_input,
1913                            &mut state.last_ime_cursor_rectangle,
1914                            area,
1915                            last_serial.as_raw() == serial,
1916                        );
1917                    }
1918                } else {
1919                    state.composing = false;
1920                    drop(state);
1921                    window.handle_ime(ImeInput::DeleteText);
1922                }
1923            }
1924            _ => {}
1925        }
1926    }
1927}
1928
1929fn linux_button_to_gpui(button: u32) -> Option<MouseButton> {
1930    // These values are coming from <linux/input-event-codes.h>.
1931    const BTN_LEFT: u32 = 0x110;
1932    const BTN_RIGHT: u32 = 0x111;
1933    const BTN_MIDDLE: u32 = 0x112;
1934    const BTN_SIDE: u32 = 0x113;
1935    const BTN_EXTRA: u32 = 0x114;
1936    const BTN_FORWARD: u32 = 0x115;
1937    const BTN_BACK: u32 = 0x116;
1938
1939    Some(match button {
1940        BTN_LEFT => MouseButton::Left,
1941        BTN_RIGHT => MouseButton::Right,
1942        BTN_MIDDLE => MouseButton::Middle,
1943        BTN_BACK | BTN_SIDE => MouseButton::Navigate(NavigationDirection::Back),
1944        BTN_FORWARD | BTN_EXTRA => MouseButton::Navigate(NavigationDirection::Forward),
1945        _ => return None,
1946    })
1947}
1948
1949impl Dispatch<wl_pointer::WlPointer, ()> for WaylandClientStatePtr {
1950    fn event(
1951        this: &mut Self,
1952        wl_pointer: &wl_pointer::WlPointer,
1953        event: wl_pointer::Event,
1954        _: &(),
1955        _: &Connection,
1956        _: &QueueHandle<Self>,
1957    ) {
1958        let client = this.get_client();
1959        let mut state = client.borrow_mut();
1960
1961        match event {
1962            wl_pointer::Event::Enter {
1963                serial,
1964                surface,
1965                surface_x,
1966                surface_y,
1967                ..
1968            } => {
1969                let position = point(px(surface_x as f32), px(surface_y as f32));
1970                state.serial_tracker.update(SerialKind::MouseEnter, serial);
1971                state.mouse_location = Some(position);
1972                state.button_pressed = None;
1973
1974                if let Some(window) = get_window(&mut state, &surface.id()) {
1975                    state.mouse_focused_window = Some(window.clone());
1976
1977                    if state.enter_token.is_some() {
1978                        state.enter_token = None;
1979                    }
1980                    state.restore_cursor_after_hide();
1981                    if let Some(style) = state.cursor_style {
1982                        if let Some(cursor_shape_device) = &state.cursor_shape_device {
1983                            cursor_shape_device.set_shape(serial, to_shape(style));
1984                        } else {
1985                            let scale = window.primary_output_scale();
1986                            state.cursor.set_icon(
1987                                wl_pointer,
1988                                serial,
1989                                cursor_style_to_icon_names(style),
1990                                scale,
1991                            );
1992                        }
1993                    }
1994                    let modifiers = state.modifiers;
1995                    drop(state);
1996                    window.set_hovered(true);
1997                    // No Motion follows Enter unless the pointer keeps moving, so synthesize
1998                    // a MouseMove to establish hover at the entry position.
1999                    window.handle_input(PlatformInput::MouseMove(MouseMoveEvent {
2000                        position,
2001                        pressed_button: None,
2002                        modifiers,
2003                    }));
2004                }
2005            }
2006            wl_pointer::Event::Leave { .. } => {
2007                if let Some(focused_window) = state.mouse_focused_window.clone() {
2008                    let input = PlatformInput::MouseExited(MouseExitEvent {
2009                        position: state.mouse_location.unwrap(),
2010                        pressed_button: state.button_pressed,
2011                        modifiers: state.modifiers,
2012                    });
2013                    state.mouse_focused_window = None;
2014                    state.mouse_location = None;
2015                    state.button_pressed = None;
2016                    state.cursor_hidden_window = None;
2017
2018                    drop(state);
2019                    focused_window.handle_input(input);
2020                    focused_window.set_hovered(false);
2021                }
2022            }
2023            wl_pointer::Event::Motion {
2024                surface_x,
2025                surface_y,
2026                ..
2027            } => {
2028                if state.mouse_focused_window.is_none() {
2029                    return;
2030                }
2031                state.mouse_location = Some(point(px(surface_x as f32), px(surface_y as f32)));
2032                state.restore_cursor_after_hide();
2033
2034                if let Some(window) = state.mouse_focused_window.clone() {
2035                    if window.is_blocked() {
2036                        let default_style = CursorStyle::Arrow;
2037                        if state.cursor_style != Some(default_style) {
2038                            let serial = state.serial_tracker.get(SerialKind::MouseEnter);
2039                            state.cursor_style = Some(default_style);
2040
2041                            if let Some(cursor_shape_device) = &state.cursor_shape_device {
2042                                cursor_shape_device
2043                                    .set_shape(serial.as_raw(), to_shape(default_style));
2044                            } else {
2045                                // cursor-shape-v1 isn't supported, set the cursor using a surface.
2046                                let wl_pointer = state
2047                                    .wl_pointer
2048                                    .clone()
2049                                    .expect("window is focused by pointer");
2050                                let scale = window.primary_output_scale();
2051                                state.cursor.set_icon(
2052                                    &wl_pointer,
2053                                    serial.as_raw(),
2054                                    cursor_style_to_icon_names(default_style),
2055                                    scale,
2056                                );
2057                            }
2058                        }
2059                    }
2060                    if state
2061                        .keyboard_focused_window
2062                        .as_ref()
2063                        .is_some_and(|keyboard_window| window.ptr_eq(keyboard_window))
2064                    {
2065                        state.enter_token = None;
2066                    }
2067                    let input = PlatformInput::MouseMove(MouseMoveEvent {
2068                        position: state.mouse_location.unwrap(),
2069                        pressed_button: state.button_pressed,
2070                        modifiers: state.modifiers,
2071                    });
2072                    drop(state);
2073                    window.handle_input(input);
2074                }
2075            }
2076            wl_pointer::Event::Button {
2077                serial,
2078                button,
2079                state: WEnum::Value(button_state),
2080                ..
2081            } => {
2082                // Record presses only. Requests referencing this serial (popup grabs,
2083                // interactive moves) are declined when given a release serial.
2084                if button_state == wl_pointer::ButtonState::Pressed {
2085                    state.serial_tracker.update(SerialKind::MousePress, serial);
2086                }
2087                let button = linux_button_to_gpui(button);
2088                let Some(button) = button else { return };
2089                if state.mouse_focused_window.is_none() {
2090                    return;
2091                }
2092                match button_state {
2093                    wl_pointer::ButtonState::Pressed => {
2094                        if let Some(window) = state.keyboard_focused_window.clone() {
2095                            if state.composing && state.text_input.is_some() {
2096                                drop(state);
2097                                // text_input_v3 don't have something like a reset function
2098                                this.disable_ime();
2099                                this.enable_ime();
2100                                window.handle_ime(ImeInput::UnmarkText);
2101                                state = client.borrow_mut();
2102                            } else if let (Some(text), Some(compose)) =
2103                                (state.pre_edit_text.take(), state.compose_state.as_mut())
2104                            {
2105                                compose.reset();
2106                                drop(state);
2107                                window.handle_ime(ImeInput::InsertText(text));
2108                                state = client.borrow_mut();
2109                            }
2110                        }
2111                        let click_elapsed = state.click.last_click.elapsed();
2112
2113                        if click_elapsed < DOUBLE_CLICK_INTERVAL
2114                            && state
2115                                .click
2116                                .last_mouse_button
2117                                .is_some_and(|prev_button| prev_button == button)
2118                            && is_within_click_distance(
2119                                state.click.last_location,
2120                                state.mouse_location.unwrap(),
2121                            )
2122                        {
2123                            state.click.current_count += 1;
2124                        } else {
2125                            state.click.current_count = 1;
2126                        }
2127
2128                        state.click.last_click = Instant::now();
2129                        state.click.last_mouse_button = Some(button);
2130                        state.click.last_location = state.mouse_location.unwrap();
2131
2132                        state.button_pressed = Some(button);
2133
2134                        if let Some(window) = state.mouse_focused_window.clone() {
2135                            let input = PlatformInput::MouseDown(MouseDownEvent {
2136                                button,
2137                                position: state.mouse_location.unwrap(),
2138                                modifiers: state.modifiers,
2139                                click_count: state.click.current_count,
2140                                first_mouse: state.enter_token.take().is_some(),
2141                            });
2142                            drop(state);
2143                            window.handle_input(input);
2144                        }
2145                    }
2146                    wl_pointer::ButtonState::Released => {
2147                        state.button_pressed = None;
2148
2149                        if let Some(window) = state.mouse_focused_window.clone() {
2150                            let input = PlatformInput::MouseUp(MouseUpEvent {
2151                                button,
2152                                position: state.mouse_location.unwrap(),
2153                                modifiers: state.modifiers,
2154                                click_count: state.click.current_count,
2155                            });
2156                            drop(state);
2157                            window.handle_input(input);
2158                        }
2159                    }
2160                    _ => {}
2161                }
2162            }
2163
2164            // Axis Events
2165            wl_pointer::Event::AxisSource {
2166                axis_source: WEnum::Value(axis_source),
2167            } => {
2168                state.axis_source = axis_source;
2169            }
2170            wl_pointer::Event::Axis {
2171                axis: WEnum::Value(axis),
2172                value,
2173                ..
2174            } => {
2175                if state.axis_source == AxisSource::Wheel {
2176                    return;
2177                }
2178                let axis = if state.modifiers.shift {
2179                    wl_pointer::Axis::HorizontalScroll
2180                } else {
2181                    axis
2182                };
2183                let axis_modifier = match axis {
2184                    wl_pointer::Axis::VerticalScroll => state.vertical_modifier,
2185                    wl_pointer::Axis::HorizontalScroll => state.horizontal_modifier,
2186                    _ => 1.0,
2187                };
2188                state.scroll_event_received = true;
2189                let scroll_delta = state
2190                    .continuous_scroll_delta
2191                    .get_or_insert(point(px(0.0), px(0.0)));
2192                let modifier = 3.0;
2193                match axis {
2194                    wl_pointer::Axis::VerticalScroll => {
2195                        scroll_delta.y += px(value as f32 * modifier * axis_modifier);
2196                    }
2197                    wl_pointer::Axis::HorizontalScroll => {
2198                        scroll_delta.x += px(value as f32 * modifier * axis_modifier);
2199                    }
2200                    _ => unreachable!(),
2201                }
2202            }
2203            wl_pointer::Event::AxisDiscrete {
2204                axis: WEnum::Value(axis),
2205                discrete,
2206            } => {
2207                state.scroll_event_received = true;
2208                let axis = if state.modifiers.shift {
2209                    wl_pointer::Axis::HorizontalScroll
2210                } else {
2211                    axis
2212                };
2213                let axis_modifier = match axis {
2214                    wl_pointer::Axis::VerticalScroll => state.vertical_modifier,
2215                    wl_pointer::Axis::HorizontalScroll => state.horizontal_modifier,
2216                    _ => 1.0,
2217                };
2218
2219                let scroll_delta = state.discrete_scroll_delta.get_or_insert(point(0.0, 0.0));
2220                match axis {
2221                    wl_pointer::Axis::VerticalScroll => {
2222                        scroll_delta.y += discrete as f32 * axis_modifier * SCROLL_LINES;
2223                    }
2224                    wl_pointer::Axis::HorizontalScroll => {
2225                        scroll_delta.x += discrete as f32 * axis_modifier * SCROLL_LINES;
2226                    }
2227                    _ => unreachable!(),
2228                }
2229            }
2230            wl_pointer::Event::AxisValue120 {
2231                axis: WEnum::Value(axis),
2232                value120,
2233            } => {
2234                state.scroll_event_received = true;
2235                let axis = if state.modifiers.shift {
2236                    wl_pointer::Axis::HorizontalScroll
2237                } else {
2238                    axis
2239                };
2240                let axis_modifier = match axis {
2241                    wl_pointer::Axis::VerticalScroll => state.vertical_modifier,
2242                    wl_pointer::Axis::HorizontalScroll => state.horizontal_modifier,
2243                    _ => unreachable!(),
2244                };
2245
2246                let scroll_delta = state.discrete_scroll_delta.get_or_insert(point(0.0, 0.0));
2247                let wheel_percent = value120 as f32 / 120.0;
2248                match axis {
2249                    wl_pointer::Axis::VerticalScroll => {
2250                        scroll_delta.y += wheel_percent * axis_modifier * SCROLL_LINES;
2251                    }
2252                    wl_pointer::Axis::HorizontalScroll => {
2253                        scroll_delta.x += wheel_percent * axis_modifier * SCROLL_LINES;
2254                    }
2255                    _ => unreachable!(),
2256                }
2257            }
2258            wl_pointer::Event::Frame => {
2259                if state.scroll_event_received {
2260                    state.scroll_event_received = false;
2261                    let continuous = state.continuous_scroll_delta.take();
2262                    let discrete = state.discrete_scroll_delta.take();
2263                    if let Some(continuous) = continuous {
2264                        if let Some(window) = state.mouse_focused_window.clone() {
2265                            let input = PlatformInput::ScrollWheel(ScrollWheelEvent {
2266                                position: state.mouse_location.unwrap(),
2267                                delta: ScrollDelta::Pixels(continuous),
2268                                modifiers: state.modifiers,
2269                                touch_phase: TouchPhase::Moved,
2270                            });
2271                            drop(state);
2272                            window.handle_input(input);
2273                        }
2274                    } else if let Some(discrete) = discrete
2275                        && let Some(window) = state.mouse_focused_window.clone()
2276                    {
2277                        let input = PlatformInput::ScrollWheel(ScrollWheelEvent {
2278                            position: state.mouse_location.unwrap(),
2279                            delta: ScrollDelta::Lines(discrete),
2280                            modifiers: state.modifiers,
2281                            touch_phase: TouchPhase::Moved,
2282                        });
2283                        drop(state);
2284                        window.handle_input(input);
2285                    }
2286                }
2287            }
2288            _ => {}
2289        }
2290    }
2291}
2292
2293impl Dispatch<zwp_pointer_gestures_v1::ZwpPointerGesturesV1, ()> for WaylandClientStatePtr {
2294    fn event(
2295        _this: &mut Self,
2296        _: &zwp_pointer_gestures_v1::ZwpPointerGesturesV1,
2297        _: <zwp_pointer_gestures_v1::ZwpPointerGesturesV1 as Proxy>::Event,
2298        _: &(),
2299        _: &Connection,
2300        _: &QueueHandle<Self>,
2301    ) {
2302        // The gesture manager doesn't generate events
2303    }
2304}
2305
2306impl Dispatch<zwp_pointer_gesture_pinch_v1::ZwpPointerGesturePinchV1, ()>
2307    for WaylandClientStatePtr
2308{
2309    fn event(
2310        this: &mut Self,
2311        _: &zwp_pointer_gesture_pinch_v1::ZwpPointerGesturePinchV1,
2312        event: <zwp_pointer_gesture_pinch_v1::ZwpPointerGesturePinchV1 as Proxy>::Event,
2313        _: &(),
2314        _: &Connection,
2315        _: &QueueHandle<Self>,
2316    ) {
2317        use gpui::PinchEvent;
2318
2319        let client = this.get_client();
2320        let mut state = client.borrow_mut();
2321
2322        let Some(window) = state.mouse_focused_window.clone() else {
2323            return;
2324        };
2325
2326        match event {
2327            zwp_pointer_gesture_pinch_v1::Event::Begin {
2328                serial: _,
2329                time: _,
2330                surface: _,
2331                fingers: _,
2332            } => {
2333                state.pinch_scale = 1.0;
2334                let input = PlatformInput::Pinch(PinchEvent {
2335                    position: state.mouse_location.unwrap_or(point(px(0.0), px(0.0))),
2336                    delta: 0.0,
2337                    modifiers: state.modifiers,
2338                    phase: TouchPhase::Started,
2339                });
2340                drop(state);
2341                window.handle_input(input);
2342            }
2343            zwp_pointer_gesture_pinch_v1::Event::Update { time: _, scale, .. } => {
2344                let new_absolute_scale = scale as f32;
2345                let previous_scale = state.pinch_scale;
2346                let zoom_delta = new_absolute_scale - previous_scale;
2347                state.pinch_scale = new_absolute_scale;
2348
2349                let input = PlatformInput::Pinch(PinchEvent {
2350                    position: state.mouse_location.unwrap_or(point(px(0.0), px(0.0))),
2351                    delta: zoom_delta,
2352                    modifiers: state.modifiers,
2353                    phase: TouchPhase::Moved,
2354                });
2355                drop(state);
2356                window.handle_input(input);
2357            }
2358            zwp_pointer_gesture_pinch_v1::Event::End {
2359                serial: _,
2360                time: _,
2361                cancelled: _,
2362            } => {
2363                state.pinch_scale = 1.0;
2364                let input = PlatformInput::Pinch(PinchEvent {
2365                    position: state.mouse_location.unwrap_or(point(px(0.0), px(0.0))),
2366                    delta: 0.0,
2367                    modifiers: state.modifiers,
2368                    phase: TouchPhase::Ended,
2369                });
2370                drop(state);
2371                window.handle_input(input);
2372            }
2373            _ => {}
2374        }
2375    }
2376}
2377
2378impl Dispatch<wp_fractional_scale_v1::WpFractionalScaleV1, ObjectId> for WaylandClientStatePtr {
2379    fn event(
2380        this: &mut Self,
2381        _: &wp_fractional_scale_v1::WpFractionalScaleV1,
2382        event: <wp_fractional_scale_v1::WpFractionalScaleV1 as Proxy>::Event,
2383        surface_id: &ObjectId,
2384        _: &Connection,
2385        _: &QueueHandle<Self>,
2386    ) {
2387        let client = this.get_client();
2388        let mut state = client.borrow_mut();
2389
2390        let Some(window) = get_window(&mut state, surface_id) else {
2391            return;
2392        };
2393
2394        drop(state);
2395        window.handle_fractional_scale_event(event);
2396    }
2397}
2398
2399impl Dispatch<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1, ObjectId>
2400    for WaylandClientStatePtr
2401{
2402    fn event(
2403        this: &mut Self,
2404        _: &zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1,
2405        event: zxdg_toplevel_decoration_v1::Event,
2406        surface_id: &ObjectId,
2407        _: &Connection,
2408        _: &QueueHandle<Self>,
2409    ) {
2410        let client = this.get_client();
2411        let mut state = client.borrow_mut();
2412        let Some(window) = get_window(&mut state, surface_id) else {
2413            return;
2414        };
2415
2416        drop(state);
2417        window.handle_toplevel_decoration_event(event);
2418    }
2419}
2420
2421impl Dispatch<wl_data_device::WlDataDevice, ()> for WaylandClientStatePtr {
2422    fn event(
2423        this: &mut Self,
2424        _: &wl_data_device::WlDataDevice,
2425        event: wl_data_device::Event,
2426        _: &(),
2427        _: &Connection,
2428        _: &QueueHandle<Self>,
2429    ) {
2430        let client = this.get_client();
2431        let mut state = client.borrow_mut();
2432
2433        match event {
2434            // Clipboard
2435            wl_data_device::Event::DataOffer { id: data_offer } => {
2436                state.data_offers.push(DataOffer::new(data_offer));
2437                if state.data_offers.len() > 2 {
2438                    // At most we store a clipboard offer and a drag and drop offer.
2439                    state.data_offers.remove(0).inner.destroy();
2440                }
2441            }
2442            wl_data_device::Event::Selection { id: data_offer } => {
2443                if let Some(offer) = data_offer {
2444                    let offer = state
2445                        .data_offers
2446                        .iter()
2447                        .find(|wrapper| wrapper.inner.id() == offer.id());
2448                    let offer = offer.cloned();
2449                    state.clipboard.set_offer(offer);
2450                } else {
2451                    state.clipboard.set_offer(None);
2452                }
2453            }
2454
2455            // Drag and drop
2456            wl_data_device::Event::Enter {
2457                serial,
2458                surface,
2459                x,
2460                y,
2461                id: data_offer,
2462            } => {
2463                state.serial_tracker.update(SerialKind::DataDevice, serial);
2464                if let Some(data_offer) = data_offer {
2465                    let Some(drag_window) = get_window(&mut state, &surface.id()) else {
2466                        return;
2467                    };
2468
2469                    const ACTIONS: DndAction = DndAction::Copy;
2470                    data_offer.set_actions(ACTIONS, ACTIONS);
2471
2472                    let pipe = Pipe::new().unwrap();
2473                    data_offer.receive(FILE_LIST_MIME_TYPE.to_string(), unsafe {
2474                        BorrowedFd::borrow_raw(pipe.write.as_raw_fd())
2475                    });
2476                    let fd = pipe.read;
2477                    drop(pipe.write);
2478
2479                    let read_task = state.common.background_executor.spawn(async {
2480                        let buffer = read_fd_with_timeout(fd, PIPE_READ_TIMEOUT)?;
2481                        let text = String::from_utf8(buffer)?;
2482                        anyhow::Ok(text)
2483                    });
2484
2485                    let this = this.clone();
2486                    state
2487                        .common
2488                        .foreground_executor
2489                        .spawn(async move {
2490                            let file_list = match read_task.await {
2491                                Ok(list) => list,
2492                                Err(err) => {
2493                                    log::error!("error reading drag and drop pipe: {err:?}");
2494                                    return;
2495                                }
2496                            };
2497
2498                            let paths: SmallVec<[_; 2]> = file_list
2499                                .lines()
2500                                .filter_map(|path| Url::parse(path).log_err())
2501                                .filter_map(|url| match url.to_file_path() {
2502                                    Ok(url) => Some(url),
2503                                    Err(()) => {
2504                                        log::error!("Failed turn {url:?} into a file path");
2505                                        None
2506                                    }
2507                                })
2508                                .collect();
2509                            let position = Point::new(x.into(), y.into());
2510
2511                            // Prevent dropping text from other programs.
2512                            if paths.is_empty() {
2513                                data_offer.destroy();
2514                                return;
2515                            }
2516
2517                            let input = PlatformInput::FileDrop(FileDropEvent::Entered {
2518                                position,
2519                                paths: gpui::ExternalPaths(paths),
2520                            });
2521
2522                            let client = this.get_client();
2523                            let mut state = client.borrow_mut();
2524                            state.drag.data_offer = Some(data_offer);
2525                            state.drag.window = Some(drag_window.clone());
2526                            state.drag.position = position;
2527
2528                            drop(state);
2529                            drag_window.handle_input(input);
2530                        })
2531                        .detach();
2532                }
2533            }
2534            wl_data_device::Event::Motion { x, y, .. } => {
2535                let Some(drag_window) = state.drag.window.clone() else {
2536                    return;
2537                };
2538                let position = Point::new(x.into(), y.into());
2539                state.drag.position = position;
2540
2541                let input = PlatformInput::FileDrop(FileDropEvent::Pending { position });
2542                drop(state);
2543                drag_window.handle_input(input);
2544            }
2545            wl_data_device::Event::Leave => {
2546                let Some(drag_window) = state.drag.window.clone() else {
2547                    return;
2548                };
2549                let data_offer = state.drag.data_offer.clone().unwrap();
2550                data_offer.destroy();
2551
2552                state.drag.data_offer = None;
2553                state.drag.window = None;
2554
2555                let input = PlatformInput::FileDrop(FileDropEvent::Exited {});
2556                drop(state);
2557                drag_window.handle_input(input);
2558            }
2559            wl_data_device::Event::Drop => {
2560                let Some(drag_window) = state.drag.window.clone() else {
2561                    return;
2562                };
2563                let data_offer = state.drag.data_offer.clone().unwrap();
2564                data_offer.finish();
2565                data_offer.destroy();
2566
2567                state.drag.data_offer = None;
2568                state.drag.window = None;
2569
2570                let input = PlatformInput::FileDrop(FileDropEvent::Submit {
2571                    position: state.drag.position,
2572                });
2573                drop(state);
2574                drag_window.handle_input(input);
2575            }
2576            _ => {}
2577        }
2578    }
2579
2580    event_created_child!(WaylandClientStatePtr, wl_data_device::WlDataDevice, [
2581        wl_data_device::EVT_DATA_OFFER_OPCODE => (wl_data_offer::WlDataOffer, ()),
2582    ]);
2583}
2584
2585impl Dispatch<wl_data_offer::WlDataOffer, ()> for WaylandClientStatePtr {
2586    fn event(
2587        this: &mut Self,
2588        data_offer: &wl_data_offer::WlDataOffer,
2589        event: wl_data_offer::Event,
2590        _: &(),
2591        _: &Connection,
2592        _: &QueueHandle<Self>,
2593    ) {
2594        let client = this.get_client();
2595        let mut state = client.borrow_mut();
2596
2597        if let wl_data_offer::Event::Offer { mime_type } = event {
2598            // Drag and drop
2599            if mime_type == FILE_LIST_MIME_TYPE {
2600                let serial = state.serial_tracker.get(SerialKind::DataDevice);
2601                let mime_type = mime_type.clone();
2602                data_offer.accept(serial.as_raw(), Some(mime_type));
2603            }
2604
2605            // Clipboard
2606            if let Some(offer) = state
2607                .data_offers
2608                .iter_mut()
2609                .find(|wrapper| wrapper.inner.id() == data_offer.id())
2610            {
2611                offer.add_mime_type(mime_type);
2612            }
2613        }
2614    }
2615}
2616
2617impl Dispatch<wl_data_source::WlDataSource, ()> for WaylandClientStatePtr {
2618    fn event(
2619        this: &mut Self,
2620        data_source: &wl_data_source::WlDataSource,
2621        event: wl_data_source::Event,
2622        _: &(),
2623        _: &Connection,
2624        _: &QueueHandle<Self>,
2625    ) {
2626        let client = this.get_client();
2627        let state = client.borrow_mut();
2628
2629        match event {
2630            wl_data_source::Event::Send { mime_type, fd } => {
2631                state.clipboard.send(mime_type, fd);
2632            }
2633            wl_data_source::Event::Cancelled => {
2634                data_source.destroy();
2635            }
2636            _ => {}
2637        }
2638    }
2639}
2640
2641impl Dispatch<zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1, ()>
2642    for WaylandClientStatePtr
2643{
2644    fn event(
2645        this: &mut Self,
2646        _: &zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1,
2647        event: zwp_primary_selection_device_v1::Event,
2648        _: &(),
2649        _: &Connection,
2650        _: &QueueHandle<Self>,
2651    ) {
2652        let client = this.get_client();
2653        let mut state = client.borrow_mut();
2654
2655        match event {
2656            zwp_primary_selection_device_v1::Event::DataOffer { offer } => {
2657                let old_offer = state.primary_data_offer.replace(DataOffer::new(offer));
2658                if let Some(old_offer) = old_offer {
2659                    old_offer.inner.destroy();
2660                }
2661            }
2662            zwp_primary_selection_device_v1::Event::Selection { id: data_offer } => {
2663                if data_offer.is_some() {
2664                    let offer = state.primary_data_offer.clone();
2665                    state.clipboard.set_primary_offer(offer);
2666                } else {
2667                    state.clipboard.set_primary_offer(None);
2668                }
2669            }
2670            _ => {}
2671        }
2672    }
2673
2674    event_created_child!(WaylandClientStatePtr, zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1, [
2675        zwp_primary_selection_device_v1::EVT_DATA_OFFER_OPCODE => (zwp_primary_selection_offer_v1::ZwpPrimarySelectionOfferV1, ()),
2676    ]);
2677}
2678
2679impl Dispatch<zwp_primary_selection_offer_v1::ZwpPrimarySelectionOfferV1, ()>
2680    for WaylandClientStatePtr
2681{
2682    fn event(
2683        this: &mut Self,
2684        _data_offer: &zwp_primary_selection_offer_v1::ZwpPrimarySelectionOfferV1,
2685        event: zwp_primary_selection_offer_v1::Event,
2686        _: &(),
2687        _: &Connection,
2688        _: &QueueHandle<Self>,
2689    ) {
2690        let client = this.get_client();
2691        let mut state = client.borrow_mut();
2692
2693        if let zwp_primary_selection_offer_v1::Event::Offer { mime_type } = event
2694            && let Some(offer) = state.primary_data_offer.as_mut()
2695        {
2696            offer.add_mime_type(mime_type);
2697        }
2698    }
2699}
2700
2701impl Dispatch<zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1, ()>
2702    for WaylandClientStatePtr
2703{
2704    fn event(
2705        this: &mut Self,
2706        selection_source: &zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1,
2707        event: zwp_primary_selection_source_v1::Event,
2708        _: &(),
2709        _: &Connection,
2710        _: &QueueHandle<Self>,
2711    ) {
2712        let client = this.get_client();
2713        let state = client.borrow_mut();
2714
2715        match event {
2716            zwp_primary_selection_source_v1::Event::Send { mime_type, fd } => {
2717                state.clipboard.send_primary(mime_type, fd);
2718            }
2719            zwp_primary_selection_source_v1::Event::Cancelled => {
2720                selection_source.destroy();
2721            }
2722            _ => {}
2723        }
2724    }
2725}
2726
2727impl Dispatch<XdgWmDialogV1, ()> for WaylandClientStatePtr {
2728    fn event(
2729        _: &mut Self,
2730        _: &XdgWmDialogV1,
2731        _: <XdgWmDialogV1 as Proxy>::Event,
2732        _: &(),
2733        _: &Connection,
2734        _: &QueueHandle<Self>,
2735    ) {
2736    }
2737}
2738
2739impl Dispatch<XdgDialogV1, ()> for WaylandClientStatePtr {
2740    fn event(
2741        _state: &mut Self,
2742        _proxy: &XdgDialogV1,
2743        _event: <XdgDialogV1 as Proxy>::Event,
2744        _data: &(),
2745        _conn: &Connection,
2746        _qhandle: &QueueHandle<Self>,
2747    ) {
2748    }
2749}
2750
2751#[cfg(test)]
2752mod tests {
2753    use std::cell::Cell;
2754
2755    use super::*;
2756
2757    #[derive(Default)]
2758    struct FakeImeCursorRectangleSink {
2759        cursor_rectangles: RefCell<Vec<(i32, i32, i32, i32)>>,
2760        commit_count: Cell<usize>,
2761    }
2762
2763    impl ImeCursorRectangleSink for FakeImeCursorRectangleSink {
2764        fn set_ime_cursor_rectangle(&self, x: i32, y: i32, width: i32, height: i32) {
2765            self.cursor_rectangles
2766                .borrow_mut()
2767                .push((x, y, width, height));
2768        }
2769
2770        fn commit_ime_state(&self) {
2771            self.commit_count.set(self.commit_count.get() + 1);
2772        }
2773    }
2774
2775    fn ime_cursor_bounds(x: f32) -> Bounds<Pixels> {
2776        Bounds::new(point(px(x), px(20.25)), size(px(1.0), px(18.75)))
2777    }
2778
2779    #[test]
2780    fn caches_cursor_rectangle_committed_after_done() {
2781        let text_input = FakeImeCursorRectangleSink::default();
2782        let mut last_ime_cursor_rectangle = None;
2783        let initial_bounds = ime_cursor_bounds(10.0);
2784        let updated_bounds = ime_cursor_bounds(20.0);
2785
2786        update_ime_cursor_rectangle(&text_input, &mut last_ime_cursor_rectangle, initial_bounds);
2787        set_ime_cursor_rectangle_after_done(
2788            &text_input,
2789            &mut last_ime_cursor_rectangle,
2790            updated_bounds,
2791            true,
2792        );
2793        update_ime_cursor_rectangle(&text_input, &mut last_ime_cursor_rectangle, updated_bounds);
2794
2795        assert_eq!(text_input.commit_count.get(), 2);
2796        assert_eq!(text_input.cursor_rectangles.borrow().len(), 2);
2797    }
2798
2799    #[test]
2800    fn skips_unchanged_cursor_rectangle_after_done() {
2801        let text_input = FakeImeCursorRectangleSink::default();
2802        let mut last_ime_cursor_rectangle = None;
2803        let bounds = ime_cursor_bounds(10.0);
2804
2805        update_ime_cursor_rectangle(&text_input, &mut last_ime_cursor_rectangle, bounds);
2806        set_ime_cursor_rectangle_after_done(
2807            &text_input,
2808            &mut last_ime_cursor_rectangle,
2809            bounds,
2810            true,
2811        );
2812
2813        assert_eq!(text_input.commit_count.get(), 1);
2814        assert_eq!(text_input.cursor_rectangles.borrow().len(), 1);
2815    }
2816
2817    #[test]
2818    fn skips_cursor_rectangles_with_unchanged_protocol_coordinates() {
2819        let text_input = FakeImeCursorRectangleSink::default();
2820        let mut last_ime_cursor_rectangle = None;
2821
2822        update_ime_cursor_rectangle(
2823            &text_input,
2824            &mut last_ime_cursor_rectangle,
2825            ime_cursor_bounds(10.25),
2826        );
2827        update_ime_cursor_rectangle(
2828            &text_input,
2829            &mut last_ime_cursor_rectangle,
2830            ime_cursor_bounds(10.75),
2831        );
2832
2833        assert_eq!(text_input.commit_count.get(), 1);
2834        assert_eq!(
2835            text_input.cursor_rectangles.borrow().as_slice(),
2836            &[(10, 20, 1, 18)]
2837        );
2838    }
2839}
2840
Served at tenant.openagents/omega Member data and write actions are omitted.