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