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}