1use std::any::Any;
2use std::cell::{Ref, RefCell, RefMut};
3use std::ffi::c_void;
4use std::num::NonZeroU32;
5use std::ptr::NonNull;
6use std::rc::{Rc, Weak};
7use std::sync::Arc;
8
9use blade_graphics as gpu;
10use collections::{HashMap, HashSet};
11use futures::channel::oneshot::Receiver;
12use raw_window_handle as rwh;
13use wayland_backend::client::ObjectId;
14use wayland_client::protocol::wl_region::WlRegion;
15use wayland_client::WEnum;
16use wayland_client::{protocol::wl_surface, Proxy};
17use wayland_protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1;
18use wayland_protocols::wp::viewporter::client::wp_viewport;
19use wayland_protocols::xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1;
20use wayland_protocols::xdg::shell::client::xdg_surface;
21use wayland_protocols::xdg::shell::client::xdg_toplevel::{self, WmCapabilities};
22use wayland_protocols_plasma::blur::client::{org_kde_kwin_blur, org_kde_kwin_blur_manager};
23
24use crate::platform::blade::{BladeRenderer, BladeSurfaceConfig};
25use crate::platform::linux::wayland::display::WaylandDisplay;
26use crate::platform::{PlatformAtlas, PlatformInputHandler, PlatformWindow};
27use crate::scene::Scene;
28use crate::{
29 px, size, Bounds, DevicePixels, Globals, Modifiers, Pixels, PlatformDisplay, PlatformInput,
30 Point, PromptLevel, Size, WaylandClientState, WaylandClientStatePtr, WindowAppearance,
31 WindowBackgroundAppearance, WindowBounds, WindowParams,
32};
33
34#[derive(Default)]
35pub(crate) struct Callbacks {
36 request_frame: Option<Box<dyn FnMut()>>,
37 input: Option<Box<dyn FnMut(crate::PlatformInput) -> crate::DispatchEventResult>>,
38 active_status_change: Option<Box<dyn FnMut(bool)>>,
39 resize: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
40 moved: Option<Box<dyn FnMut()>>,
41 should_close: Option<Box<dyn FnMut() -> bool>>,
42 close: Option<Box<dyn FnOnce()>>,
43 appearance_changed: Option<Box<dyn FnMut()>>,
44}
45
46struct RawWindow {
47 window: *mut c_void,
48 display: *mut c_void,
49}
50
51impl rwh::HasWindowHandle for RawWindow {
52 fn window_handle(&self) -> Result<rwh::WindowHandle<'_>, rwh::HandleError> {
53 let window = NonNull::new(self.window).unwrap();
54 let handle = rwh::WaylandWindowHandle::new(window);
55 Ok(unsafe { rwh::WindowHandle::borrow_raw(handle.into()) })
56 }
57}
58impl rwh::HasDisplayHandle for RawWindow {
59 fn display_handle(&self) -> Result<rwh::DisplayHandle<'_>, rwh::HandleError> {
60 let display = NonNull::new(self.display).unwrap();
61 let handle = rwh::WaylandDisplayHandle::new(display);
62 Ok(unsafe { rwh::DisplayHandle::borrow_raw(handle.into()) })
63 }
64}
65
66pub struct WaylandWindowState {
67 xdg_surface: xdg_surface::XdgSurface,
68 acknowledged_first_configure: bool,
69 pub surface: wl_surface::WlSurface,
70 decoration: Option<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1>,
71 blur: Option<org_kde_kwin_blur::OrgKdeKwinBlur>,
72 toplevel: xdg_toplevel::XdgToplevel,
73 viewport: Option<wp_viewport::WpViewport>,
74 outputs: HashSet<ObjectId>,
75 globals: Globals,
76 renderer: BladeRenderer,
77 bounds: Bounds<u32>,
78 scale: f32,
79 input_handler: Option<PlatformInputHandler>,
80 decoration_state: WaylandDecorationState,
81 fullscreen: bool,
82 restore_bounds: Bounds<DevicePixels>,
83 maximized: bool,
84 client: WaylandClientStatePtr,
85 callbacks: Callbacks,
86}
87
88#[derive(Clone)]
89pub struct WaylandWindowStatePtr {
90 state: Rc<RefCell<WaylandWindowState>>,
91 callbacks: Rc<RefCell<Callbacks>>,
92}
93
94impl WaylandWindowState {
95 #[allow(clippy::too_many_arguments)]
96 pub(crate) fn new(
97 surface: wl_surface::WlSurface,
98 xdg_surface: xdg_surface::XdgSurface,
99 toplevel: xdg_toplevel::XdgToplevel,
100 decoration: Option<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1>,
101 viewport: Option<wp_viewport::WpViewport>,
102 client: WaylandClientStatePtr,
103 globals: Globals,
104 options: WindowParams,
105 ) -> Self {
106 let bounds = options.bounds.map(|p| p.0 as u32);
107
108 let raw = RawWindow {
109 window: surface.id().as_ptr().cast::<c_void>(),
110 display: surface
111 .backend()
112 .upgrade()
113 .unwrap()
114 .display_ptr()
115 .cast::<c_void>(),
116 };
117 let gpu = Arc::new(
118 unsafe {
119 gpu::Context::init_windowed(
120 &raw,
121 gpu::ContextDesc {
122 validation: false,
123 capture: false,
124 overlay: false,
125 },
126 )
127 }
128 .unwrap(),
129 );
130 let config = BladeSurfaceConfig {
131 size: gpu::Extent {
132 width: bounds.size.width,
133 height: bounds.size.height,
134 depth: 1,
135 },
136 transparent: options.window_background != WindowBackgroundAppearance::Opaque,
137 };
138
139 Self {
140 xdg_surface,
141 acknowledged_first_configure: false,
142 surface,
143 decoration,
144 blur: None,
145 toplevel,
146 viewport,
147 globals,
148 outputs: HashSet::default(),
149 renderer: BladeRenderer::new(gpu, config),
150 bounds,
151 scale: 1.0,
152 input_handler: None,
153 decoration_state: WaylandDecorationState::Client,
154 fullscreen: false,
155 restore_bounds: Bounds::default(),
156 maximized: false,
157 callbacks: Callbacks::default(),
158 client,
159 }
160 }
161}
162
163pub(crate) struct WaylandWindow(pub WaylandWindowStatePtr);
164
165impl Drop for WaylandWindow {
166 fn drop(&mut self) {
167 let mut state = self.0.state.borrow_mut();
168 let surface_id = state.surface.id();
169 let client = state.client.clone();
170
171 state.renderer.destroy();
172 if let Some(decoration) = &state.decoration {
173 decoration.destroy();
174 }
175 if let Some(blur) = &state.blur {
176 blur.release();
177 }
178 state.toplevel.destroy();
179 if let Some(viewport) = &state.viewport {
180 viewport.destroy();
181 }
182 state.xdg_surface.destroy();
183 state.surface.destroy();
184
185 let state_ptr = self.0.clone();
186 state
187 .globals
188 .executor
189 .spawn(async move {
190 state_ptr.close();
191 client.drop_window(&surface_id)
192 })
193 .detach();
194 drop(state);
195 }
196}
197
198impl WaylandWindow {
199 fn borrow(&self) -> Ref<WaylandWindowState> {
200 self.0.state.borrow()
201 }
202
203 fn borrow_mut(&self) -> RefMut<WaylandWindowState> {
204 self.0.state.borrow_mut()
205 }
206
207 pub fn new(
208 globals: Globals,
209 client: WaylandClientStatePtr,
210 params: WindowParams,
211 ) -> (Self, ObjectId) {
212 let surface = globals.compositor.create_surface(&globals.qh, ());
213 let xdg_surface = globals
214 .wm_base
215 .get_xdg_surface(&surface, &globals.qh, surface.id());
216 let toplevel = xdg_surface.get_toplevel(&globals.qh, surface.id());
217
218 if let Some(fractional_scale_manager) = globals.fractional_scale_manager.as_ref() {
219 fractional_scale_manager.get_fractional_scale(&surface, &globals.qh, surface.id());
220 }
221
222 // Attempt to set up window decorations based on the requested configuration
223 let decoration = globals
224 .decoration_manager
225 .as_ref()
226 .map(|decoration_manager| {
227 let decoration = decoration_manager.get_toplevel_decoration(
228 &toplevel,
229 &globals.qh,
230 surface.id(),
231 );
232 decoration.set_mode(zxdg_toplevel_decoration_v1::Mode::ClientSide);
233 decoration
234 });
235
236 let viewport = globals
237 .viewporter
238 .as_ref()
239 .map(|viewporter| viewporter.get_viewport(&surface, &globals.qh, ()));
240
241 let this = Self(WaylandWindowStatePtr {
242 state: Rc::new(RefCell::new(WaylandWindowState::new(
243 surface.clone(),
244 xdg_surface,
245 toplevel,
246 decoration,
247 viewport,
248 client,
249 globals,
250 params,
251 ))),
252 callbacks: Rc::new(RefCell::new(Callbacks::default())),
253 });
254
255 // Kick things off
256 surface.commit();
257
258 (this, surface.id())
259 }
260}
261
262impl WaylandWindowStatePtr {
263 pub fn surface(&self) -> wl_surface::WlSurface {
264 self.state.borrow().surface.clone()
265 }
266
267 pub fn ptr_eq(&self, other: &Self) -> bool {
268 Rc::ptr_eq(&self.state, &other.state)
269 }
270
271 pub fn frame(&self, request_frame_callback: bool) {
272 if request_frame_callback {
273 let state = self.state.borrow_mut();
274 state.surface.frame(&state.globals.qh, state.surface.id());
275 drop(state);
276 }
277 let mut cb = self.callbacks.borrow_mut();
278 if let Some(fun) = cb.request_frame.as_mut() {
279 fun();
280 }
281 }
282
283 pub fn handle_xdg_surface_event(&self, event: xdg_surface::Event) {
284 match event {
285 xdg_surface::Event::Configure { serial } => {
286 let mut state = self.state.borrow_mut();
287 state.xdg_surface.ack_configure(serial);
288 let request_frame_callback = !state.acknowledged_first_configure;
289 state.acknowledged_first_configure = true;
290 drop(state);
291 self.frame(request_frame_callback);
292 }
293 _ => {}
294 }
295 }
296
297 pub fn handle_toplevel_decoration_event(&self, event: zxdg_toplevel_decoration_v1::Event) {
298 match event {
299 zxdg_toplevel_decoration_v1::Event::Configure { mode } => match mode {
300 WEnum::Value(zxdg_toplevel_decoration_v1::Mode::ServerSide) => {
301 self.set_decoration_state(WaylandDecorationState::Server)
302 }
303 WEnum::Value(zxdg_toplevel_decoration_v1::Mode::ClientSide) => {
304 self.set_decoration_state(WaylandDecorationState::Client)
305 }
306 WEnum::Value(_) => {
307 log::warn!("Unknown decoration mode");
308 }
309 WEnum::Unknown(v) => {
310 log::warn!("Unknown decoration mode: {}", v);
311 }
312 },
313 _ => {}
314 }
315 }
316
317 pub fn handle_fractional_scale_event(&self, event: wp_fractional_scale_v1::Event) {
318 match event {
319 wp_fractional_scale_v1::Event::PreferredScale { scale } => {
320 self.rescale(scale as f32 / 120.0);
321 }
322 _ => {}
323 }
324 }
325
326 pub fn handle_toplevel_event(&self, event: xdg_toplevel::Event) -> bool {
327 match event {
328 xdg_toplevel::Event::Configure {
329 width,
330 height,
331 states,
332 } => {
333 let width = NonZeroU32::new(width as u32);
334 let height = NonZeroU32::new(height as u32);
335 let fullscreen = states.contains(&(xdg_toplevel::State::Fullscreen as u8));
336 let maximized = states.contains(&(xdg_toplevel::State::Maximized as u8));
337 let mut state = self.state.borrow_mut();
338 state.maximized = maximized;
339 state.fullscreen = fullscreen;
340 if fullscreen || maximized {
341 state.restore_bounds = state.bounds.map(|p| DevicePixels(p as i32));
342 }
343 drop(state);
344 self.resize(width, height);
345 self.set_fullscreen(fullscreen);
346
347 false
348 }
349 xdg_toplevel::Event::Close => {
350 let mut cb = self.callbacks.borrow_mut();
351 if let Some(mut should_close) = cb.should_close.take() {
352 let result = (should_close)();
353 cb.should_close = Some(should_close);
354 if result {
355 drop(cb);
356 self.close();
357 }
358 result
359 } else {
360 true
361 }
362 }
363 _ => false,
364 }
365 }
366
367 pub fn handle_surface_event(
368 &self,
369 event: wl_surface::Event,
370 output_scales: HashMap<ObjectId, i32>,
371 ) {
372 let mut state = self.state.borrow_mut();
373
374 // We use `WpFractionalScale` instead to set the scale if it's available
375 if state.globals.fractional_scale_manager.is_some() {
376 return;
377 }
378
379 match event {
380 wl_surface::Event::Enter { output } => {
381 // We use `PreferredBufferScale` instead to set the scale if it's available
382 if state.surface.version() >= wl_surface::EVT_PREFERRED_BUFFER_SCALE_SINCE {
383 return;
384 }
385
386 state.outputs.insert(output.id());
387
388 let mut scale = 1;
389 for output in state.outputs.iter() {
390 if let Some(s) = output_scales.get(output) {
391 scale = scale.max(*s)
392 }
393 }
394
395 state.surface.set_buffer_scale(scale);
396 drop(state);
397 self.rescale(scale as f32);
398 }
399 wl_surface::Event::Leave { output } => {
400 // We use `PreferredBufferScale` instead to set the scale if it's available
401 if state.surface.version() >= wl_surface::EVT_PREFERRED_BUFFER_SCALE_SINCE {
402 return;
403 }
404
405 state.outputs.remove(&output.id());
406
407 let mut scale = 1;
408 for output in state.outputs.iter() {
409 if let Some(s) = output_scales.get(output) {
410 scale = scale.max(*s)
411 }
412 }
413
414 state.surface.set_buffer_scale(scale);
415 drop(state);
416 self.rescale(scale as f32);
417 }
418 wl_surface::Event::PreferredBufferScale { factor } => {
419 state.surface.set_buffer_scale(factor);
420 drop(state);
421 self.rescale(factor as f32);
422 }
423 _ => {}
424 }
425 }
426
427 pub fn set_size_and_scale(
428 &self,
429 width: Option<NonZeroU32>,
430 height: Option<NonZeroU32>,
431 scale: Option<f32>,
432 ) {
433 let (width, height, scale) = {
434 let mut state = self.state.borrow_mut();
435 if width.map_or(true, |width| width.get() == state.bounds.size.width)
436 && height.map_or(true, |height| height.get() == state.bounds.size.height)
437 && scale.map_or(true, |scale| scale == state.scale)
438 {
439 return;
440 }
441 if let Some(width) = width {
442 state.bounds.size.width = width.get();
443 }
444 if let Some(height) = height {
445 state.bounds.size.height = height.get();
446 }
447 if let Some(scale) = scale {
448 state.scale = scale;
449 }
450 let width = state.bounds.size.width;
451 let height = state.bounds.size.height;
452 let scale = state.scale;
453 state.renderer.update_drawable_size(size(
454 width as f64 * scale as f64,
455 height as f64 * scale as f64,
456 ));
457 (width, height, scale)
458 };
459
460 if let Some(ref mut fun) = self.callbacks.borrow_mut().resize {
461 fun(
462 Size {
463 width: px(width as f32),
464 height: px(height as f32),
465 },
466 scale,
467 );
468 }
469
470 {
471 let state = self.state.borrow();
472 if let Some(viewport) = &state.viewport {
473 viewport.set_destination(width as i32, height as i32);
474 }
475 }
476 }
477
478 pub fn resize(&self, width: Option<NonZeroU32>, height: Option<NonZeroU32>) {
479 self.set_size_and_scale(width, height, None);
480 }
481
482 pub fn rescale(&self, scale: f32) {
483 self.set_size_and_scale(None, None, Some(scale));
484 }
485
486 pub fn set_fullscreen(&self, fullscreen: bool) {
487 let mut state = self.state.borrow_mut();
488 state.fullscreen = fullscreen;
489 }
490
491 /// Notifies the window of the state of the decorations.
492 ///
493 /// # Note
494 ///
495 /// This API is indirectly called by the wayland compositor and
496 /// not meant to be called by a user who wishes to change the state
497 /// of the decorations. This is because the state of the decorations
498 /// is managed by the compositor and not the client.
499 pub fn set_decoration_state(&self, state: WaylandDecorationState) {
500 self.state.borrow_mut().decoration_state = state;
501 }
502
503 pub fn close(&self) {
504 let mut callbacks = self.callbacks.borrow_mut();
505 if let Some(fun) = callbacks.close.take() {
506 fun()
507 }
508 }
509
510 pub fn handle_input(&self, input: PlatformInput) {
511 if let Some(ref mut fun) = self.callbacks.borrow_mut().input {
512 if !fun(input.clone()).propagate {
513 return;
514 }
515 }
516 if let PlatformInput::KeyDown(event) = input {
517 if let Some(ime_key) = &event.keystroke.ime_key {
518 let mut state = self.state.borrow_mut();
519 if let Some(mut input_handler) = state.input_handler.take() {
520 drop(state);
521 input_handler.replace_text_in_range(None, ime_key);
522 self.state.borrow_mut().input_handler = Some(input_handler);
523 }
524 }
525 }
526 }
527
528 pub fn set_focused(&self, focus: bool) {
529 if let Some(ref mut fun) = self.callbacks.borrow_mut().active_status_change {
530 fun(focus);
531 }
532 }
533}
534
535impl rwh::HasWindowHandle for WaylandWindow {
536 fn window_handle(&self) -> Result<rwh::WindowHandle<'_>, rwh::HandleError> {
537 unimplemented!()
538 }
539}
540impl rwh::HasDisplayHandle for WaylandWindow {
541 fn display_handle(&self) -> Result<rwh::DisplayHandle<'_>, rwh::HandleError> {
542 unimplemented!()
543 }
544}
545
546impl PlatformWindow for WaylandWindow {
547 fn bounds(&self) -> Bounds<DevicePixels> {
548 self.borrow().bounds.map(|p| DevicePixels(p as i32))
549 }
550
551 fn is_maximized(&self) -> bool {
552 self.borrow().maximized
553 }
554
555 // todo(linux)
556 // check if it is right
557 fn window_bounds(&self) -> WindowBounds {
558 let state = self.borrow();
559 if state.fullscreen {
560 WindowBounds::Fullscreen(state.restore_bounds)
561 } else if state.maximized {
562 WindowBounds::Maximized(state.restore_bounds)
563 } else {
564 WindowBounds::Windowed(state.bounds.map(|p| DevicePixels(p as i32)))
565 }
566 }
567
568 fn content_size(&self) -> Size<Pixels> {
569 let state = self.borrow();
570 Size {
571 width: Pixels(state.bounds.size.width as f32),
572 height: Pixels(state.bounds.size.height as f32),
573 }
574 }
575
576 fn scale_factor(&self) -> f32 {
577 self.borrow().scale
578 }
579
580 // todo(linux)
581 fn appearance(&self) -> WindowAppearance {
582 WindowAppearance::Light
583 }
584
585 // todo(linux)
586 fn display(&self) -> Rc<dyn PlatformDisplay> {
587 Rc::new(WaylandDisplay {})
588 }
589
590 // todo(linux)
591 fn mouse_position(&self) -> Point<Pixels> {
592 Point::default()
593 }
594
595 // todo(linux)
596 fn modifiers(&self) -> Modifiers {
597 crate::Modifiers::default()
598 }
599
600 fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
601 self.borrow_mut().input_handler = Some(input_handler);
602 }
603
604 fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
605 self.borrow_mut().input_handler.take()
606 }
607
608 fn prompt(
609 &self,
610 level: PromptLevel,
611 msg: &str,
612 detail: Option<&str>,
613 answers: &[&str],
614 ) -> Option<Receiver<usize>> {
615 None
616 }
617
618 fn activate(&self) {
619 // todo(linux)
620 }
621
622 // todo(linux)
623 fn is_active(&self) -> bool {
624 false
625 }
626
627 fn set_title(&mut self, title: &str) {
628 self.borrow().toplevel.set_title(title.to_string());
629 }
630
631 fn set_app_id(&mut self, app_id: &str) {
632 self.borrow().toplevel.set_app_id(app_id.to_owned());
633 }
634
635 fn set_background_appearance(&mut self, background_appearance: WindowBackgroundAppearance) {
636 let opaque = background_appearance == WindowBackgroundAppearance::Opaque;
637 let mut state = self.borrow_mut();
638 state.renderer.update_transparency(!opaque);
639
640 let region = state
641 .globals
642 .compositor
643 .create_region(&state.globals.qh, ());
644 region.add(0, 0, i32::MAX, i32::MAX);
645
646 if opaque {
647 // Promise the compositor that this region of the window surface
648 // contains no transparent pixels. This allows the compositor to
649 // do skip whatever is behind the surface for better performance.
650 state.surface.set_opaque_region(Some(®ion));
651 } else {
652 state.surface.set_opaque_region(None);
653 }
654
655 if let Some(ref blur_manager) = state.globals.blur_manager {
656 if (background_appearance == WindowBackgroundAppearance::Blurred) {
657 if (state.blur.is_none()) {
658 let blur = blur_manager.create(&state.surface, &state.globals.qh, ());
659 blur.set_region(Some(®ion));
660 state.blur = Some(blur);
661 }
662 state.blur.as_ref().unwrap().commit();
663 } else {
664 // It probably doesn't hurt to clear the blur for opaque windows
665 blur_manager.unset(&state.surface);
666 if let Some(b) = state.blur.take() {
667 b.release()
668 }
669 }
670 }
671
672 region.destroy();
673 }
674
675 fn set_edited(&mut self, edited: bool) {
676 // todo(linux)
677 }
678
679 fn show_character_palette(&self) {
680 // todo(linux)
681 }
682
683 fn minimize(&self) {
684 self.borrow().toplevel.set_minimized();
685 }
686
687 fn zoom(&self) {
688 let state = self.borrow();
689 if !state.maximized {
690 state.toplevel.set_maximized();
691 } else {
692 state.toplevel.unset_maximized();
693 }
694 }
695
696 fn toggle_fullscreen(&self) {
697 let mut state = self.borrow_mut();
698 state.restore_bounds = state.bounds.map(|p| DevicePixels(p as i32));
699 if !state.fullscreen {
700 state.toplevel.set_fullscreen(None);
701 } else {
702 state.toplevel.unset_fullscreen();
703 }
704 }
705
706 fn is_fullscreen(&self) -> bool {
707 self.borrow().fullscreen
708 }
709
710 fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
711 self.0.callbacks.borrow_mut().request_frame = Some(callback);
712 }
713
714 fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> crate::DispatchEventResult>) {
715 self.0.callbacks.borrow_mut().input = Some(callback);
716 }
717
718 fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
719 self.0.callbacks.borrow_mut().active_status_change = Some(callback);
720 }
721
722 fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
723 self.0.callbacks.borrow_mut().resize = Some(callback);
724 }
725
726 fn on_moved(&self, callback: Box<dyn FnMut()>) {
727 self.0.callbacks.borrow_mut().moved = Some(callback);
728 }
729
730 fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>) {
731 self.0.callbacks.borrow_mut().should_close = Some(callback);
732 }
733
734 fn on_close(&self, callback: Box<dyn FnOnce()>) {
735 self.0.callbacks.borrow_mut().close = Some(callback);
736 }
737
738 fn on_appearance_changed(&self, callback: Box<dyn FnMut()>) {
739 // todo(linux)
740 }
741
742 fn draw(&self, scene: &Scene) {
743 let mut state = self.borrow_mut();
744 state.renderer.draw(scene);
745 }
746
747 fn completed_frame(&self) {
748 let mut state = self.borrow_mut();
749 state.surface.commit();
750 }
751
752 fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
753 let state = self.borrow();
754 state.renderer.sprite_atlas().clone()
755 }
756}
757
758#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
759pub enum WaylandDecorationState {
760 /// Decorations are to be provided by the client
761 Client,
762
763 /// Decorations are provided by the server
764 Server,
765}