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