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