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