client.rs

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