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