1use std::cell::{RefCell, RefMut};
2use std::rc::{Rc, Weak};
3use std::time::{Duration, Instant};
4
5use async_task::Runnable;
6use calloop::timer::{TimeoutAction, Timer};
7use calloop::{EventLoop, LoopHandle};
8use calloop_wayland_source::WaylandSource;
9use collections::HashMap;
10use copypasta::wayland_clipboard::{create_clipboards_from_external, Clipboard, Primary};
11use copypasta::ClipboardProvider;
12use util::ResultExt;
13use wayland_backend::client::ObjectId;
14use wayland_backend::protocol::WEnum;
15use wayland_client::globals::{registry_queue_init, GlobalList, GlobalListContents};
16use wayland_client::protocol::wl_callback::{self, WlCallback};
17use wayland_client::protocol::wl_output;
18use wayland_client::protocol::wl_pointer::{AxisRelativeDirection, AxisSource};
19use wayland_client::{
20 delegate_noop,
21 protocol::{
22 wl_buffer, wl_compositor, wl_keyboard, wl_pointer, wl_registry, wl_seat, wl_shm,
23 wl_shm_pool, wl_surface,
24 },
25 Connection, Dispatch, Proxy, QueueHandle,
26};
27use wayland_protocols::wp::fractional_scale::v1::client::{
28 wp_fractional_scale_manager_v1, wp_fractional_scale_v1,
29};
30use wayland_protocols::wp::viewporter::client::{wp_viewport, wp_viewporter};
31use wayland_protocols::xdg::decoration::zv1::client::{
32 zxdg_decoration_manager_v1, zxdg_toplevel_decoration_v1,
33};
34use wayland_protocols::xdg::shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base};
35use xkbcommon::xkb::ffi::XKB_KEYMAP_FORMAT_TEXT_V1;
36use xkbcommon::xkb::{self, Keycode, KEYMAP_COMPILE_NO_FLAGS};
37
38use super::super::DOUBLE_CLICK_INTERVAL;
39use super::window::{WaylandWindowState, WaylandWindowStatePtr};
40use crate::platform::linux::is_within_click_distance;
41use crate::platform::linux::wayland::cursor::Cursor;
42use crate::platform::linux::wayland::window::WaylandWindow;
43use crate::platform::linux::LinuxClient;
44use crate::platform::PlatformWindow;
45use crate::{point, px, ForegroundExecutor, MouseExitEvent};
46use crate::{
47 AnyWindowHandle, CursorStyle, DisplayId, KeyDownEvent, KeyUpEvent, Keystroke, Modifiers,
48 ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
49 NavigationDirection, Pixels, PlatformDisplay, PlatformInput, Point, ScrollDelta,
50 ScrollWheelEvent, TouchPhase,
51};
52use crate::{LinuxCommon, WindowParams};
53
54/// Used to convert evdev scancode to xkb scancode
55const MIN_KEYCODE: u32 = 8;
56
57#[derive(Clone)]
58pub struct Globals {
59 pub qh: QueueHandle<WaylandClientStatePtr>,
60 pub compositor: wl_compositor::WlCompositor,
61 pub wm_base: xdg_wm_base::XdgWmBase,
62 pub shm: wl_shm::WlShm,
63 pub viewporter: Option<wp_viewporter::WpViewporter>,
64 pub fractional_scale_manager:
65 Option<wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1>,
66 pub decoration_manager: Option<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1>,
67 pub executor: ForegroundExecutor,
68}
69
70impl Globals {
71 fn new(
72 globals: GlobalList,
73 executor: ForegroundExecutor,
74 qh: QueueHandle<WaylandClientStatePtr>,
75 ) -> Self {
76 Globals {
77 compositor: globals
78 .bind(
79 &qh,
80 wl_surface::REQ_SET_BUFFER_SCALE_SINCE
81 ..=wl_surface::EVT_PREFERRED_BUFFER_SCALE_SINCE,
82 (),
83 )
84 .unwrap(),
85 shm: globals.bind(&qh, 1..=1, ()).unwrap(),
86 wm_base: globals.bind(&qh, 1..=1, ()).unwrap(),
87 viewporter: globals.bind(&qh, 1..=1, ()).ok(),
88 fractional_scale_manager: globals.bind(&qh, 1..=1, ()).ok(),
89 decoration_manager: globals.bind(&qh, 1..=1, ()).ok(),
90 executor,
91 qh,
92 }
93 }
94}
95
96pub(crate) struct WaylandClientState {
97 globals: Globals,
98 // Surface to Window mapping
99 windows: HashMap<ObjectId, WaylandWindowStatePtr>,
100 // Output to scale mapping
101 output_scales: HashMap<ObjectId, i32>,
102 keymap_state: Option<xkb::State>,
103 click: ClickState,
104 repeat: KeyRepeat,
105 modifiers: Modifiers,
106 scroll_direction: f64,
107 axis_source: AxisSource,
108 mouse_location: Option<Point<Pixels>>,
109 enter_token: Option<()>,
110 button_pressed: Option<MouseButton>,
111 mouse_focused_window: Option<WaylandWindowStatePtr>,
112 keyboard_focused_window: Option<WaylandWindowStatePtr>,
113 loop_handle: LoopHandle<'static, WaylandClientStatePtr>,
114 cursor_icon_name: String,
115 cursor: Cursor,
116 clipboard: Clipboard,
117 primary: Primary,
118 event_loop: Option<EventLoop<'static, WaylandClientStatePtr>>,
119 common: LinuxCommon,
120}
121
122pub struct ClickState {
123 last_click: Instant,
124 last_location: Point<Pixels>,
125 current_count: usize,
126}
127
128pub(crate) struct KeyRepeat {
129 characters_per_second: u32,
130 delay: Duration,
131 current_id: u64,
132 current_keysym: Option<xkb::Keysym>,
133}
134
135/// This struct is required to conform to Rust's orphan rules, so we can dispatch on the state but hand the
136/// window to GPUI.
137#[derive(Clone)]
138pub struct WaylandClientStatePtr(Weak<RefCell<WaylandClientState>>);
139
140impl WaylandClientStatePtr {
141 fn get_client(&self) -> Rc<RefCell<WaylandClientState>> {
142 self.0
143 .upgrade()
144 .expect("The pointer should always be valid when dispatching in wayland")
145 }
146
147 pub fn drop_window(&self, surface_id: &ObjectId) {
148 let mut client = self.get_client();
149 let mut state = client.borrow_mut();
150 let closed_window = state.windows.remove(surface_id).unwrap();
151 if let Some(window) = state.mouse_focused_window.take() {
152 if !window.ptr_eq(&closed_window) {
153 state.mouse_focused_window = Some(window);
154 }
155 }
156 if let Some(window) = state.keyboard_focused_window.take() {
157 if !window.ptr_eq(&closed_window) {
158 state.mouse_focused_window = Some(window);
159 }
160 }
161 }
162}
163
164#[derive(Clone)]
165pub struct WaylandClient(Rc<RefCell<WaylandClientState>>);
166
167const WL_SEAT_MIN_VERSION: u32 = 4;
168const WL_OUTPUT_VERSION: u32 = 2;
169
170fn wl_seat_version(version: u32) -> u32 {
171 if version >= wl_pointer::EVT_AXIS_VALUE120_SINCE {
172 wl_pointer::EVT_AXIS_VALUE120_SINCE
173 } else if version >= WL_SEAT_MIN_VERSION {
174 WL_SEAT_MIN_VERSION
175 } else {
176 panic!(
177 "wl_seat below required version: {} < {}",
178 version, WL_SEAT_MIN_VERSION
179 );
180 }
181}
182
183impl WaylandClient {
184 pub(crate) fn new() -> Self {
185 let conn = Connection::connect_to_env().unwrap();
186
187 let (globals, mut event_queue) =
188 registry_queue_init::<WaylandClientStatePtr>(&conn).unwrap();
189 let qh = event_queue.handle();
190 let mut outputs = HashMap::default();
191
192 globals.contents().with_list(|list| {
193 for global in list {
194 match &global.interface[..] {
195 "wl_seat" => {
196 globals.registry().bind::<wl_seat::WlSeat, _, _>(
197 global.name,
198 wl_seat_version(global.version),
199 &qh,
200 (),
201 );
202 }
203 "wl_output" => {
204 let output = globals.registry().bind::<wl_output::WlOutput, _, _>(
205 global.name,
206 WL_OUTPUT_VERSION,
207 &qh,
208 (),
209 );
210 outputs.insert(output.id(), 1);
211 }
212 _ => {}
213 }
214 }
215 });
216
217 let display = conn.backend().display_ptr() as *mut std::ffi::c_void;
218 let (primary, clipboard) = unsafe { create_clipboards_from_external(display) };
219
220 let event_loop = EventLoop::<WaylandClientStatePtr>::try_new().unwrap();
221
222 let (common, main_receiver) = LinuxCommon::new(event_loop.get_signal());
223
224 let handle = event_loop.handle();
225
226 handle.insert_source(main_receiver, |event, _, _: &mut WaylandClientStatePtr| {
227 if let calloop::channel::Event::Msg(runnable) = event {
228 runnable.run();
229 }
230 });
231
232 let globals = Globals::new(globals, common.foreground_executor.clone(), qh);
233
234 let cursor = Cursor::new(&conn, &globals, 24);
235
236 let mut state = Rc::new(RefCell::new(WaylandClientState {
237 globals,
238 output_scales: outputs,
239 windows: HashMap::default(),
240 common,
241 keymap_state: None,
242 click: ClickState {
243 last_click: Instant::now(),
244 last_location: Point::new(px(0.0), px(0.0)),
245 current_count: 0,
246 },
247 repeat: KeyRepeat {
248 characters_per_second: 16,
249 delay: Duration::from_millis(500),
250 current_id: 0,
251 current_keysym: None,
252 },
253 modifiers: Modifiers {
254 shift: false,
255 control: false,
256 alt: false,
257 function: false,
258 platform: false,
259 },
260 scroll_direction: -1.0,
261 axis_source: AxisSource::Wheel,
262 mouse_location: None,
263 button_pressed: None,
264 mouse_focused_window: None,
265 keyboard_focused_window: None,
266 loop_handle: handle.clone(),
267 cursor_icon_name: "arrow".to_string(),
268 enter_token: None,
269 cursor,
270 clipboard,
271 primary,
272 event_loop: Some(event_loop),
273 }));
274
275 WaylandSource::new(conn, event_queue).insert(handle);
276
277 Self(state)
278 }
279}
280
281impl LinuxClient for WaylandClient {
282 fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
283 Vec::new()
284 }
285
286 fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
287 unimplemented!()
288 }
289
290 fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>> {
291 None
292 }
293
294 fn open_window(
295 &self,
296 handle: AnyWindowHandle,
297 params: WindowParams,
298 ) -> Box<dyn PlatformWindow> {
299 let mut state = self.0.borrow_mut();
300
301 let (window, surface_id) = WaylandWindow::new(
302 state.globals.clone(),
303 WaylandClientStatePtr(Rc::downgrade(&self.0)),
304 params,
305 );
306 state.windows.insert(surface_id, window.0.clone());
307
308 Box::new(window)
309 }
310
311 fn set_cursor_style(&self, style: CursorStyle) {
312 // Based on cursor names from https://gitlab.gnome.org/GNOME/adwaita-icon-theme (GNOME)
313 // and https://github.com/KDE/breeze (KDE). Both of them seem to be also derived from
314 // Web CSS cursor names: https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#values
315 let cursor_icon_name = match style {
316 CursorStyle::Arrow => "arrow",
317 CursorStyle::IBeam => "text",
318 CursorStyle::Crosshair => "crosshair",
319 CursorStyle::ClosedHand => "grabbing",
320 CursorStyle::OpenHand => "grab",
321 CursorStyle::PointingHand => "pointer",
322 CursorStyle::ResizeLeft => "w-resize",
323 CursorStyle::ResizeRight => "e-resize",
324 CursorStyle::ResizeLeftRight => "ew-resize",
325 CursorStyle::ResizeUp => "n-resize",
326 CursorStyle::ResizeDown => "s-resize",
327 CursorStyle::ResizeUpDown => "ns-resize",
328 CursorStyle::DisappearingItem => "grabbing", // todo(linux) - couldn't find equivalent icon in linux
329 CursorStyle::IBeamCursorForVerticalLayout => "vertical-text",
330 CursorStyle::OperationNotAllowed => "not-allowed",
331 CursorStyle::DragLink => "alias",
332 CursorStyle::DragCopy => "copy",
333 CursorStyle::ContextualMenu => "context-menu",
334 }
335 .to_string();
336
337 self.0.borrow_mut().cursor_icon_name = cursor_icon_name;
338 }
339
340 fn with_common<R>(&self, f: impl FnOnce(&mut LinuxCommon) -> R) -> R {
341 f(&mut self.0.borrow_mut().common)
342 }
343
344 fn run(&self) {
345 let mut event_loop = self
346 .0
347 .borrow_mut()
348 .event_loop
349 .take()
350 .expect("App is already running");
351
352 event_loop
353 .run(
354 None,
355 &mut WaylandClientStatePtr(Rc::downgrade(&self.0)),
356 |_| {},
357 )
358 .log_err();
359 }
360
361 fn write_to_clipboard(&self, item: crate::ClipboardItem) {
362 self.0.borrow_mut().clipboard.set_contents(item.text);
363 }
364
365 fn read_from_clipboard(&self) -> Option<crate::ClipboardItem> {
366 self.0
367 .borrow_mut()
368 .clipboard
369 .get_contents()
370 .ok()
371 .map(|s| crate::ClipboardItem {
372 text: s,
373 metadata: None,
374 })
375 }
376}
377
378impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for WaylandClientStatePtr {
379 fn event(
380 this: &mut Self,
381 registry: &wl_registry::WlRegistry,
382 event: wl_registry::Event,
383 _: &GlobalListContents,
384 _: &Connection,
385 qh: &QueueHandle<Self>,
386 ) {
387 let mut client = this.get_client();
388 let mut state = client.borrow_mut();
389
390 match event {
391 wl_registry::Event::Global {
392 name,
393 interface,
394 version,
395 } => match &interface[..] {
396 "wl_seat" => {
397 registry.bind::<wl_seat::WlSeat, _, _>(name, wl_seat_version(version), qh, ());
398 }
399 "wl_output" => {
400 let output =
401 registry.bind::<wl_output::WlOutput, _, _>(name, WL_OUTPUT_VERSION, qh, ());
402
403 state.output_scales.insert(output.id(), 1);
404 }
405 _ => {}
406 },
407 wl_registry::Event::GlobalRemove { name: _ } => {}
408 _ => {}
409 }
410 }
411}
412
413delegate_noop!(WaylandClientStatePtr: ignore wl_compositor::WlCompositor);
414delegate_noop!(WaylandClientStatePtr: ignore wl_shm::WlShm);
415delegate_noop!(WaylandClientStatePtr: ignore wl_shm_pool::WlShmPool);
416delegate_noop!(WaylandClientStatePtr: ignore wl_buffer::WlBuffer);
417delegate_noop!(WaylandClientStatePtr: ignore wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1);
418delegate_noop!(WaylandClientStatePtr: ignore zxdg_decoration_manager_v1::ZxdgDecorationManagerV1);
419delegate_noop!(WaylandClientStatePtr: ignore wp_viewporter::WpViewporter);
420delegate_noop!(WaylandClientStatePtr: ignore wp_viewport::WpViewport);
421
422impl Dispatch<WlCallback, ObjectId> for WaylandClientStatePtr {
423 fn event(
424 state: &mut WaylandClientStatePtr,
425 _: &wl_callback::WlCallback,
426 event: wl_callback::Event,
427 surface_id: &ObjectId,
428 _: &Connection,
429 qh: &QueueHandle<Self>,
430 ) {
431 let client = state.get_client();
432 let mut state = client.borrow_mut();
433 let Some(window) = get_window(&mut state, surface_id) else {
434 return;
435 };
436 drop(state);
437
438 match event {
439 wl_callback::Event::Done { callback_data } => {
440 window.frame(true);
441 }
442 _ => {}
443 }
444 }
445}
446
447fn get_window(
448 mut state: &mut RefMut<WaylandClientState>,
449 surface_id: &ObjectId,
450) -> Option<WaylandWindowStatePtr> {
451 state.windows.get(surface_id).cloned()
452}
453
454impl Dispatch<wl_surface::WlSurface, ()> for WaylandClientStatePtr {
455 fn event(
456 this: &mut Self,
457 surface: &wl_surface::WlSurface,
458 event: <wl_surface::WlSurface as Proxy>::Event,
459 _: &(),
460 _: &Connection,
461 _: &QueueHandle<Self>,
462 ) {
463 let mut client = this.get_client();
464 let mut state = client.borrow_mut();
465
466 let Some(window) = get_window(&mut state, &surface.id()) else {
467 return;
468 };
469 let scales = state.output_scales.clone();
470 drop(state);
471
472 window.handle_surface_event(event, scales);
473 }
474}
475
476impl Dispatch<wl_output::WlOutput, ()> for WaylandClientStatePtr {
477 fn event(
478 this: &mut Self,
479 output: &wl_output::WlOutput,
480 event: <wl_output::WlOutput as Proxy>::Event,
481 _: &(),
482 _: &Connection,
483 _: &QueueHandle<Self>,
484 ) {
485 let mut client = this.get_client();
486 let mut state = client.borrow_mut();
487
488 let Some(mut output_scale) = state.output_scales.get_mut(&output.id()) else {
489 return;
490 };
491
492 match event {
493 wl_output::Event::Scale { factor } => {
494 *output_scale = factor;
495 }
496 _ => {}
497 }
498 }
499}
500
501impl Dispatch<xdg_surface::XdgSurface, ObjectId> for WaylandClientStatePtr {
502 fn event(
503 state: &mut Self,
504 xdg_surface: &xdg_surface::XdgSurface,
505 event: xdg_surface::Event,
506 surface_id: &ObjectId,
507 _: &Connection,
508 _: &QueueHandle<Self>,
509 ) {
510 let client = state.get_client();
511 let mut state = client.borrow_mut();
512 let Some(window) = get_window(&mut state, surface_id) else {
513 return;
514 };
515 drop(state);
516 window.handle_xdg_surface_event(event);
517 }
518}
519
520impl Dispatch<xdg_toplevel::XdgToplevel, ObjectId> for WaylandClientStatePtr {
521 fn event(
522 this: &mut Self,
523 xdg_toplevel: &xdg_toplevel::XdgToplevel,
524 event: <xdg_toplevel::XdgToplevel as Proxy>::Event,
525 surface_id: &ObjectId,
526 _: &Connection,
527 _: &QueueHandle<Self>,
528 ) {
529 let client = this.get_client();
530 let mut state = client.borrow_mut();
531 let Some(window) = get_window(&mut state, surface_id) else {
532 return;
533 };
534
535 drop(state);
536 let should_close = window.handle_toplevel_event(event);
537
538 if should_close {
539 this.drop_window(surface_id);
540 }
541 }
542}
543
544impl Dispatch<xdg_wm_base::XdgWmBase, ()> for WaylandClientStatePtr {
545 fn event(
546 _: &mut Self,
547 wm_base: &xdg_wm_base::XdgWmBase,
548 event: <xdg_wm_base::XdgWmBase as Proxy>::Event,
549 _: &(),
550 _: &Connection,
551 _: &QueueHandle<Self>,
552 ) {
553 if let xdg_wm_base::Event::Ping { serial } = event {
554 wm_base.pong(serial);
555 }
556 }
557}
558
559impl Dispatch<wl_seat::WlSeat, ()> for WaylandClientStatePtr {
560 fn event(
561 state: &mut Self,
562 seat: &wl_seat::WlSeat,
563 event: wl_seat::Event,
564 data: &(),
565 conn: &Connection,
566 qh: &QueueHandle<Self>,
567 ) {
568 if let wl_seat::Event::Capabilities {
569 capabilities: WEnum::Value(capabilities),
570 } = event
571 {
572 if capabilities.contains(wl_seat::Capability::Keyboard) {
573 seat.get_keyboard(qh, ());
574 }
575 if capabilities.contains(wl_seat::Capability::Pointer) {
576 seat.get_pointer(qh, ());
577 }
578 }
579 }
580}
581
582impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClientStatePtr {
583 fn event(
584 this: &mut Self,
585 keyboard: &wl_keyboard::WlKeyboard,
586 event: wl_keyboard::Event,
587 data: &(),
588 conn: &Connection,
589 qh: &QueueHandle<Self>,
590 ) {
591 let mut client = this.get_client();
592 let mut state = client.borrow_mut();
593 match event {
594 wl_keyboard::Event::RepeatInfo { rate, delay } => {
595 state.repeat.characters_per_second = rate as u32;
596 state.repeat.delay = Duration::from_millis(delay as u64);
597 }
598 wl_keyboard::Event::Keymap {
599 format: WEnum::Value(format),
600 fd,
601 size,
602 ..
603 } => {
604 assert_eq!(
605 format,
606 wl_keyboard::KeymapFormat::XkbV1,
607 "Unsupported keymap format"
608 );
609 let keymap = unsafe {
610 xkb::Keymap::new_from_fd(
611 &xkb::Context::new(xkb::CONTEXT_NO_FLAGS),
612 fd,
613 size as usize,
614 XKB_KEYMAP_FORMAT_TEXT_V1,
615 KEYMAP_COMPILE_NO_FLAGS,
616 )
617 .log_err()
618 .flatten()
619 .expect("Failed to create keymap")
620 };
621 state.keymap_state = Some(xkb::State::new(&keymap));
622 }
623 wl_keyboard::Event::Enter { surface, .. } => {
624 state.keyboard_focused_window = get_window(&mut state, &surface.id());
625
626 if let Some(window) = state.keyboard_focused_window.clone() {
627 drop(state);
628 window.set_focused(true);
629 }
630 }
631 wl_keyboard::Event::Leave { surface, .. } => {
632 let keyboard_focused_window = get_window(&mut state, &surface.id());
633 state.keyboard_focused_window = None;
634
635 if let Some(window) = keyboard_focused_window {
636 drop(state);
637 window.set_focused(false);
638 }
639 }
640 wl_keyboard::Event::Modifiers {
641 mods_depressed,
642 mods_latched,
643 mods_locked,
644 group,
645 ..
646 } => {
647 let focused_window = state.keyboard_focused_window.clone();
648 let Some(focused_window) = focused_window else {
649 return;
650 };
651
652 let keymap_state = state.keymap_state.as_mut().unwrap();
653 keymap_state.update_mask(mods_depressed, mods_latched, mods_locked, 0, 0, group);
654 state.modifiers = Modifiers::from_xkb(keymap_state);
655
656 let input = PlatformInput::ModifiersChanged(ModifiersChangedEvent {
657 modifiers: state.modifiers,
658 });
659
660 drop(state);
661 focused_window.handle_input(input);
662 }
663 wl_keyboard::Event::Key {
664 key,
665 state: WEnum::Value(key_state),
666 ..
667 } => {
668 let focused_window = state.keyboard_focused_window.clone();
669 let Some(focused_window) = focused_window else {
670 return;
671 };
672 let focused_window = focused_window.clone();
673
674 let keymap_state = state.keymap_state.as_ref().unwrap();
675 let keycode = Keycode::from(key + MIN_KEYCODE);
676 let keysym = keymap_state.key_get_one_sym(keycode);
677
678 match key_state {
679 wl_keyboard::KeyState::Pressed if !keysym.is_modifier_key() => {
680 let input = PlatformInput::KeyDown(KeyDownEvent {
681 keystroke: Keystroke::from_xkb(keymap_state, state.modifiers, keycode),
682 is_held: false, // todo(linux)
683 });
684
685 state.repeat.current_id += 1;
686 state.repeat.current_keysym = Some(keysym);
687
688 let rate = state.repeat.characters_per_second;
689 let id = state.repeat.current_id;
690 state
691 .loop_handle
692 .insert_source(Timer::from_duration(state.repeat.delay), {
693 let input = input.clone();
694 move |event, _metadata, this| {
695 let mut client = this.get_client();
696 let mut state = client.borrow_mut();
697 let is_repeating = id == state.repeat.current_id
698 && state.repeat.current_keysym.is_some()
699 && state.keyboard_focused_window.is_some();
700
701 if !is_repeating {
702 return TimeoutAction::Drop;
703 }
704
705 let focused_window =
706 state.keyboard_focused_window.as_ref().unwrap().clone();
707
708 drop(state);
709 focused_window.handle_input(input.clone());
710
711 TimeoutAction::ToDuration(Duration::from_secs(1) / rate)
712 }
713 })
714 .unwrap();
715
716 drop(state);
717 focused_window.handle_input(input);
718 }
719 wl_keyboard::KeyState::Released if !keysym.is_modifier_key() => {
720 let input = PlatformInput::KeyUp(KeyUpEvent {
721 keystroke: Keystroke::from_xkb(keymap_state, state.modifiers, keycode),
722 });
723
724 state.repeat.current_keysym = None;
725
726 drop(state);
727 focused_window.handle_input(input);
728 }
729 _ => {}
730 }
731 }
732 _ => {}
733 }
734 }
735}
736
737fn linux_button_to_gpui(button: u32) -> Option<MouseButton> {
738 // These values are coming from <linux/input-event-codes.h>.
739 const BTN_LEFT: u32 = 0x110;
740 const BTN_RIGHT: u32 = 0x111;
741 const BTN_MIDDLE: u32 = 0x112;
742 const BTN_SIDE: u32 = 0x113;
743 const BTN_EXTRA: u32 = 0x114;
744 const BTN_FORWARD: u32 = 0x115;
745 const BTN_BACK: u32 = 0x116;
746
747 Some(match button {
748 BTN_LEFT => MouseButton::Left,
749 BTN_RIGHT => MouseButton::Right,
750 BTN_MIDDLE => MouseButton::Middle,
751 BTN_BACK | BTN_SIDE => MouseButton::Navigate(NavigationDirection::Back),
752 BTN_FORWARD | BTN_EXTRA => MouseButton::Navigate(NavigationDirection::Forward),
753 _ => return None,
754 })
755}
756
757impl Dispatch<wl_pointer::WlPointer, ()> for WaylandClientStatePtr {
758 fn event(
759 this: &mut Self,
760 wl_pointer: &wl_pointer::WlPointer,
761 event: wl_pointer::Event,
762 data: &(),
763 conn: &Connection,
764 qh: &QueueHandle<Self>,
765 ) {
766 let mut client = this.get_client();
767 let mut state = client.borrow_mut();
768 let cursor_icon_name = state.cursor_icon_name.clone();
769
770 match event {
771 wl_pointer::Event::Enter {
772 serial,
773 surface,
774 surface_x,
775 surface_y,
776 ..
777 } => {
778 state.mouse_location = Some(point(px(surface_x as f32), px(surface_y as f32)));
779
780 if let Some(window) = get_window(&mut state, &surface.id()) {
781 state.enter_token = Some(());
782 state.mouse_focused_window = Some(window.clone());
783 state.cursor.set_serial_id(serial);
784 state
785 .cursor
786 .set_icon(&wl_pointer, Some(cursor_icon_name.as_str()));
787 drop(state);
788 window.set_focused(true);
789 }
790 }
791 wl_pointer::Event::Leave { surface, .. } => {
792 if let Some(focused_window) = state.mouse_focused_window.clone() {
793 state.enter_token.take();
794 let input = PlatformInput::MouseExited(MouseExitEvent {
795 position: state.mouse_location.unwrap(),
796 pressed_button: state.button_pressed,
797 modifiers: state.modifiers,
798 });
799 state.mouse_focused_window = None;
800 state.mouse_location = None;
801
802 drop(state);
803 focused_window.handle_input(input);
804 focused_window.set_focused(false);
805 }
806 }
807 wl_pointer::Event::Motion {
808 time,
809 surface_x,
810 surface_y,
811 ..
812 } => {
813 if state.mouse_focused_window.is_none() {
814 return;
815 }
816 state.mouse_location = Some(point(px(surface_x as f32), px(surface_y as f32)));
817 state
818 .cursor
819 .set_icon(&wl_pointer, Some(cursor_icon_name.as_str()));
820
821 if let Some(window) = state.mouse_focused_window.clone() {
822 let input = PlatformInput::MouseMove(MouseMoveEvent {
823 position: state.mouse_location.unwrap(),
824 pressed_button: state.button_pressed,
825 modifiers: state.modifiers,
826 });
827 drop(state);
828 window.handle_input(input);
829 }
830 }
831 wl_pointer::Event::Button {
832 button,
833 state: WEnum::Value(button_state),
834 ..
835 } => {
836 let button = linux_button_to_gpui(button);
837 let Some(button) = button else { return };
838 if state.mouse_focused_window.is_none() {
839 return;
840 }
841 match button_state {
842 wl_pointer::ButtonState::Pressed => {
843 let click_elapsed = state.click.last_click.elapsed();
844
845 if click_elapsed < DOUBLE_CLICK_INTERVAL
846 && is_within_click_distance(
847 state.click.last_location,
848 state.mouse_location.unwrap(),
849 )
850 {
851 state.click.current_count += 1;
852 } else {
853 state.click.current_count = 1;
854 }
855
856 state.click.last_click = Instant::now();
857 state.click.last_location = state.mouse_location.unwrap();
858
859 state.button_pressed = Some(button);
860
861 if let Some(window) = state.mouse_focused_window.clone() {
862 let input = PlatformInput::MouseDown(MouseDownEvent {
863 button,
864 position: state.mouse_location.unwrap(),
865 modifiers: state.modifiers,
866 click_count: state.click.current_count,
867 first_mouse: state.enter_token.take().is_some(),
868 });
869 drop(state);
870 window.handle_input(input);
871 }
872 }
873 wl_pointer::ButtonState::Released => {
874 state.button_pressed = None;
875
876 if let Some(window) = state.mouse_focused_window.clone() {
877 let input = PlatformInput::MouseUp(MouseUpEvent {
878 button,
879 position: state.mouse_location.unwrap(),
880 modifiers: state.modifiers,
881 click_count: state.click.current_count,
882 });
883 drop(state);
884 window.handle_input(input);
885 }
886 }
887 _ => {}
888 }
889 }
890 wl_pointer::Event::AxisRelativeDirection {
891 direction: WEnum::Value(direction),
892 ..
893 } => {
894 state.scroll_direction = match direction {
895 AxisRelativeDirection::Identical => -1.0,
896 AxisRelativeDirection::Inverted => 1.0,
897 _ => -1.0,
898 }
899 }
900 wl_pointer::Event::AxisSource {
901 axis_source: WEnum::Value(axis_source),
902 } => {
903 state.axis_source = axis_source;
904 }
905 wl_pointer::Event::AxisValue120 {
906 axis: WEnum::Value(axis),
907 value120,
908 } => {
909 if let Some(focused_window) = state.mouse_focused_window.clone() {
910 let value = value120 as f64 * state.scroll_direction;
911
912 let input = PlatformInput::ScrollWheel(ScrollWheelEvent {
913 position: state.mouse_location.unwrap(),
914 delta: match axis {
915 wl_pointer::Axis::VerticalScroll => {
916 ScrollDelta::Pixels(point(px(0.0), px(value as f32)))
917 }
918 wl_pointer::Axis::HorizontalScroll => {
919 ScrollDelta::Pixels(point(px(value as f32), px(0.0)))
920 }
921 _ => unimplemented!(),
922 },
923 modifiers: state.modifiers,
924 touch_phase: TouchPhase::Moved,
925 });
926 drop(state);
927 focused_window.handle_input(input)
928 }
929 }
930 wl_pointer::Event::Axis {
931 time,
932 axis: WEnum::Value(axis),
933 value,
934 ..
935 } => {
936 // We handle discrete scroll events with `AxisValue120`.
937 if wl_pointer.version() >= wl_pointer::EVT_AXIS_VALUE120_SINCE
938 && state.axis_source == AxisSource::Wheel
939 {
940 return;
941 }
942 if let Some(focused_window) = state.mouse_focused_window.clone() {
943 let value = value * state.scroll_direction;
944
945 let input = PlatformInput::ScrollWheel(ScrollWheelEvent {
946 position: state.mouse_location.unwrap(),
947 delta: match axis {
948 wl_pointer::Axis::VerticalScroll => {
949 ScrollDelta::Pixels(point(px(0.0), px(value as f32)))
950 }
951 wl_pointer::Axis::HorizontalScroll => {
952 ScrollDelta::Pixels(point(px(value as f32), px(0.0)))
953 }
954 _ => unimplemented!(),
955 },
956 modifiers: state.modifiers,
957 touch_phase: TouchPhase::Moved,
958 });
959 drop(state);
960 focused_window.handle_input(input)
961 }
962 }
963 _ => {}
964 }
965 }
966}
967
968impl Dispatch<wp_fractional_scale_v1::WpFractionalScaleV1, ObjectId> for WaylandClientStatePtr {
969 fn event(
970 this: &mut Self,
971 _: &wp_fractional_scale_v1::WpFractionalScaleV1,
972 event: <wp_fractional_scale_v1::WpFractionalScaleV1 as Proxy>::Event,
973 surface_id: &ObjectId,
974 _: &Connection,
975 _: &QueueHandle<Self>,
976 ) {
977 let client = this.get_client();
978 let mut state = client.borrow_mut();
979
980 let Some(window) = get_window(&mut state, surface_id) else {
981 return;
982 };
983
984 drop(state);
985 window.handle_fractional_scale_event(event);
986 }
987}
988
989impl Dispatch<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1, ObjectId>
990 for WaylandClientStatePtr
991{
992 fn event(
993 this: &mut Self,
994 _: &zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1,
995 event: zxdg_toplevel_decoration_v1::Event,
996 surface_id: &ObjectId,
997 _: &Connection,
998 _: &QueueHandle<Self>,
999 ) {
1000 let client = this.get_client();
1001 let mut state = client.borrow_mut();
1002 let Some(window) = get_window(&mut state, surface_id) else {
1003 return;
1004 };
1005
1006 drop(state);
1007 window.handle_toplevel_decoration_event(event);
1008 }
1009}