1use std::cell::{Ref, RefCell, RefMut};
2use std::ffi::c_void;
3use std::ptr::NonNull;
4use std::rc::Rc;
5use std::sync::Arc;
6
7use blade_graphics as gpu;
8use collections::HashMap;
9use futures::channel::oneshot::Receiver;
10
11use raw_window_handle as rwh;
12use wayland_backend::client::ObjectId;
13use wayland_client::WEnum;
14use wayland_client::{protocol::wl_surface, Proxy};
15use wayland_protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1;
16use wayland_protocols::wp::viewporter::client::wp_viewport;
17use wayland_protocols::xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1;
18use wayland_protocols::xdg::shell::client::xdg_surface;
19use wayland_protocols::xdg::shell::client::xdg_toplevel::{self};
20use wayland_protocols_plasma::blur::client::org_kde_kwin_blur;
21
22use crate::platform::blade::{BladeRenderer, BladeSurfaceConfig};
23use crate::platform::linux::wayland::display::WaylandDisplay;
24use crate::platform::linux::wayland::serial::SerialKind;
25use crate::platform::{PlatformAtlas, PlatformInputHandler, PlatformWindow};
26use crate::scene::Scene;
27use crate::{
28 px, size, AnyWindowHandle, Bounds, Decorations, Globals, Modifiers, Output, Pixels,
29 PlatformDisplay, PlatformInput, Point, PromptLevel, ResizeEdge, Size, Tiling,
30 WaylandClientStatePtr, WindowAppearance, WindowBackgroundAppearance, WindowBounds,
31 WindowControls, WindowDecorations, 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
66#[derive(Debug)]
67struct InProgressConfigure {
68 size: Option<Size<Pixels>>,
69 fullscreen: bool,
70 maximized: bool,
71 tiling: Tiling,
72}
73
74pub struct WaylandWindowState {
75 xdg_surface: xdg_surface::XdgSurface,
76 acknowledged_first_configure: bool,
77 pub surface: wl_surface::WlSurface,
78 decoration: Option<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1>,
79 appearance: WindowAppearance,
80 blur: Option<org_kde_kwin_blur::OrgKdeKwinBlur>,
81 toplevel: xdg_toplevel::XdgToplevel,
82 viewport: Option<wp_viewport::WpViewport>,
83 outputs: HashMap<ObjectId, Output>,
84 display: Option<(ObjectId, Output)>,
85 globals: Globals,
86 renderer: BladeRenderer,
87 bounds: Bounds<Pixels>,
88 scale: f32,
89 input_handler: Option<PlatformInputHandler>,
90 decorations: WindowDecorations,
91 background_appearance: WindowBackgroundAppearance,
92 fullscreen: bool,
93 maximized: bool,
94 tiling: Tiling,
95 window_bounds: Bounds<Pixels>,
96 client: WaylandClientStatePtr,
97 handle: AnyWindowHandle,
98 active: bool,
99 in_progress_configure: Option<InProgressConfigure>,
100 in_progress_window_controls: Option<WindowControls>,
101 window_controls: WindowControls,
102 inset: Option<Pixels>,
103}
104
105#[derive(Clone)]
106pub struct WaylandWindowStatePtr {
107 state: Rc<RefCell<WaylandWindowState>>,
108 callbacks: Rc<RefCell<Callbacks>>,
109}
110
111impl WaylandWindowState {
112 #[allow(clippy::too_many_arguments)]
113 pub(crate) fn new(
114 handle: AnyWindowHandle,
115 surface: wl_surface::WlSurface,
116 xdg_surface: xdg_surface::XdgSurface,
117 toplevel: xdg_toplevel::XdgToplevel,
118 decoration: Option<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1>,
119 appearance: WindowAppearance,
120 viewport: Option<wp_viewport::WpViewport>,
121 client: WaylandClientStatePtr,
122 globals: Globals,
123 options: WindowParams,
124 ) -> anyhow::Result<Self> {
125 let raw = RawWindow {
126 window: surface.id().as_ptr().cast::<c_void>(),
127 display: surface
128 .backend()
129 .upgrade()
130 .unwrap()
131 .display_ptr()
132 .cast::<c_void>(),
133 };
134 let gpu = Arc::new(
135 unsafe {
136 gpu::Context::init_windowed(
137 &raw,
138 gpu::ContextDesc {
139 validation: false,
140 capture: false,
141 overlay: false,
142 },
143 )
144 }
145 .map_err(|e| anyhow::anyhow!("{:?}", e))?,
146 );
147 let config = BladeSurfaceConfig {
148 size: gpu::Extent {
149 width: options.bounds.size.width.0 as u32,
150 height: options.bounds.size.height.0 as u32,
151 depth: 1,
152 },
153 transparent: true,
154 };
155
156 Ok(Self {
157 xdg_surface,
158 acknowledged_first_configure: false,
159 surface,
160 decoration,
161 blur: None,
162 toplevel,
163 viewport,
164 globals,
165 outputs: HashMap::default(),
166 display: None,
167 renderer: BladeRenderer::new(gpu, config),
168 bounds: options.bounds,
169 scale: 1.0,
170 input_handler: None,
171 decorations: WindowDecorations::Client,
172 background_appearance: WindowBackgroundAppearance::Opaque,
173 fullscreen: false,
174 maximized: false,
175 tiling: Tiling::default(),
176 window_bounds: options.bounds,
177 in_progress_configure: None,
178 client,
179 appearance,
180 handle,
181 active: false,
182 in_progress_window_controls: None,
183 // Assume that we can do anything, unless told otherwise
184 window_controls: WindowControls {
185 fullscreen: true,
186 maximize: true,
187 minimize: true,
188 window_menu: true,
189 },
190 inset: None,
191 })
192 }
193
194 pub fn is_transparent(&self) -> bool {
195 self.decorations == WindowDecorations::Client
196 || self.background_appearance != WindowBackgroundAppearance::Opaque
197 }
198}
199
200pub(crate) struct WaylandWindow(pub WaylandWindowStatePtr);
201pub enum ImeInput {
202 InsertText(String),
203 SetMarkedText(String),
204 UnmarkText,
205 DeleteText,
206}
207
208impl Drop for WaylandWindow {
209 fn drop(&mut self) {
210 let mut state = self.0.state.borrow_mut();
211 let surface_id = state.surface.id();
212 let client = state.client.clone();
213
214 state.renderer.destroy();
215 if let Some(decoration) = &state.decoration {
216 decoration.destroy();
217 }
218 if let Some(blur) = &state.blur {
219 blur.release();
220 }
221 state.toplevel.destroy();
222 if let Some(viewport) = &state.viewport {
223 viewport.destroy();
224 }
225 state.xdg_surface.destroy();
226 state.surface.destroy();
227
228 let state_ptr = self.0.clone();
229 state
230 .globals
231 .executor
232 .spawn(async move {
233 state_ptr.close();
234 client.drop_window(&surface_id)
235 })
236 .detach();
237 drop(state);
238 }
239}
240
241impl WaylandWindow {
242 fn borrow(&self) -> Ref<WaylandWindowState> {
243 self.0.state.borrow()
244 }
245
246 fn borrow_mut(&self) -> RefMut<WaylandWindowState> {
247 self.0.state.borrow_mut()
248 }
249
250 pub fn new(
251 handle: AnyWindowHandle,
252 globals: Globals,
253 client: WaylandClientStatePtr,
254 params: WindowParams,
255 appearance: WindowAppearance,
256 ) -> anyhow::Result<(Self, ObjectId)> {
257 let surface = globals.compositor.create_surface(&globals.qh, ());
258 let xdg_surface = globals
259 .wm_base
260 .get_xdg_surface(&surface, &globals.qh, surface.id());
261 let toplevel = xdg_surface.get_toplevel(&globals.qh, surface.id());
262 toplevel.set_min_size(50, 50);
263
264 if let Some(fractional_scale_manager) = globals.fractional_scale_manager.as_ref() {
265 fractional_scale_manager.get_fractional_scale(&surface, &globals.qh, surface.id());
266 }
267
268 // Attempt to set up window decorations based on the requested configuration
269 let decoration = globals
270 .decoration_manager
271 .as_ref()
272 .map(|decoration_manager| {
273 decoration_manager.get_toplevel_decoration(&toplevel, &globals.qh, surface.id())
274 });
275
276 let viewport = globals
277 .viewporter
278 .as_ref()
279 .map(|viewporter| viewporter.get_viewport(&surface, &globals.qh, ()));
280
281 let this = Self(WaylandWindowStatePtr {
282 state: Rc::new(RefCell::new(WaylandWindowState::new(
283 handle,
284 surface.clone(),
285 xdg_surface,
286 toplevel,
287 decoration,
288 appearance,
289 viewport,
290 client,
291 globals,
292 params,
293 )?)),
294 callbacks: Rc::new(RefCell::new(Callbacks::default())),
295 });
296
297 // Kick things off
298 surface.commit();
299
300 Ok((this, surface.id()))
301 }
302}
303
304impl WaylandWindowStatePtr {
305 pub fn handle(&self) -> AnyWindowHandle {
306 self.state.borrow().handle
307 }
308
309 pub fn surface(&self) -> wl_surface::WlSurface {
310 self.state.borrow().surface.clone()
311 }
312
313 pub fn ptr_eq(&self, other: &Self) -> bool {
314 Rc::ptr_eq(&self.state, &other.state)
315 }
316
317 pub fn frame(&self) {
318 let mut state = self.state.borrow_mut();
319 state.surface.frame(&state.globals.qh, state.surface.id());
320 drop(state);
321
322 let mut cb = self.callbacks.borrow_mut();
323 if let Some(fun) = cb.request_frame.as_mut() {
324 fun();
325 }
326 }
327
328 pub fn handle_xdg_surface_event(&self, event: xdg_surface::Event) {
329 match event {
330 xdg_surface::Event::Configure { serial } => {
331 {
332 let mut state = self.state.borrow_mut();
333 if let Some(window_controls) = state.in_progress_window_controls.take() {
334 state.window_controls = window_controls;
335
336 drop(state);
337 let mut callbacks = self.callbacks.borrow_mut();
338 if let Some(appearance_changed) = callbacks.appearance_changed.as_mut() {
339 appearance_changed();
340 }
341 }
342 }
343 {
344 let mut state = self.state.borrow_mut();
345
346 if let Some(mut configure) = state.in_progress_configure.take() {
347 let got_unmaximized = state.maximized && !configure.maximized;
348 state.fullscreen = configure.fullscreen;
349 state.maximized = configure.maximized;
350 state.tiling = configure.tiling;
351 if !configure.fullscreen && !configure.maximized {
352 configure.size = if got_unmaximized {
353 Some(state.window_bounds.size)
354 } else {
355 compute_outer_size(state.inset, configure.size, state.tiling)
356 };
357 if let Some(size) = configure.size {
358 state.window_bounds = Bounds {
359 origin: Point::default(),
360 size,
361 };
362 }
363 }
364 drop(state);
365 if let Some(size) = configure.size {
366 self.resize(size);
367 }
368 }
369 }
370 let mut state = self.state.borrow_mut();
371 state.xdg_surface.ack_configure(serial);
372
373 let window_geometry = inset_by_tiling(
374 state.bounds.map_origin(|_| px(0.0)),
375 state.inset.unwrap_or(px(0.0)),
376 state.tiling,
377 )
378 .map(|v| v.0 as i32)
379 .map_size(|v| if v <= 0 { 1 } else { v });
380
381 state.xdg_surface.set_window_geometry(
382 window_geometry.origin.x,
383 window_geometry.origin.y,
384 window_geometry.size.width,
385 window_geometry.size.height,
386 );
387
388 let request_frame_callback = !state.acknowledged_first_configure;
389 if request_frame_callback {
390 state.acknowledged_first_configure = true;
391 drop(state);
392 self.frame();
393 }
394 }
395 _ => {}
396 }
397 }
398
399 pub fn handle_toplevel_decoration_event(&self, event: zxdg_toplevel_decoration_v1::Event) {
400 match event {
401 zxdg_toplevel_decoration_v1::Event::Configure { mode } => match mode {
402 WEnum::Value(zxdg_toplevel_decoration_v1::Mode::ServerSide) => {
403 self.state.borrow_mut().decorations = WindowDecorations::Server;
404 if let Some(mut appearance_changed) =
405 self.callbacks.borrow_mut().appearance_changed.as_mut()
406 {
407 appearance_changed();
408 }
409 }
410 WEnum::Value(zxdg_toplevel_decoration_v1::Mode::ClientSide) => {
411 self.state.borrow_mut().decorations = WindowDecorations::Client;
412 // Update background to be transparent
413 if let Some(mut appearance_changed) =
414 self.callbacks.borrow_mut().appearance_changed.as_mut()
415 {
416 appearance_changed();
417 }
418 }
419 WEnum::Value(_) => {
420 log::warn!("Unknown decoration mode");
421 }
422 WEnum::Unknown(v) => {
423 log::warn!("Unknown decoration mode: {}", v);
424 }
425 },
426 _ => {}
427 }
428 }
429
430 pub fn handle_fractional_scale_event(&self, event: wp_fractional_scale_v1::Event) {
431 match event {
432 wp_fractional_scale_v1::Event::PreferredScale { scale } => {
433 self.rescale(scale as f32 / 120.0);
434 }
435 _ => {}
436 }
437 }
438
439 pub fn handle_toplevel_event(&self, event: xdg_toplevel::Event) -> bool {
440 match event {
441 xdg_toplevel::Event::Configure {
442 width,
443 height,
444 states,
445 } => {
446 let mut size = if width == 0 || height == 0 {
447 None
448 } else {
449 Some(size(px(width as f32), px(height as f32)))
450 };
451
452 let states = extract_states::<xdg_toplevel::State>(&states);
453
454 let mut tiling = Tiling::default();
455 let mut fullscreen = false;
456 let mut maximized = false;
457
458 for state in states {
459 match state {
460 xdg_toplevel::State::Maximized => {
461 maximized = true;
462 }
463 xdg_toplevel::State::Fullscreen => {
464 fullscreen = true;
465 }
466 xdg_toplevel::State::TiledTop => {
467 tiling.top = true;
468 }
469 xdg_toplevel::State::TiledLeft => {
470 tiling.left = true;
471 }
472 xdg_toplevel::State::TiledRight => {
473 tiling.right = true;
474 }
475 xdg_toplevel::State::TiledBottom => {
476 tiling.bottom = true;
477 }
478 _ => {
479 // noop
480 }
481 }
482 }
483
484 if fullscreen || maximized {
485 tiling = Tiling::tiled();
486 }
487
488 let mut state = self.state.borrow_mut();
489 state.in_progress_configure = Some(InProgressConfigure {
490 size,
491 fullscreen,
492 maximized,
493 tiling,
494 });
495
496 false
497 }
498 xdg_toplevel::Event::Close => {
499 let mut cb = self.callbacks.borrow_mut();
500 if let Some(mut should_close) = cb.should_close.take() {
501 let result = (should_close)();
502 cb.should_close = Some(should_close);
503 if result {
504 drop(cb);
505 self.close();
506 }
507 result
508 } else {
509 true
510 }
511 }
512 xdg_toplevel::Event::WmCapabilities { capabilities } => {
513 let mut window_controls = WindowControls::default();
514
515 let states = extract_states::<xdg_toplevel::WmCapabilities>(&capabilities);
516
517 for state in states {
518 match state {
519 xdg_toplevel::WmCapabilities::Maximize => {
520 window_controls.maximize = true;
521 }
522 xdg_toplevel::WmCapabilities::Minimize => {
523 window_controls.minimize = true;
524 }
525 xdg_toplevel::WmCapabilities::Fullscreen => {
526 window_controls.fullscreen = true;
527 }
528 xdg_toplevel::WmCapabilities::WindowMenu => {
529 window_controls.window_menu = true;
530 }
531 _ => {}
532 }
533 }
534
535 let mut state = self.state.borrow_mut();
536 state.in_progress_window_controls = Some(window_controls);
537 false
538 }
539 _ => false,
540 }
541 }
542
543 pub fn handle_surface_event(
544 &self,
545 event: wl_surface::Event,
546 outputs: HashMap<ObjectId, Output>,
547 ) {
548 let mut state = self.state.borrow_mut();
549
550 match event {
551 wl_surface::Event::Enter { output } => {
552 let id = output.id();
553
554 let Some(output) = outputs.get(&id) else {
555 return;
556 };
557
558 state.outputs.insert(id, output.clone());
559
560 let scale = primary_output_scale(&mut state);
561
562 // We use `PreferredBufferScale` instead to set the scale if it's available
563 if state.surface.version() < wl_surface::EVT_PREFERRED_BUFFER_SCALE_SINCE {
564 state.surface.set_buffer_scale(scale);
565 drop(state);
566 self.rescale(scale as f32);
567 }
568 }
569 wl_surface::Event::Leave { output } => {
570 state.outputs.remove(&output.id());
571
572 let scale = primary_output_scale(&mut state);
573
574 // We use `PreferredBufferScale` instead to set the scale if it's available
575 if state.surface.version() < wl_surface::EVT_PREFERRED_BUFFER_SCALE_SINCE {
576 state.surface.set_buffer_scale(scale);
577 drop(state);
578 self.rescale(scale as f32);
579 }
580 }
581 wl_surface::Event::PreferredBufferScale { factor } => {
582 // We use `WpFractionalScale` instead to set the scale if it's available
583 if state.globals.fractional_scale_manager.is_none() {
584 state.surface.set_buffer_scale(factor);
585 drop(state);
586 self.rescale(factor as f32);
587 }
588 }
589 _ => {}
590 }
591 }
592
593 pub fn handle_ime(&self, ime: ImeInput) {
594 let mut state = self.state.borrow_mut();
595 if let Some(mut input_handler) = state.input_handler.take() {
596 drop(state);
597 match ime {
598 ImeInput::InsertText(text) => {
599 input_handler.replace_text_in_range(None, &text);
600 }
601 ImeInput::SetMarkedText(text) => {
602 input_handler.replace_and_mark_text_in_range(None, &text, None);
603 }
604 ImeInput::UnmarkText => {
605 input_handler.unmark_text();
606 }
607 ImeInput::DeleteText => {
608 if let Some(marked) = input_handler.marked_text_range() {
609 input_handler.replace_text_in_range(Some(marked), "");
610 }
611 }
612 }
613 self.state.borrow_mut().input_handler = Some(input_handler);
614 }
615 }
616
617 pub fn get_ime_area(&self) -> Option<Bounds<Pixels>> {
618 let mut state = self.state.borrow_mut();
619 let mut bounds: Option<Bounds<Pixels>> = None;
620 if let Some(mut input_handler) = state.input_handler.take() {
621 drop(state);
622 if let Some(range) = input_handler.selected_text_range() {
623 bounds = input_handler.bounds_for_range(range);
624 }
625 self.state.borrow_mut().input_handler = Some(input_handler);
626 }
627 bounds
628 }
629
630 pub fn set_size_and_scale(&self, size: Option<Size<Pixels>>, scale: Option<f32>) {
631 let (size, scale) = {
632 let mut state = self.state.borrow_mut();
633 if size.map_or(true, |size| size == state.bounds.size)
634 && scale.map_or(true, |scale| scale == state.scale)
635 {
636 return;
637 }
638 if let Some(size) = size {
639 state.bounds.size = size;
640 }
641 if let Some(scale) = scale {
642 state.scale = scale;
643 }
644 let device_bounds = state.bounds.to_device_pixels(state.scale);
645 state.renderer.update_drawable_size(device_bounds.size);
646 (state.bounds.size, state.scale)
647 };
648
649 if let Some(ref mut fun) = self.callbacks.borrow_mut().resize {
650 fun(size, scale);
651 }
652
653 {
654 let state = self.state.borrow();
655 if let Some(viewport) = &state.viewport {
656 viewport.set_destination(size.width.0 as i32, size.height.0 as i32);
657 }
658 }
659 }
660
661 pub fn resize(&self, size: Size<Pixels>) {
662 self.set_size_and_scale(Some(size), None);
663 }
664
665 pub fn rescale(&self, scale: f32) {
666 self.set_size_and_scale(None, Some(scale));
667 }
668
669 pub fn close(&self) {
670 let mut callbacks = self.callbacks.borrow_mut();
671 if let Some(fun) = callbacks.close.take() {
672 fun()
673 }
674 }
675
676 pub fn handle_input(&self, input: PlatformInput) {
677 if let Some(ref mut fun) = self.callbacks.borrow_mut().input {
678 if !fun(input.clone()).propagate {
679 return;
680 }
681 }
682 if let PlatformInput::KeyDown(event) = input {
683 if let Some(ime_key) = &event.keystroke.ime_key {
684 let mut state = self.state.borrow_mut();
685 if let Some(mut input_handler) = state.input_handler.take() {
686 drop(state);
687 input_handler.replace_text_in_range(None, ime_key);
688 self.state.borrow_mut().input_handler = Some(input_handler);
689 }
690 }
691 }
692 }
693
694 pub fn set_focused(&self, focus: bool) {
695 self.state.borrow_mut().active = focus;
696 if let Some(ref mut fun) = self.callbacks.borrow_mut().active_status_change {
697 fun(focus);
698 }
699 }
700
701 pub fn set_appearance(&mut self, appearance: WindowAppearance) {
702 self.state.borrow_mut().appearance = appearance;
703
704 let mut callbacks = self.callbacks.borrow_mut();
705 if let Some(ref mut fun) = callbacks.appearance_changed {
706 (fun)()
707 }
708 }
709}
710
711fn extract_states<'a, S: TryFrom<u32> + 'a>(states: &'a [u8]) -> impl Iterator<Item = S> + 'a
712where
713 <S as TryFrom<u32>>::Error: 'a,
714{
715 states
716 .chunks_exact(4)
717 .flat_map(TryInto::<[u8; 4]>::try_into)
718 .map(u32::from_ne_bytes)
719 .flat_map(S::try_from)
720}
721
722fn primary_output_scale(state: &mut RefMut<WaylandWindowState>) -> i32 {
723 let mut scale = 1;
724 let mut current_output = state.display.take();
725 for (id, output) in state.outputs.iter() {
726 if let Some((_, output_data)) = ¤t_output {
727 if output.scale > output_data.scale {
728 current_output = Some((id.clone(), output.clone()));
729 }
730 } else {
731 current_output = Some((id.clone(), output.clone()));
732 }
733 scale = scale.max(output.scale);
734 }
735 state.display = current_output;
736 scale
737}
738
739impl rwh::HasWindowHandle for WaylandWindow {
740 fn window_handle(&self) -> Result<rwh::WindowHandle<'_>, rwh::HandleError> {
741 unimplemented!()
742 }
743}
744impl rwh::HasDisplayHandle for WaylandWindow {
745 fn display_handle(&self) -> Result<rwh::DisplayHandle<'_>, rwh::HandleError> {
746 unimplemented!()
747 }
748}
749
750impl PlatformWindow for WaylandWindow {
751 fn bounds(&self) -> Bounds<Pixels> {
752 self.borrow().bounds
753 }
754
755 fn is_maximized(&self) -> bool {
756 self.borrow().maximized
757 }
758
759 fn window_bounds(&self) -> WindowBounds {
760 let state = self.borrow();
761 if state.fullscreen {
762 WindowBounds::Fullscreen(state.window_bounds)
763 } else if state.maximized {
764 WindowBounds::Maximized(state.window_bounds)
765 } else {
766 drop(state);
767 WindowBounds::Windowed(self.bounds())
768 }
769 }
770
771 fn content_size(&self) -> Size<Pixels> {
772 self.borrow().bounds.size
773 }
774
775 fn scale_factor(&self) -> f32 {
776 self.borrow().scale
777 }
778
779 fn appearance(&self) -> WindowAppearance {
780 self.borrow().appearance
781 }
782
783 fn display(&self) -> Option<Rc<dyn PlatformDisplay>> {
784 let state = self.borrow();
785 state.display.as_ref().map(|(id, display)| {
786 Rc::new(WaylandDisplay {
787 id: id.clone(),
788 name: display.name.clone(),
789 bounds: display.bounds.to_pixels(state.scale),
790 }) as Rc<dyn PlatformDisplay>
791 })
792 }
793
794 fn mouse_position(&self) -> Point<Pixels> {
795 self.borrow()
796 .client
797 .get_client()
798 .borrow()
799 .mouse_location
800 .unwrap_or_default()
801 }
802
803 fn modifiers(&self) -> Modifiers {
804 self.borrow().client.get_client().borrow().modifiers
805 }
806
807 fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
808 self.borrow_mut().input_handler = Some(input_handler);
809 }
810
811 fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
812 self.borrow_mut().input_handler.take()
813 }
814
815 fn prompt(
816 &self,
817 _level: PromptLevel,
818 _msg: &str,
819 _detail: Option<&str>,
820 _answers: &[&str],
821 ) -> Option<Receiver<usize>> {
822 None
823 }
824
825 fn activate(&self) {
826 log::info!("Wayland does not support this API");
827 }
828
829 fn is_active(&self) -> bool {
830 self.borrow().active
831 }
832
833 fn set_title(&mut self, title: &str) {
834 self.borrow().toplevel.set_title(title.to_string());
835 }
836
837 fn set_app_id(&mut self, app_id: &str) {
838 self.borrow().toplevel.set_app_id(app_id.to_owned());
839 }
840
841 fn set_background_appearance(&self, background_appearance: WindowBackgroundAppearance) {
842 let mut state = self.borrow_mut();
843 state.background_appearance = background_appearance;
844 update_window(state);
845 }
846
847 fn minimize(&self) {
848 self.borrow().toplevel.set_minimized();
849 }
850
851 fn zoom(&self) {
852 let state = self.borrow();
853 if !state.maximized {
854 state.toplevel.set_maximized();
855 } else {
856 state.toplevel.unset_maximized();
857 }
858 }
859
860 fn toggle_fullscreen(&self) {
861 let mut state = self.borrow_mut();
862 if !state.fullscreen {
863 state.toplevel.set_fullscreen(None);
864 } else {
865 state.toplevel.unset_fullscreen();
866 }
867 }
868
869 fn is_fullscreen(&self) -> bool {
870 self.borrow().fullscreen
871 }
872
873 fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
874 self.0.callbacks.borrow_mut().request_frame = Some(callback);
875 }
876
877 fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> crate::DispatchEventResult>) {
878 self.0.callbacks.borrow_mut().input = Some(callback);
879 }
880
881 fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
882 self.0.callbacks.borrow_mut().active_status_change = Some(callback);
883 }
884
885 fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
886 self.0.callbacks.borrow_mut().resize = Some(callback);
887 }
888
889 fn on_moved(&self, callback: Box<dyn FnMut()>) {
890 self.0.callbacks.borrow_mut().moved = Some(callback);
891 }
892
893 fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>) {
894 self.0.callbacks.borrow_mut().should_close = Some(callback);
895 }
896
897 fn on_close(&self, callback: Box<dyn FnOnce()>) {
898 self.0.callbacks.borrow_mut().close = Some(callback);
899 }
900
901 fn on_appearance_changed(&self, callback: Box<dyn FnMut()>) {
902 self.0.callbacks.borrow_mut().appearance_changed = Some(callback);
903 }
904
905 fn draw(&self, scene: &Scene) {
906 let mut state = self.borrow_mut();
907 state.renderer.draw(scene);
908 }
909
910 fn completed_frame(&self) {
911 let state = self.borrow();
912 state.surface.commit();
913 }
914
915 fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
916 let state = self.borrow();
917 state.renderer.sprite_atlas().clone()
918 }
919
920 fn show_window_menu(&self, position: Point<Pixels>) {
921 let state = self.borrow();
922 let serial = state.client.get_serial(SerialKind::MousePress);
923 state.toplevel.show_window_menu(
924 &state.globals.seat,
925 serial,
926 position.x.0 as i32,
927 position.y.0 as i32,
928 );
929 }
930
931 fn start_window_move(&self) {
932 let state = self.borrow();
933 let serial = state.client.get_serial(SerialKind::MousePress);
934 state.toplevel._move(&state.globals.seat, serial);
935 }
936
937 fn start_window_resize(&self, edge: crate::ResizeEdge) {
938 let state = self.borrow();
939 state.toplevel.resize(
940 &state.globals.seat,
941 state.client.get_serial(SerialKind::MousePress),
942 edge.to_xdg(),
943 )
944 }
945
946 fn window_decorations(&self) -> Decorations {
947 let state = self.borrow();
948 match state.decorations {
949 WindowDecorations::Server => Decorations::Server,
950 WindowDecorations::Client => Decorations::Client {
951 tiling: state.tiling,
952 },
953 }
954 }
955
956 fn request_decorations(&self, decorations: WindowDecorations) {
957 let mut state = self.borrow_mut();
958 state.decorations = decorations;
959 if let Some(decoration) = state.decoration.as_ref() {
960 decoration.set_mode(decorations.to_xdg());
961 update_window(state);
962 }
963 }
964
965 fn window_controls(&self) -> WindowControls {
966 self.borrow().window_controls
967 }
968
969 fn set_client_inset(&self, inset: Pixels) {
970 let mut state = self.borrow_mut();
971 if Some(inset) != state.inset {
972 state.inset = Some(inset);
973 update_window(state);
974 }
975 }
976}
977
978fn update_window(mut state: RefMut<WaylandWindowState>) {
979 let opaque = !state.is_transparent();
980
981 state.renderer.update_transparency(!opaque);
982 let mut opaque_area = state.window_bounds.map(|v| v.0 as i32);
983 if let Some(inset) = state.inset {
984 opaque_area.inset(inset.0 as i32);
985 }
986
987 let region = state
988 .globals
989 .compositor
990 .create_region(&state.globals.qh, ());
991 region.add(
992 opaque_area.origin.x,
993 opaque_area.origin.y,
994 opaque_area.size.width,
995 opaque_area.size.height,
996 );
997
998 // Note that rounded corners make this rectangle API hard to work with.
999 // As this is common when using CSD, let's just disable this API.
1000 if state.background_appearance == WindowBackgroundAppearance::Opaque
1001 && state.decorations == WindowDecorations::Server
1002 {
1003 // Promise the compositor that this region of the window surface
1004 // contains no transparent pixels. This allows the compositor to
1005 // do skip whatever is behind the surface for better performance.
1006 state.surface.set_opaque_region(Some(®ion));
1007 } else {
1008 state.surface.set_opaque_region(None);
1009 }
1010
1011 if let Some(ref blur_manager) = state.globals.blur_manager {
1012 if state.background_appearance == WindowBackgroundAppearance::Blurred {
1013 if state.blur.is_none() {
1014 let blur = blur_manager.create(&state.surface, &state.globals.qh, ());
1015 blur.set_region(Some(®ion));
1016 state.blur = Some(blur);
1017 }
1018 state.blur.as_ref().unwrap().commit();
1019 } else {
1020 // It probably doesn't hurt to clear the blur for opaque windows
1021 blur_manager.unset(&state.surface);
1022 if let Some(b) = state.blur.take() {
1023 b.release()
1024 }
1025 }
1026 }
1027
1028 region.destroy();
1029}
1030
1031impl WindowDecorations {
1032 fn to_xdg(&self) -> zxdg_toplevel_decoration_v1::Mode {
1033 match self {
1034 WindowDecorations::Client => zxdg_toplevel_decoration_v1::Mode::ClientSide,
1035 WindowDecorations::Server => zxdg_toplevel_decoration_v1::Mode::ServerSide,
1036 }
1037 }
1038}
1039
1040impl ResizeEdge {
1041 fn to_xdg(&self) -> xdg_toplevel::ResizeEdge {
1042 match self {
1043 ResizeEdge::Top => xdg_toplevel::ResizeEdge::Top,
1044 ResizeEdge::TopRight => xdg_toplevel::ResizeEdge::TopRight,
1045 ResizeEdge::Right => xdg_toplevel::ResizeEdge::Right,
1046 ResizeEdge::BottomRight => xdg_toplevel::ResizeEdge::BottomRight,
1047 ResizeEdge::Bottom => xdg_toplevel::ResizeEdge::Bottom,
1048 ResizeEdge::BottomLeft => xdg_toplevel::ResizeEdge::BottomLeft,
1049 ResizeEdge::Left => xdg_toplevel::ResizeEdge::Left,
1050 ResizeEdge::TopLeft => xdg_toplevel::ResizeEdge::TopLeft,
1051 }
1052 }
1053}
1054
1055/// The configuration event is in terms of the window geometry, which we are constantly
1056/// updating to account for the client decorations. But that's not the area we want to render
1057/// to, due to our intrusize CSD. So, here we calculate the 'actual' size, by adding back in the insets
1058fn compute_outer_size(
1059 inset: Option<Pixels>,
1060 new_size: Option<Size<Pixels>>,
1061 tiling: Tiling,
1062) -> Option<Size<Pixels>> {
1063 let Some(inset) = inset else { return new_size };
1064
1065 new_size.map(|mut new_size| {
1066 if !tiling.top {
1067 new_size.height += inset;
1068 }
1069 if !tiling.bottom {
1070 new_size.height += inset;
1071 }
1072 if !tiling.left {
1073 new_size.width += inset;
1074 }
1075 if !tiling.right {
1076 new_size.width += inset;
1077 }
1078
1079 new_size
1080 })
1081}
1082
1083fn inset_by_tiling(mut bounds: Bounds<Pixels>, inset: Pixels, tiling: Tiling) -> Bounds<Pixels> {
1084 if !tiling.top {
1085 bounds.origin.y += inset;
1086 bounds.size.height -= inset;
1087 }
1088 if !tiling.bottom {
1089 bounds.size.height -= inset;
1090 }
1091 if !tiling.left {
1092 bounds.origin.x += inset;
1093 bounds.size.width -= inset;
1094 }
1095 if !tiling.right {
1096 bounds.size.width -= inset;
1097 }
1098
1099 bounds
1100}