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