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