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