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