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