client.rs

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