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