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 pub fn handle_surface_event(
549 &self,
550 event: wl_surface::Event,
551 outputs: HashMap<ObjectId, Output>,
552 ) {
553 let mut state = self.state.borrow_mut();
554
555 match event {
556 wl_surface::Event::Enter { output } => {
557 let id = output.id();
558
559 let Some(output) = outputs.get(&id) else {
560 return;
561 };
562
563 state.outputs.insert(id, output.clone());
564
565 let scale = primary_output_scale(&mut state);
566
567 // We use `PreferredBufferScale` instead to set the scale if it's available
568 if state.surface.version() < wl_surface::EVT_PREFERRED_BUFFER_SCALE_SINCE {
569 state.surface.set_buffer_scale(scale);
570 drop(state);
571 self.rescale(scale as f32);
572 }
573 }
574 wl_surface::Event::Leave { output } => {
575 state.outputs.remove(&output.id());
576
577 let scale = primary_output_scale(&mut state);
578
579 // We use `PreferredBufferScale` instead to set the scale if it's available
580 if state.surface.version() < wl_surface::EVT_PREFERRED_BUFFER_SCALE_SINCE {
581 state.surface.set_buffer_scale(scale);
582 drop(state);
583 self.rescale(scale as f32);
584 }
585 }
586 wl_surface::Event::PreferredBufferScale { factor } => {
587 // We use `WpFractionalScale` instead to set the scale if it's available
588 if state.globals.fractional_scale_manager.is_none() {
589 state.surface.set_buffer_scale(factor);
590 drop(state);
591 self.rescale(factor as f32);
592 }
593 }
594 _ => {}
595 }
596 }
597
598 pub fn handle_ime(&self, ime: ImeInput) {
599 let mut state = self.state.borrow_mut();
600 if let Some(mut input_handler) = state.input_handler.take() {
601 drop(state);
602 match ime {
603 ImeInput::InsertText(text) => {
604 input_handler.replace_text_in_range(None, &text);
605 }
606 ImeInput::SetMarkedText(text) => {
607 input_handler.replace_and_mark_text_in_range(None, &text, None);
608 }
609 ImeInput::UnmarkText => {
610 input_handler.unmark_text();
611 }
612 ImeInput::DeleteText => {
613 if let Some(marked) = input_handler.marked_text_range() {
614 input_handler.replace_text_in_range(Some(marked), "");
615 }
616 }
617 }
618 self.state.borrow_mut().input_handler = Some(input_handler);
619 }
620 }
621
622 pub fn get_ime_area(&self) -> Option<Bounds<Pixels>> {
623 let mut state = self.state.borrow_mut();
624 let mut bounds: Option<Bounds<Pixels>> = None;
625 if let Some(mut input_handler) = state.input_handler.take() {
626 drop(state);
627 if let Some(range) = input_handler.selected_text_range() {
628 bounds = input_handler.bounds_for_range(range);
629 }
630 self.state.borrow_mut().input_handler = Some(input_handler);
631 }
632 bounds
633 }
634
635 pub fn set_size_and_scale(&self, size: Option<Size<Pixels>>, scale: Option<f32>) {
636 let (size, scale) = {
637 let mut state = self.state.borrow_mut();
638 if size.map_or(true, |size| size == state.bounds.size)
639 && scale.map_or(true, |scale| scale == state.scale)
640 {
641 return;
642 }
643 if let Some(size) = size {
644 state.bounds.size = size;
645 }
646 if let Some(scale) = scale {
647 state.scale = scale;
648 }
649 let device_bounds = state.bounds.to_device_pixels(state.scale);
650 state.renderer.update_drawable_size(device_bounds.size);
651 (state.bounds.size, state.scale)
652 };
653
654 if let Some(ref mut fun) = self.callbacks.borrow_mut().resize {
655 fun(size, scale);
656 }
657
658 {
659 let state = self.state.borrow();
660 if let Some(viewport) = &state.viewport {
661 viewport.set_destination(size.width.0 as i32, size.height.0 as i32);
662 }
663 }
664 }
665
666 pub fn resize(&self, size: Size<Pixels>) {
667 self.set_size_and_scale(Some(size), None);
668 }
669
670 pub fn rescale(&self, scale: f32) {
671 self.set_size_and_scale(None, Some(scale));
672 }
673
674 pub fn close(&self) {
675 let mut callbacks = self.callbacks.borrow_mut();
676 if let Some(fun) = callbacks.close.take() {
677 fun()
678 }
679 }
680
681 pub fn handle_input(&self, input: PlatformInput) {
682 if let Some(ref mut fun) = self.callbacks.borrow_mut().input {
683 if !fun(input.clone()).propagate {
684 return;
685 }
686 }
687 if let PlatformInput::KeyDown(event) = input {
688 if let Some(ime_key) = &event.keystroke.ime_key {
689 let mut state = self.state.borrow_mut();
690 if let Some(mut input_handler) = state.input_handler.take() {
691 drop(state);
692 input_handler.replace_text_in_range(None, ime_key);
693 self.state.borrow_mut().input_handler = Some(input_handler);
694 }
695 }
696 }
697 }
698
699 pub fn set_focused(&self, focus: bool) {
700 self.state.borrow_mut().active = focus;
701 if let Some(ref mut fun) = self.callbacks.borrow_mut().active_status_change {
702 fun(focus);
703 }
704 }
705
706 pub fn set_hovered(&self, focus: bool) {
707 if let Some(ref mut fun) = self.callbacks.borrow_mut().hover_status_change {
708 fun(focus);
709 }
710 }
711
712 pub fn set_appearance(&mut self, appearance: WindowAppearance) {
713 self.state.borrow_mut().appearance = appearance;
714
715 let mut callbacks = self.callbacks.borrow_mut();
716 if let Some(ref mut fun) = callbacks.appearance_changed {
717 (fun)()
718 }
719 }
720}
721
722fn extract_states<'a, S: TryFrom<u32> + 'a>(states: &'a [u8]) -> impl Iterator<Item = S> + 'a
723where
724 <S as TryFrom<u32>>::Error: 'a,
725{
726 states
727 .chunks_exact(4)
728 .flat_map(TryInto::<[u8; 4]>::try_into)
729 .map(u32::from_ne_bytes)
730 .flat_map(S::try_from)
731}
732
733fn primary_output_scale(state: &mut RefMut<WaylandWindowState>) -> i32 {
734 let mut scale = 1;
735 let mut current_output = state.display.take();
736 for (id, output) in state.outputs.iter() {
737 if let Some((_, output_data)) = ¤t_output {
738 if output.scale > output_data.scale {
739 current_output = Some((id.clone(), output.clone()));
740 }
741 } else {
742 current_output = Some((id.clone(), output.clone()));
743 }
744 scale = scale.max(output.scale);
745 }
746 state.display = current_output;
747 scale
748}
749
750impl rwh::HasWindowHandle for WaylandWindow {
751 fn window_handle(&self) -> Result<rwh::WindowHandle<'_>, rwh::HandleError> {
752 unimplemented!()
753 }
754}
755impl rwh::HasDisplayHandle for WaylandWindow {
756 fn display_handle(&self) -> Result<rwh::DisplayHandle<'_>, rwh::HandleError> {
757 unimplemented!()
758 }
759}
760
761impl PlatformWindow for WaylandWindow {
762 fn bounds(&self) -> Bounds<Pixels> {
763 self.borrow().bounds
764 }
765
766 fn is_maximized(&self) -> bool {
767 self.borrow().maximized
768 }
769
770 fn window_bounds(&self) -> WindowBounds {
771 let state = self.borrow();
772 if state.fullscreen {
773 WindowBounds::Fullscreen(state.window_bounds)
774 } else if state.maximized {
775 WindowBounds::Maximized(state.window_bounds)
776 } else {
777 drop(state);
778 WindowBounds::Windowed(self.bounds())
779 }
780 }
781
782 fn content_size(&self) -> Size<Pixels> {
783 self.borrow().bounds.size
784 }
785
786 fn scale_factor(&self) -> f32 {
787 self.borrow().scale
788 }
789
790 fn appearance(&self) -> WindowAppearance {
791 self.borrow().appearance
792 }
793
794 fn display(&self) -> Option<Rc<dyn PlatformDisplay>> {
795 let state = self.borrow();
796 state.display.as_ref().map(|(id, display)| {
797 Rc::new(WaylandDisplay {
798 id: id.clone(),
799 name: display.name.clone(),
800 bounds: display.bounds.to_pixels(state.scale),
801 }) as Rc<dyn PlatformDisplay>
802 })
803 }
804
805 fn mouse_position(&self) -> Point<Pixels> {
806 self.borrow()
807 .client
808 .get_client()
809 .borrow()
810 .mouse_location
811 .unwrap_or_default()
812 }
813
814 fn modifiers(&self) -> Modifiers {
815 self.borrow().client.get_client().borrow().modifiers
816 }
817
818 fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
819 self.borrow_mut().input_handler = Some(input_handler);
820 }
821
822 fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
823 self.borrow_mut().input_handler.take()
824 }
825
826 fn prompt(
827 &self,
828 _level: PromptLevel,
829 _msg: &str,
830 _detail: Option<&str>,
831 _answers: &[&str],
832 ) -> Option<Receiver<usize>> {
833 None
834 }
835
836 fn activate(&self) {
837 // Try to request an activation token. Even though the activation is likely going to be rejected,
838 // KWin and Mutter can use the app_id to visually indicate we're requesting attention.
839 let state = self.borrow();
840 if let (Some(activation), Some(app_id)) = (&state.globals.activation, state.app_id.clone())
841 {
842 state.client.set_pending_activation(state.surface.id());
843 let token = activation.get_activation_token(&state.globals.qh, ());
844 // The serial isn't exactly important here, since the activation is probably going to be rejected anyway.
845 let serial = state.client.get_serial(SerialKind::MousePress);
846 token.set_app_id(app_id);
847 token.set_serial(serial, &state.globals.seat);
848 token.set_surface(&state.surface);
849 token.commit();
850 }
851 }
852
853 fn is_active(&self) -> bool {
854 self.borrow().active
855 }
856
857 fn is_hovered(&self) -> bool {
858 self.borrow().hovered
859 }
860
861 fn set_title(&mut self, title: &str) {
862 self.borrow().toplevel.set_title(title.to_string());
863 }
864
865 fn set_app_id(&mut self, app_id: &str) {
866 let mut state = self.borrow_mut();
867 state.toplevel.set_app_id(app_id.to_owned());
868 state.app_id = Some(app_id.to_owned());
869 }
870
871 fn set_background_appearance(&self, background_appearance: WindowBackgroundAppearance) {
872 let mut state = self.borrow_mut();
873 state.background_appearance = background_appearance;
874 update_window(state);
875 }
876
877 fn minimize(&self) {
878 self.borrow().toplevel.set_minimized();
879 }
880
881 fn zoom(&self) {
882 let state = self.borrow();
883 if !state.maximized {
884 state.toplevel.set_maximized();
885 } else {
886 state.toplevel.unset_maximized();
887 }
888 }
889
890 fn toggle_fullscreen(&self) {
891 let mut state = self.borrow_mut();
892 if !state.fullscreen {
893 state.toplevel.set_fullscreen(None);
894 } else {
895 state.toplevel.unset_fullscreen();
896 }
897 }
898
899 fn is_fullscreen(&self) -> bool {
900 self.borrow().fullscreen
901 }
902
903 fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
904 self.0.callbacks.borrow_mut().request_frame = Some(callback);
905 }
906
907 fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> crate::DispatchEventResult>) {
908 self.0.callbacks.borrow_mut().input = Some(callback);
909 }
910
911 fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
912 self.0.callbacks.borrow_mut().active_status_change = Some(callback);
913 }
914
915 fn on_hover_status_change(&self, callback: Box<dyn FnMut(bool)>) {
916 self.0.callbacks.borrow_mut().hover_status_change = Some(callback);
917 }
918
919 fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
920 self.0.callbacks.borrow_mut().resize = Some(callback);
921 }
922
923 fn on_moved(&self, callback: Box<dyn FnMut()>) {
924 self.0.callbacks.borrow_mut().moved = Some(callback);
925 }
926
927 fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>) {
928 self.0.callbacks.borrow_mut().should_close = Some(callback);
929 }
930
931 fn on_close(&self, callback: Box<dyn FnOnce()>) {
932 self.0.callbacks.borrow_mut().close = Some(callback);
933 }
934
935 fn on_appearance_changed(&self, callback: Box<dyn FnMut()>) {
936 self.0.callbacks.borrow_mut().appearance_changed = Some(callback);
937 }
938
939 fn draw(&self, scene: &Scene) {
940 let mut state = self.borrow_mut();
941 state.renderer.draw(scene);
942 }
943
944 fn completed_frame(&self) {
945 let state = self.borrow();
946 state.surface.commit();
947 }
948
949 fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
950 let state = self.borrow();
951 state.renderer.sprite_atlas().clone()
952 }
953
954 fn show_window_menu(&self, position: Point<Pixels>) {
955 let state = self.borrow();
956 let serial = state.client.get_serial(SerialKind::MousePress);
957 state.toplevel.show_window_menu(
958 &state.globals.seat,
959 serial,
960 position.x.0 as i32,
961 position.y.0 as i32,
962 );
963 }
964
965 fn start_window_move(&self) {
966 let state = self.borrow();
967 let serial = state.client.get_serial(SerialKind::MousePress);
968 state.toplevel._move(&state.globals.seat, serial);
969 }
970
971 fn start_window_resize(&self, edge: crate::ResizeEdge) {
972 let state = self.borrow();
973 state.toplevel.resize(
974 &state.globals.seat,
975 state.client.get_serial(SerialKind::MousePress),
976 edge.to_xdg(),
977 )
978 }
979
980 fn window_decorations(&self) -> Decorations {
981 let state = self.borrow();
982 match state.decorations {
983 WindowDecorations::Server => Decorations::Server,
984 WindowDecorations::Client => Decorations::Client {
985 tiling: state.tiling,
986 },
987 }
988 }
989
990 fn request_decorations(&self, decorations: WindowDecorations) {
991 let mut state = self.borrow_mut();
992 state.decorations = decorations;
993 if let Some(decoration) = state.decoration.as_ref() {
994 decoration.set_mode(decorations.to_xdg());
995 update_window(state);
996 }
997 }
998
999 fn window_controls(&self) -> WindowControls {
1000 self.borrow().window_controls
1001 }
1002
1003 fn set_client_inset(&self, inset: Pixels) {
1004 let mut state = self.borrow_mut();
1005 if Some(inset) != state.inset {
1006 state.inset = Some(inset);
1007 update_window(state);
1008 }
1009 }
1010
1011 fn gpu_specs(&self) -> Option<GPUSpecs> {
1012 self.borrow().renderer.gpu_specs().into()
1013 }
1014}
1015
1016fn update_window(mut state: RefMut<WaylandWindowState>) {
1017 let opaque = !state.is_transparent();
1018
1019 state.renderer.update_transparency(!opaque);
1020 let mut opaque_area = state.window_bounds.map(|v| v.0 as i32);
1021 if let Some(inset) = state.inset {
1022 opaque_area.inset(inset.0 as i32);
1023 }
1024
1025 let region = state
1026 .globals
1027 .compositor
1028 .create_region(&state.globals.qh, ());
1029 region.add(
1030 opaque_area.origin.x,
1031 opaque_area.origin.y,
1032 opaque_area.size.width,
1033 opaque_area.size.height,
1034 );
1035
1036 // Note that rounded corners make this rectangle API hard to work with.
1037 // As this is common when using CSD, let's just disable this API.
1038 if state.background_appearance == WindowBackgroundAppearance::Opaque
1039 && state.decorations == WindowDecorations::Server
1040 {
1041 // Promise the compositor that this region of the window surface
1042 // contains no transparent pixels. This allows the compositor to
1043 // do skip whatever is behind the surface for better performance.
1044 state.surface.set_opaque_region(Some(®ion));
1045 } else {
1046 state.surface.set_opaque_region(None);
1047 }
1048
1049 if let Some(ref blur_manager) = state.globals.blur_manager {
1050 if state.background_appearance == WindowBackgroundAppearance::Blurred {
1051 if state.blur.is_none() {
1052 let blur = blur_manager.create(&state.surface, &state.globals.qh, ());
1053 blur.set_region(Some(®ion));
1054 state.blur = Some(blur);
1055 }
1056 state.blur.as_ref().unwrap().commit();
1057 } else {
1058 // It probably doesn't hurt to clear the blur for opaque windows
1059 blur_manager.unset(&state.surface);
1060 if let Some(b) = state.blur.take() {
1061 b.release()
1062 }
1063 }
1064 }
1065
1066 region.destroy();
1067}
1068
1069impl WindowDecorations {
1070 fn to_xdg(&self) -> zxdg_toplevel_decoration_v1::Mode {
1071 match self {
1072 WindowDecorations::Client => zxdg_toplevel_decoration_v1::Mode::ClientSide,
1073 WindowDecorations::Server => zxdg_toplevel_decoration_v1::Mode::ServerSide,
1074 }
1075 }
1076}
1077
1078impl ResizeEdge {
1079 fn to_xdg(&self) -> xdg_toplevel::ResizeEdge {
1080 match self {
1081 ResizeEdge::Top => xdg_toplevel::ResizeEdge::Top,
1082 ResizeEdge::TopRight => xdg_toplevel::ResizeEdge::TopRight,
1083 ResizeEdge::Right => xdg_toplevel::ResizeEdge::Right,
1084 ResizeEdge::BottomRight => xdg_toplevel::ResizeEdge::BottomRight,
1085 ResizeEdge::Bottom => xdg_toplevel::ResizeEdge::Bottom,
1086 ResizeEdge::BottomLeft => xdg_toplevel::ResizeEdge::BottomLeft,
1087 ResizeEdge::Left => xdg_toplevel::ResizeEdge::Left,
1088 ResizeEdge::TopLeft => xdg_toplevel::ResizeEdge::TopLeft,
1089 }
1090 }
1091}
1092
1093/// The configuration event is in terms of the window geometry, which we are constantly
1094/// updating to account for the client decorations. But that's not the area we want to render
1095/// to, due to our intrusize CSD. So, here we calculate the 'actual' size, by adding back in the insets
1096fn compute_outer_size(
1097 inset: Option<Pixels>,
1098 new_size: Option<Size<Pixels>>,
1099 tiling: Tiling,
1100) -> Option<Size<Pixels>> {
1101 let Some(inset) = inset else { return new_size };
1102
1103 new_size.map(|mut new_size| {
1104 if !tiling.top {
1105 new_size.height += inset;
1106 }
1107 if !tiling.bottom {
1108 new_size.height += inset;
1109 }
1110 if !tiling.left {
1111 new_size.width += inset;
1112 }
1113 if !tiling.right {
1114 new_size.width += inset;
1115 }
1116
1117 new_size
1118 })
1119}
1120
1121fn inset_by_tiling(mut bounds: Bounds<Pixels>, inset: Pixels, tiling: Tiling) -> Bounds<Pixels> {
1122 if !tiling.top {
1123 bounds.origin.y += inset;
1124 bounds.size.height -= inset;
1125 }
1126 if !tiling.bottom {
1127 bounds.size.height -= inset;
1128 }
1129 if !tiling.left {
1130 bounds.origin.x += inset;
1131 bounds.size.width -= inset;
1132 }
1133 if !tiling.right {
1134 bounds.size.width -= inset;
1135 }
1136
1137 bounds
1138}