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