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