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