1use std::cell::RefCell;
2use std::rc::Rc;
3use std::sync::Arc;
4use std::time::Duration;
5
6use calloop::timer::{TimeoutAction, Timer};
7use calloop::LoopHandle;
8use calloop_wayland_source::WaylandSource;
9use copypasta::wayland_clipboard::{create_clipboards_from_external, Clipboard, Primary};
10use copypasta::ClipboardProvider;
11use wayland_backend::client::ObjectId;
12use wayland_backend::protocol::WEnum;
13use wayland_client::globals::{registry_queue_init, GlobalListContents};
14use wayland_client::protocol::wl_callback::WlCallback;
15use wayland_client::protocol::wl_pointer::AxisRelativeDirection;
16use wayland_client::{
17 delegate_noop,
18 protocol::{
19 wl_buffer, wl_callback, wl_compositor, wl_keyboard, wl_pointer, wl_registry, wl_seat,
20 wl_shm, wl_shm_pool,
21 wl_surface::{self, WlSurface},
22 },
23 Connection, Dispatch, Proxy, QueueHandle,
24};
25use wayland_protocols::wp::fractional_scale::v1::client::{
26 wp_fractional_scale_manager_v1, wp_fractional_scale_v1,
27};
28use wayland_protocols::wp::viewporter::client::{wp_viewport, wp_viewporter};
29use wayland_protocols::xdg::decoration::zv1::client::{
30 zxdg_decoration_manager_v1, zxdg_toplevel_decoration_v1,
31};
32use wayland_protocols::xdg::shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base};
33use xkbcommon::xkb::ffi::XKB_KEYMAP_FORMAT_TEXT_V1;
34use xkbcommon::xkb::{self, Keycode, KEYMAP_COMPILE_NO_FLAGS};
35
36use crate::platform::linux::client::Client;
37use crate::platform::linux::wayland::cursor::Cursor;
38use crate::platform::linux::wayland::window::{WaylandDecorationState, WaylandWindow};
39use crate::platform::{LinuxPlatformInner, PlatformWindow};
40use crate::{
41 platform::linux::wayland::window::WaylandWindowState, AnyWindowHandle, CursorStyle, DisplayId,
42 KeyDownEvent, KeyUpEvent, Keystroke, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent,
43 MouseUpEvent, NavigationDirection, Pixels, PlatformDisplay, PlatformInput, Point, ScrollDelta,
44 ScrollWheelEvent, TouchPhase, WindowOptions,
45};
46
47/// Used to convert evdev scancode to xkb scancode
48const MIN_KEYCODE: u32 = 8;
49
50pub(crate) struct WaylandClientStateInner {
51 compositor: wl_compositor::WlCompositor,
52 wm_base: xdg_wm_base::XdgWmBase,
53 shm: wl_shm::WlShm,
54 viewporter: Option<wp_viewporter::WpViewporter>,
55 fractional_scale_manager: Option<wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1>,
56 decoration_manager: Option<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1>,
57 windows: Vec<(xdg_surface::XdgSurface, Rc<WaylandWindowState>)>,
58 platform_inner: Rc<LinuxPlatformInner>,
59 keymap_state: Option<xkb::State>,
60 repeat: KeyRepeat,
61 modifiers: Modifiers,
62 scroll_direction: f64,
63 mouse_location: Option<Point<Pixels>>,
64 button_pressed: Option<MouseButton>,
65 mouse_focused_window: Option<Rc<WaylandWindowState>>,
66 keyboard_focused_window: Option<Rc<WaylandWindowState>>,
67 loop_handle: Rc<LoopHandle<'static, ()>>,
68}
69
70pub(crate) struct CursorState {
71 cursor_icon_name: String,
72 cursor: Option<Cursor>,
73}
74
75#[derive(Clone)]
76pub(crate) struct WaylandClientState {
77 client_state_inner: Rc<RefCell<WaylandClientStateInner>>,
78 cursor_state: Rc<RefCell<CursorState>>,
79 clipboard: Rc<RefCell<Clipboard>>,
80 primary: Rc<RefCell<Primary>>,
81}
82
83pub(crate) struct KeyRepeat {
84 characters_per_second: u32,
85 delay: Duration,
86 current_id: u64,
87 current_keysym: Option<xkb::Keysym>,
88}
89
90pub(crate) struct WaylandClient {
91 platform_inner: Rc<LinuxPlatformInner>,
92 state: WaylandClientState,
93 qh: Arc<QueueHandle<WaylandClientState>>,
94}
95
96const WL_SEAT_VERSION: u32 = 4;
97
98impl WaylandClient {
99 pub(crate) fn new(linux_platform_inner: Rc<LinuxPlatformInner>) -> Self {
100 let conn = Connection::connect_to_env().unwrap();
101
102 let (globals, mut event_queue) = registry_queue_init::<WaylandClientState>(&conn).unwrap();
103 let qh = event_queue.handle();
104
105 globals.contents().with_list(|list| {
106 for global in list {
107 if global.interface == "wl_seat" {
108 globals.registry().bind::<wl_seat::WlSeat, _, _>(
109 global.name,
110 WL_SEAT_VERSION,
111 &qh,
112 (),
113 );
114 }
115 }
116 });
117
118 let display = conn.backend().display_ptr() as *mut std::ffi::c_void;
119 let (primary, clipboard) = unsafe { create_clipboards_from_external(display) };
120
121 let mut state_inner = Rc::new(RefCell::new(WaylandClientStateInner {
122 compositor: globals.bind(&qh, 1..=1, ()).unwrap(),
123 wm_base: globals.bind(&qh, 1..=1, ()).unwrap(),
124 shm: globals.bind(&qh, 1..=1, ()).unwrap(),
125 viewporter: globals.bind(&qh, 1..=1, ()).ok(),
126 fractional_scale_manager: globals.bind(&qh, 1..=1, ()).ok(),
127 decoration_manager: globals.bind(&qh, 1..=1, ()).ok(),
128 windows: Vec::new(),
129 platform_inner: Rc::clone(&linux_platform_inner),
130 keymap_state: None,
131 repeat: KeyRepeat {
132 characters_per_second: 16,
133 delay: Duration::from_millis(500),
134 current_id: 0,
135 current_keysym: None,
136 },
137 modifiers: Modifiers {
138 shift: false,
139 control: false,
140 alt: false,
141 function: false,
142 command: false,
143 },
144 scroll_direction: -1.0,
145 mouse_location: None,
146 button_pressed: None,
147 mouse_focused_window: None,
148 keyboard_focused_window: None,
149 loop_handle: Rc::clone(&linux_platform_inner.loop_handle),
150 }));
151
152 let mut cursor_state = Rc::new(RefCell::new(CursorState {
153 cursor_icon_name: "arrow".to_string(),
154 cursor: None,
155 }));
156
157 let source = WaylandSource::new(conn, event_queue);
158
159 let mut state = WaylandClientState {
160 client_state_inner: Rc::clone(&state_inner),
161 cursor_state: Rc::clone(&cursor_state),
162 clipboard: Rc::new(RefCell::new(clipboard)),
163 primary: Rc::new(RefCell::new(primary)),
164 };
165 let mut state_loop = state.clone();
166 linux_platform_inner
167 .loop_handle
168 .insert_source(source, move |_, queue, _| {
169 queue.dispatch_pending(&mut state_loop)
170 })
171 .unwrap();
172
173 Self {
174 platform_inner: linux_platform_inner,
175 state,
176 qh: Arc::new(qh),
177 }
178 }
179}
180
181impl Client for WaylandClient {
182 fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
183 Vec::new()
184 }
185
186 fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
187 unimplemented!()
188 }
189
190 fn open_window(
191 &self,
192 handle: AnyWindowHandle,
193 options: WindowOptions,
194 ) -> Box<dyn PlatformWindow> {
195 let mut state = self.state.client_state_inner.borrow_mut();
196
197 let wl_surface = state.compositor.create_surface(&self.qh, ());
198 let xdg_surface = state.wm_base.get_xdg_surface(&wl_surface, &self.qh, ());
199 let toplevel = xdg_surface.get_toplevel(&self.qh, ());
200 let wl_surface = Arc::new(wl_surface);
201
202 // Attempt to set up window decorations based on the requested configuration
203 //
204 // Note that wayland compositors may either not support decorations at all, or may
205 // support them but not allow clients to choose whether they are enabled or not.
206 // We attempt to account for these cases here.
207
208 if let Some(decoration_manager) = state.decoration_manager.as_ref() {
209 // The protocol for managing decorations is present at least, but that doesn't
210 // mean that the compositor will allow us to use it.
211
212 let decoration =
213 decoration_manager.get_toplevel_decoration(&toplevel, &self.qh, xdg_surface.id());
214
215 // todo(linux) - options.titlebar is lacking information required for wayland.
216 // Especially, whether a titlebar is wanted in itself.
217 //
218 // Removing the titlebar also removes the entire window frame (ie. the ability to
219 // close, move and resize the window [snapping still works]). This needs additional
220 // handling in Zed, in order to implement drag handlers on a titlebar element.
221 //
222 // Since all of this handling is not present, we request server-side decorations
223 // for now as a stopgap solution.
224 decoration.set_mode(zxdg_toplevel_decoration_v1::Mode::ServerSide);
225 }
226
227 let viewport = state
228 .viewporter
229 .as_ref()
230 .map(|viewporter| viewporter.get_viewport(&wl_surface, &self.qh, ()));
231
232 wl_surface.frame(&self.qh, wl_surface.clone());
233 wl_surface.commit();
234
235 let window_state: Rc<WaylandWindowState> = Rc::new(WaylandWindowState::new(
236 wl_surface.clone(),
237 viewport,
238 Arc::new(toplevel),
239 options,
240 ));
241
242 if let Some(fractional_scale_manager) = state.fractional_scale_manager.as_ref() {
243 fractional_scale_manager.get_fractional_scale(&wl_surface, &self.qh, xdg_surface.id());
244 }
245
246 state.windows.push((xdg_surface, Rc::clone(&window_state)));
247 Box::new(WaylandWindow(window_state))
248 }
249
250 fn set_cursor_style(&self, style: CursorStyle) {
251 let cursor_icon_name = match style {
252 CursorStyle::Arrow => "arrow".to_string(),
253 CursorStyle::IBeam => "text".to_string(),
254 CursorStyle::Crosshair => "crosshair".to_string(),
255 CursorStyle::ClosedHand => "grabbing".to_string(),
256 CursorStyle::OpenHand => "openhand".to_string(),
257 CursorStyle::PointingHand => "hand".to_string(),
258 CursorStyle::ResizeLeft => "w-resize".to_string(),
259 CursorStyle::ResizeRight => "e-resize".to_string(),
260 CursorStyle::ResizeLeftRight => "ew-resize".to_string(),
261 CursorStyle::ResizeUp => "n-resize".to_string(),
262 CursorStyle::ResizeDown => "s-resize".to_string(),
263 CursorStyle::ResizeUpDown => "ns-resize".to_string(),
264 CursorStyle::DisappearingItem => "grabbing".to_string(), // todo!(linux) - couldn't find equivalent icon in linux
265 CursorStyle::IBeamCursorForVerticalLayout => "vertical-text".to_string(),
266 CursorStyle::OperationNotAllowed => "not-allowed".to_string(),
267 CursorStyle::DragLink => "dnd-link".to_string(),
268 CursorStyle::DragCopy => "dnd-copy".to_string(),
269 CursorStyle::ContextualMenu => "context-menu".to_string(),
270 };
271
272 let mut cursor_state = self.state.cursor_state.borrow_mut();
273 cursor_state.cursor_icon_name = cursor_icon_name;
274 }
275
276 fn get_clipboard(&self) -> Rc<RefCell<dyn ClipboardProvider>> {
277 self.state.clipboard.clone()
278 }
279
280 fn get_primary(&self) -> Rc<RefCell<dyn ClipboardProvider>> {
281 self.state.primary.clone()
282 }
283}
284
285impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for WaylandClientState {
286 fn event(
287 state: &mut Self,
288 registry: &wl_registry::WlRegistry,
289 event: wl_registry::Event,
290 _: &GlobalListContents,
291 _: &Connection,
292 qh: &QueueHandle<Self>,
293 ) {
294 match event {
295 wl_registry::Event::Global {
296 name,
297 interface,
298 version: _,
299 } => {
300 if interface.as_str() == "wl_seat" {
301 registry.bind::<wl_seat::WlSeat, _, _>(name, 4, qh, ());
302 }
303 }
304 wl_registry::Event::GlobalRemove { name: _ } => {}
305 _ => {}
306 }
307 }
308}
309
310delegate_noop!(WaylandClientState: ignore wl_compositor::WlCompositor);
311delegate_noop!(WaylandClientState: ignore wl_surface::WlSurface);
312delegate_noop!(WaylandClientState: ignore wl_shm::WlShm);
313delegate_noop!(WaylandClientState: ignore wl_shm_pool::WlShmPool);
314delegate_noop!(WaylandClientState: ignore wl_buffer::WlBuffer);
315delegate_noop!(WaylandClientState: ignore wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1);
316delegate_noop!(WaylandClientState: ignore zxdg_decoration_manager_v1::ZxdgDecorationManagerV1);
317delegate_noop!(WaylandClientState: ignore wp_viewporter::WpViewporter);
318delegate_noop!(WaylandClientState: ignore wp_viewport::WpViewport);
319
320impl Dispatch<WlCallback, Arc<WlSurface>> for WaylandClientState {
321 fn event(
322 state: &mut Self,
323 _: &WlCallback,
324 event: wl_callback::Event,
325 surf: &Arc<WlSurface>,
326 _: &Connection,
327 qh: &QueueHandle<Self>,
328 ) {
329 let mut state = state.client_state_inner.borrow_mut();
330 if let wl_callback::Event::Done { .. } = event {
331 for window in &state.windows {
332 if window.1.surface.id() == surf.id() {
333 window.1.surface.frame(qh, surf.clone());
334 window.1.update();
335 window.1.surface.commit();
336 }
337 }
338 }
339 }
340}
341
342impl Dispatch<xdg_surface::XdgSurface, ()> for WaylandClientState {
343 fn event(
344 state: &mut Self,
345 xdg_surface: &xdg_surface::XdgSurface,
346 event: xdg_surface::Event,
347 _: &(),
348 _: &Connection,
349 _: &QueueHandle<Self>,
350 ) {
351 let mut state = state.client_state_inner.borrow_mut();
352 if let xdg_surface::Event::Configure { serial, .. } = event {
353 xdg_surface.ack_configure(serial);
354 for window in &state.windows {
355 if &window.0 == xdg_surface {
356 window.1.update();
357 window.1.surface.commit();
358 return;
359 }
360 }
361 }
362 }
363}
364
365impl Dispatch<xdg_toplevel::XdgToplevel, ()> for WaylandClientState {
366 fn event(
367 state: &mut Self,
368 xdg_toplevel: &xdg_toplevel::XdgToplevel,
369 event: <xdg_toplevel::XdgToplevel as Proxy>::Event,
370 _: &(),
371 _: &Connection,
372 _: &QueueHandle<Self>,
373 ) {
374 let mut state = state.client_state_inner.borrow_mut();
375 if let xdg_toplevel::Event::Configure {
376 width,
377 height,
378 states: _states,
379 } = event
380 {
381 if width == 0 || height == 0 {
382 return;
383 }
384 for window in &state.windows {
385 if window.1.toplevel.id() == xdg_toplevel.id() {
386 window.1.resize(width, height);
387 window.1.surface.commit();
388 return;
389 }
390 }
391 } else if let xdg_toplevel::Event::Close = event {
392 state.windows.retain(|(_, window)| {
393 if window.toplevel.id() == xdg_toplevel.id() {
394 window.toplevel.destroy();
395 false
396 } else {
397 true
398 }
399 });
400 state.platform_inner.loop_signal.stop();
401 }
402 }
403}
404
405impl Dispatch<xdg_wm_base::XdgWmBase, ()> for WaylandClientState {
406 fn event(
407 _: &mut Self,
408 wm_base: &xdg_wm_base::XdgWmBase,
409 event: <xdg_wm_base::XdgWmBase as Proxy>::Event,
410 _: &(),
411 _: &Connection,
412 _: &QueueHandle<Self>,
413 ) {
414 if let xdg_wm_base::Event::Ping { serial } = event {
415 wm_base.pong(serial);
416 }
417 }
418}
419
420impl Dispatch<wl_seat::WlSeat, ()> for WaylandClientState {
421 fn event(
422 state: &mut Self,
423 seat: &wl_seat::WlSeat,
424 event: wl_seat::Event,
425 data: &(),
426 conn: &Connection,
427 qh: &QueueHandle<Self>,
428 ) {
429 if let wl_seat::Event::Capabilities {
430 capabilities: WEnum::Value(capabilities),
431 } = event
432 {
433 if capabilities.contains(wl_seat::Capability::Keyboard) {
434 seat.get_keyboard(qh, ());
435 }
436 if capabilities.contains(wl_seat::Capability::Pointer) {
437 seat.get_pointer(qh, ());
438 }
439 }
440 }
441}
442
443impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClientState {
444 fn event(
445 this: &mut Self,
446 keyboard: &wl_keyboard::WlKeyboard,
447 event: wl_keyboard::Event,
448 data: &(),
449 conn: &Connection,
450 qh: &QueueHandle<Self>,
451 ) {
452 let mut state = this.client_state_inner.borrow_mut();
453 match event {
454 wl_keyboard::Event::RepeatInfo { rate, delay } => {
455 state.repeat.characters_per_second = rate as u32;
456 state.repeat.delay = Duration::from_millis(delay as u64);
457 }
458 wl_keyboard::Event::Keymap {
459 format: WEnum::Value(format),
460 fd,
461 size,
462 ..
463 } => {
464 assert_eq!(
465 format,
466 wl_keyboard::KeymapFormat::XkbV1,
467 "Unsupported keymap format"
468 );
469 let keymap = unsafe {
470 xkb::Keymap::new_from_fd(
471 &xkb::Context::new(xkb::CONTEXT_NO_FLAGS),
472 fd,
473 size as usize,
474 XKB_KEYMAP_FORMAT_TEXT_V1,
475 KEYMAP_COMPILE_NO_FLAGS,
476 )
477 .unwrap()
478 }
479 .unwrap();
480 state.keymap_state = Some(xkb::State::new(&keymap));
481 }
482 wl_keyboard::Event::Enter { surface, .. } => {
483 state.keyboard_focused_window = state
484 .windows
485 .iter()
486 .find(|&w| w.1.surface.id() == surface.id())
487 .map(|w| w.1.clone());
488
489 if let Some(window) = &state.keyboard_focused_window {
490 window.set_focused(true);
491 }
492 }
493 wl_keyboard::Event::Leave { surface, .. } => {
494 let keyboard_focused_window = state
495 .windows
496 .iter()
497 .find(|&w| w.1.surface.id() == surface.id())
498 .map(|w| w.1.clone());
499
500 if let Some(window) = keyboard_focused_window {
501 window.set_focused(false);
502 }
503
504 state.keyboard_focused_window = None;
505 }
506 wl_keyboard::Event::Modifiers {
507 mods_depressed,
508 mods_latched,
509 mods_locked,
510 group,
511 ..
512 } => {
513 let keymap_state = state.keymap_state.as_mut().unwrap();
514 keymap_state.update_mask(mods_depressed, mods_latched, mods_locked, 0, 0, group);
515
516 let shift =
517 keymap_state.mod_name_is_active(xkb::MOD_NAME_SHIFT, xkb::STATE_MODS_EFFECTIVE);
518 let alt =
519 keymap_state.mod_name_is_active(xkb::MOD_NAME_ALT, xkb::STATE_MODS_EFFECTIVE);
520 let control =
521 keymap_state.mod_name_is_active(xkb::MOD_NAME_CTRL, xkb::STATE_MODS_EFFECTIVE);
522 let command =
523 keymap_state.mod_name_is_active(xkb::MOD_NAME_LOGO, xkb::STATE_MODS_EFFECTIVE);
524
525 state.modifiers.shift = shift;
526 state.modifiers.alt = alt;
527 state.modifiers.control = control;
528 state.modifiers.command = command;
529 }
530 wl_keyboard::Event::Key {
531 key,
532 state: WEnum::Value(key_state),
533 ..
534 } => {
535 let focused_window = &state.keyboard_focused_window;
536 let Some(focused_window) = focused_window else {
537 return;
538 };
539
540 let keymap_state = state.keymap_state.as_ref().unwrap();
541 let keycode = Keycode::from(key + MIN_KEYCODE);
542 let keysym = keymap_state.key_get_one_sym(keycode);
543
544 match key_state {
545 wl_keyboard::KeyState::Pressed => {
546 let input = PlatformInput::KeyDown(KeyDownEvent {
547 keystroke: Keystroke::from_xkb(keymap_state, state.modifiers, keycode),
548 is_held: false, // todo(linux)
549 });
550
551 focused_window.handle_input(input.clone());
552
553 if !keysym.is_modifier_key() {
554 state.repeat.current_id += 1;
555 state.repeat.current_keysym = Some(keysym);
556
557 let rate = state.repeat.characters_per_second;
558 let delay = state.repeat.delay;
559 let id = state.repeat.current_id;
560 let this = this.clone();
561
562 let timer = Timer::from_duration(delay);
563 let state_ = Rc::clone(&this.client_state_inner);
564 state
565 .loop_handle
566 .insert_source(timer, move |event, _metadata, shared_data| {
567 let state_ = state_.borrow_mut();
568 let is_repeating = id == state_.repeat.current_id
569 && state_.repeat.current_keysym.is_some()
570 && state_.keyboard_focused_window.is_some();
571
572 if !is_repeating {
573 return TimeoutAction::Drop;
574 }
575
576 let focused_window =
577 state_.keyboard_focused_window.as_ref().unwrap().clone();
578
579 drop(state_);
580
581 focused_window.handle_input(input.clone());
582
583 TimeoutAction::ToDuration(Duration::from_secs(1) / rate)
584 })
585 .unwrap();
586 }
587 }
588 wl_keyboard::KeyState::Released => {
589 focused_window.handle_input(PlatformInput::KeyUp(KeyUpEvent {
590 keystroke: Keystroke::from_xkb(keymap_state, state.modifiers, keycode),
591 }));
592
593 if !keysym.is_modifier_key() {
594 state.repeat.current_keysym = None;
595 }
596 }
597 _ => {}
598 }
599 }
600 _ => {}
601 }
602 }
603}
604
605fn linux_button_to_gpui(button: u32) -> Option<MouseButton> {
606 // These values are coming from <linux/input-event-codes.h>.
607 const BTN_LEFT: u32 = 0x110;
608 const BTN_RIGHT: u32 = 0x111;
609 const BTN_MIDDLE: u32 = 0x112;
610 const BTN_SIDE: u32 = 0x113;
611 const BTN_EXTRA: u32 = 0x114;
612 const BTN_FORWARD: u32 = 0x115;
613 const BTN_BACK: u32 = 0x116;
614
615 Some(match button {
616 BTN_LEFT => MouseButton::Left,
617 BTN_RIGHT => MouseButton::Right,
618 BTN_MIDDLE => MouseButton::Middle,
619 BTN_BACK | BTN_SIDE => MouseButton::Navigate(NavigationDirection::Back),
620 BTN_FORWARD | BTN_EXTRA => MouseButton::Navigate(NavigationDirection::Forward),
621 _ => return None,
622 })
623}
624
625impl Dispatch<wl_pointer::WlPointer, ()> for WaylandClientState {
626 fn event(
627 state: &mut Self,
628 wl_pointer: &wl_pointer::WlPointer,
629 event: wl_pointer::Event,
630 data: &(),
631 conn: &Connection,
632 qh: &QueueHandle<Self>,
633 ) {
634 let mut cursor_state = state.cursor_state.borrow_mut();
635 let mut state = state.client_state_inner.borrow_mut();
636
637 if cursor_state.cursor.is_none() {
638 cursor_state.cursor = Some(Cursor::new(&conn, &state.compositor, &qh, &state.shm, 24));
639 }
640 let cursor_icon_name = cursor_state.cursor_icon_name.clone();
641 let mut cursor: &mut Cursor = cursor_state.cursor.as_mut().unwrap();
642
643 match event {
644 wl_pointer::Event::Enter {
645 serial,
646 surface,
647 surface_x,
648 surface_y,
649 ..
650 } => {
651 let mut mouse_focused_window = None;
652 for window in &state.windows {
653 if window.1.surface.id() == surface.id() {
654 window.1.set_focused(true);
655 mouse_focused_window = Some(Rc::clone(&window.1));
656 }
657 }
658 if mouse_focused_window.is_some() {
659 state.mouse_focused_window = mouse_focused_window;
660 cursor.set_serial_id(serial);
661 cursor.set_icon(&wl_pointer, cursor_icon_name);
662 }
663
664 state.mouse_location = Some(Point {
665 x: Pixels::from(surface_x),
666 y: Pixels::from(surface_y),
667 });
668 }
669 wl_pointer::Event::Motion {
670 time,
671 surface_x,
672 surface_y,
673 ..
674 } => {
675 if state.mouse_focused_window.is_none() {
676 return;
677 }
678 state.mouse_location = Some(Point {
679 x: Pixels::from(surface_x),
680 y: Pixels::from(surface_y),
681 });
682 state.mouse_focused_window.as_ref().unwrap().handle_input(
683 PlatformInput::MouseMove(MouseMoveEvent {
684 position: state.mouse_location.unwrap(),
685 pressed_button: state.button_pressed,
686 modifiers: state.modifiers,
687 }),
688 );
689 cursor.set_icon(&wl_pointer, cursor_icon_name);
690 }
691 wl_pointer::Event::Button {
692 button,
693 state: WEnum::Value(button_state),
694 ..
695 } => {
696 let button = linux_button_to_gpui(button);
697 let Some(button) = button else { return };
698 if state.mouse_focused_window.is_none() || state.mouse_location.is_none() {
699 return;
700 }
701 match button_state {
702 wl_pointer::ButtonState::Pressed => {
703 state.button_pressed = Some(button);
704 state.mouse_focused_window.as_ref().unwrap().handle_input(
705 PlatformInput::MouseDown(MouseDownEvent {
706 button,
707 position: state.mouse_location.unwrap(),
708 modifiers: state.modifiers,
709 click_count: 1,
710 }),
711 );
712 }
713 wl_pointer::ButtonState::Released => {
714 state.button_pressed = None;
715 state.mouse_focused_window.as_ref().unwrap().handle_input(
716 PlatformInput::MouseUp(MouseUpEvent {
717 button,
718 position: state.mouse_location.unwrap(),
719 modifiers: Modifiers::default(),
720 click_count: 1,
721 }),
722 );
723 }
724 _ => {}
725 }
726 }
727 wl_pointer::Event::AxisRelativeDirection {
728 direction: WEnum::Value(direction),
729 ..
730 } => {
731 state.scroll_direction = match direction {
732 AxisRelativeDirection::Identical => -1.0,
733 AxisRelativeDirection::Inverted => 1.0,
734 _ => -1.0,
735 }
736 }
737 wl_pointer::Event::Axis {
738 time,
739 axis: WEnum::Value(axis),
740 value,
741 ..
742 } => {
743 let focused_window = &state.mouse_focused_window;
744 let mouse_location = &state.mouse_location;
745 if let (Some(focused_window), Some(mouse_location)) =
746 (focused_window, mouse_location)
747 {
748 let value = value * state.scroll_direction;
749 focused_window.handle_input(PlatformInput::ScrollWheel(ScrollWheelEvent {
750 position: *mouse_location,
751 delta: match axis {
752 wl_pointer::Axis::VerticalScroll => {
753 ScrollDelta::Pixels(Point::new(Pixels(0.0), Pixels(value as f32)))
754 }
755 wl_pointer::Axis::HorizontalScroll => {
756 ScrollDelta::Pixels(Point::new(Pixels(value as f32), Pixels(0.0)))
757 }
758 _ => unimplemented!(),
759 },
760 modifiers: state.modifiers,
761 touch_phase: TouchPhase::Started,
762 }))
763 }
764 }
765 wl_pointer::Event::Leave { surface, .. } => {
766 let focused_window = &state.mouse_focused_window;
767 if let Some(focused_window) = focused_window {
768 focused_window.handle_input(PlatformInput::MouseMove(MouseMoveEvent {
769 position: Point::<Pixels>::default(),
770 pressed_button: None,
771 modifiers: Modifiers::default(),
772 }));
773 focused_window.set_focused(false);
774 }
775 state.mouse_focused_window = None;
776 state.mouse_location = None;
777 }
778 _ => {}
779 }
780 }
781}
782
783impl Dispatch<wp_fractional_scale_v1::WpFractionalScaleV1, ObjectId> for WaylandClientState {
784 fn event(
785 state: &mut Self,
786 _: &wp_fractional_scale_v1::WpFractionalScaleV1,
787 event: <wp_fractional_scale_v1::WpFractionalScaleV1 as Proxy>::Event,
788 id: &ObjectId,
789 _: &Connection,
790 _: &QueueHandle<Self>,
791 ) {
792 let mut state = state.client_state_inner.borrow_mut();
793 if let wp_fractional_scale_v1::Event::PreferredScale { scale, .. } = event {
794 for window in &state.windows {
795 if window.0.id() == *id {
796 window.1.rescale(scale as f32 / 120.0);
797 return;
798 }
799 }
800 }
801 }
802}
803
804impl Dispatch<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1, ObjectId>
805 for WaylandClientState
806{
807 fn event(
808 state: &mut Self,
809 _: &zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1,
810 event: zxdg_toplevel_decoration_v1::Event,
811 surface_id: &ObjectId,
812 _: &Connection,
813 _: &QueueHandle<Self>,
814 ) {
815 let mut state = state.client_state_inner.borrow_mut();
816 if let zxdg_toplevel_decoration_v1::Event::Configure { mode, .. } = event {
817 for window in &state.windows {
818 if window.0.id() == *surface_id {
819 match mode {
820 WEnum::Value(zxdg_toplevel_decoration_v1::Mode::ServerSide) => {
821 window
822 .1
823 .set_decoration_state(WaylandDecorationState::Server);
824 }
825 WEnum::Value(zxdg_toplevel_decoration_v1::Mode::ClientSide) => {
826 window
827 .1
828 .set_decoration_state(WaylandDecorationState::Client);
829 }
830 WEnum::Value(_) => {
831 log::warn!("Unknown decoration mode");
832 }
833 WEnum::Unknown(v) => {
834 log::warn!("Unknown decoration mode: {}", v);
835 }
836 }
837 return;
838 }
839 }
840 }
841 }
842}