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