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