1use std::{
2 cell::{Ref, RefCell, RefMut},
3 ffi::c_void,
4 ptr::NonNull,
5 rc::Rc,
6 sync::Arc,
7};
8
9use collections::{FxHashSet, HashMap};
10use futures::channel::oneshot::Receiver;
11
12use raw_window_handle as rwh;
13use wayland_backend::client::ObjectId;
14use wayland_client::WEnum;
15use wayland_client::{Proxy, protocol::wl_surface};
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::{
21 wp::fractional_scale::v1::client::wp_fractional_scale_v1,
22 xdg::dialog::v1::client::xdg_dialog_v1::XdgDialogV1,
23};
24use wayland_protocols_plasma::blur::client::org_kde_kwin_blur;
25use wayland_protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1;
26
27use crate::{
28 AnyWindowHandle, Bounds, Decorations, DevicePixels, Globals, GpuSpecs, Modifiers, Output,
29 Pixels, PlatformDisplay, PlatformInput, Point, PromptButton, PromptLevel, RequestFrameOptions,
30 ResizeEdge, Size, Tiling, WaylandClientStatePtr, WindowAppearance, WindowBackgroundAppearance,
31 WindowBounds, WindowControlArea, WindowControls, WindowDecorations, WindowParams, get_window,
32 layer_shell::LayerShellNotSupportedError, px, size,
33};
34use crate::{
35 Capslock,
36 platform::{
37 PlatformAtlas, PlatformInputHandler, PlatformWindow,
38 linux::wayland::{display::WaylandDisplay, serial::SerialKind},
39 wgpu::{WgpuContext, WgpuRenderer, WgpuSurfaceConfig},
40 },
41};
42use crate::{WindowKind, scene::Scene};
43
44#[derive(Default)]
45pub(crate) struct Callbacks {
46 request_frame: Option<Box<dyn FnMut(RequestFrameOptions)>>,
47 input: Option<Box<dyn FnMut(crate::PlatformInput) -> crate::DispatchEventResult>>,
48 active_status_change: Option<Box<dyn FnMut(bool)>>,
49 hover_status_change: Option<Box<dyn FnMut(bool)>>,
50 resize: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
51 moved: Option<Box<dyn FnMut()>>,
52 should_close: Option<Box<dyn FnMut() -> bool>>,
53 close: Option<Box<dyn FnOnce()>>,
54 appearance_changed: Option<Box<dyn FnMut()>>,
55}
56
57struct RawWindow {
58 window: *mut c_void,
59 display: *mut c_void,
60}
61
62// Safety: The raw pointers in RawWindow point to Wayland surface/display
63// which are valid for the window's lifetime. These are used only for
64// passing to wgpu which needs Send+Sync for surface creation.
65unsafe impl Send for RawWindow {}
66unsafe impl Sync for RawWindow {}
67
68impl rwh::HasWindowHandle for RawWindow {
69 fn window_handle(&self) -> Result<rwh::WindowHandle<'_>, rwh::HandleError> {
70 let window = NonNull::new(self.window).unwrap();
71 let handle = rwh::WaylandWindowHandle::new(window);
72 Ok(unsafe { rwh::WindowHandle::borrow_raw(handle.into()) })
73 }
74}
75impl rwh::HasDisplayHandle for RawWindow {
76 fn display_handle(&self) -> Result<rwh::DisplayHandle<'_>, rwh::HandleError> {
77 let display = NonNull::new(self.display).unwrap();
78 let handle = rwh::WaylandDisplayHandle::new(display);
79 Ok(unsafe { rwh::DisplayHandle::borrow_raw(handle.into()) })
80 }
81}
82
83#[derive(Debug)]
84struct InProgressConfigure {
85 size: Option<Size<Pixels>>,
86 fullscreen: bool,
87 maximized: bool,
88 resizing: bool,
89 tiling: Tiling,
90}
91
92pub struct WaylandWindowState {
93 surface_state: WaylandSurfaceState,
94 acknowledged_first_configure: bool,
95 parent: Option<WaylandWindowStatePtr>,
96 children: FxHashSet<ObjectId>,
97 pub surface: wl_surface::WlSurface,
98 app_id: Option<String>,
99 appearance: WindowAppearance,
100 blur: Option<org_kde_kwin_blur::OrgKdeKwinBlur>,
101 viewport: Option<wp_viewport::WpViewport>,
102 outputs: HashMap<ObjectId, Output>,
103 display: Option<(ObjectId, Output)>,
104 globals: Globals,
105 renderer: WgpuRenderer,
106 bounds: Bounds<Pixels>,
107 scale: f32,
108 input_handler: Option<PlatformInputHandler>,
109 decorations: WindowDecorations,
110 background_appearance: WindowBackgroundAppearance,
111 fullscreen: bool,
112 maximized: bool,
113 tiling: Tiling,
114 window_bounds: Bounds<Pixels>,
115 client: WaylandClientStatePtr,
116 handle: AnyWindowHandle,
117 active: bool,
118 hovered: bool,
119 in_progress_configure: Option<InProgressConfigure>,
120 resize_throttle: bool,
121 in_progress_window_controls: Option<WindowControls>,
122 window_controls: WindowControls,
123 client_inset: Option<Pixels>,
124}
125
126pub enum WaylandSurfaceState {
127 Xdg(WaylandXdgSurfaceState),
128 LayerShell(WaylandLayerSurfaceState),
129}
130
131impl WaylandSurfaceState {
132 fn new(
133 surface: &wl_surface::WlSurface,
134 globals: &Globals,
135 params: &WindowParams,
136 parent: Option<WaylandWindowStatePtr>,
137 ) -> anyhow::Result<Self> {
138 // For layer_shell windows, create a layer surface instead of an xdg surface
139 if let WindowKind::LayerShell(options) = ¶ms.kind {
140 let Some(layer_shell) = globals.layer_shell.as_ref() else {
141 return Err(LayerShellNotSupportedError.into());
142 };
143
144 let layer_surface = layer_shell.get_layer_surface(
145 &surface,
146 None,
147 options.layer.into(),
148 options.namespace.clone(),
149 &globals.qh,
150 surface.id(),
151 );
152
153 let width = params.bounds.size.width.0;
154 let height = params.bounds.size.height.0;
155 layer_surface.set_size(width as u32, height as u32);
156
157 layer_surface.set_anchor(options.anchor.into());
158 layer_surface.set_keyboard_interactivity(options.keyboard_interactivity.into());
159
160 if let Some(margin) = options.margin {
161 layer_surface.set_margin(
162 margin.0.0 as i32,
163 margin.1.0 as i32,
164 margin.2.0 as i32,
165 margin.3.0 as i32,
166 )
167 }
168
169 if let Some(exclusive_zone) = options.exclusive_zone {
170 layer_surface.set_exclusive_zone(exclusive_zone.0 as i32);
171 }
172
173 if let Some(exclusive_edge) = options.exclusive_edge {
174 layer_surface.set_exclusive_edge(exclusive_edge.into());
175 }
176
177 return Ok(WaylandSurfaceState::LayerShell(WaylandLayerSurfaceState {
178 layer_surface,
179 }));
180 }
181
182 // All other WindowKinds result in a regular xdg surface
183 let xdg_surface = globals
184 .wm_base
185 .get_xdg_surface(&surface, &globals.qh, surface.id());
186
187 let toplevel = xdg_surface.get_toplevel(&globals.qh, surface.id());
188 let xdg_parent = parent.as_ref().and_then(|w| w.toplevel());
189
190 if params.kind == WindowKind::Floating || params.kind == WindowKind::Dialog {
191 toplevel.set_parent(xdg_parent.as_ref());
192 }
193
194 let dialog = if params.kind == WindowKind::Dialog {
195 let dialog = globals.dialog.as_ref().map(|dialog| {
196 let xdg_dialog = dialog.get_xdg_dialog(&toplevel, &globals.qh, ());
197 xdg_dialog.set_modal();
198 xdg_dialog
199 });
200
201 if let Some(parent) = parent.as_ref() {
202 parent.add_child(surface.id());
203 }
204
205 dialog
206 } else {
207 None
208 };
209
210 if let Some(size) = params.window_min_size {
211 toplevel.set_min_size(size.width.0 as i32, size.height.0 as i32);
212 }
213
214 // Attempt to set up window decorations based on the requested configuration
215 let decoration = globals
216 .decoration_manager
217 .as_ref()
218 .map(|decoration_manager| {
219 decoration_manager.get_toplevel_decoration(&toplevel, &globals.qh, surface.id())
220 });
221
222 Ok(WaylandSurfaceState::Xdg(WaylandXdgSurfaceState {
223 xdg_surface,
224 toplevel,
225 decoration,
226 dialog,
227 }))
228 }
229}
230
231pub struct WaylandXdgSurfaceState {
232 xdg_surface: xdg_surface::XdgSurface,
233 toplevel: xdg_toplevel::XdgToplevel,
234 decoration: Option<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1>,
235 dialog: Option<XdgDialogV1>,
236}
237
238pub struct WaylandLayerSurfaceState {
239 layer_surface: zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,
240}
241
242impl WaylandSurfaceState {
243 fn ack_configure(&self, serial: u32) {
244 match self {
245 WaylandSurfaceState::Xdg(WaylandXdgSurfaceState { xdg_surface, .. }) => {
246 xdg_surface.ack_configure(serial);
247 }
248 WaylandSurfaceState::LayerShell(WaylandLayerSurfaceState { layer_surface, .. }) => {
249 layer_surface.ack_configure(serial);
250 }
251 }
252 }
253
254 fn decoration(&self) -> Option<&zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1> {
255 if let WaylandSurfaceState::Xdg(WaylandXdgSurfaceState { decoration, .. }) = self {
256 decoration.as_ref()
257 } else {
258 None
259 }
260 }
261
262 fn toplevel(&self) -> Option<&xdg_toplevel::XdgToplevel> {
263 if let WaylandSurfaceState::Xdg(WaylandXdgSurfaceState { toplevel, .. }) = self {
264 Some(toplevel)
265 } else {
266 None
267 }
268 }
269
270 fn set_geometry(&self, x: i32, y: i32, width: i32, height: i32) {
271 match self {
272 WaylandSurfaceState::Xdg(WaylandXdgSurfaceState { xdg_surface, .. }) => {
273 xdg_surface.set_window_geometry(x, y, width, height);
274 }
275 WaylandSurfaceState::LayerShell(WaylandLayerSurfaceState { layer_surface, .. }) => {
276 // cannot set window position of a layer surface
277 layer_surface.set_size(width as u32, height as u32);
278 }
279 }
280 }
281
282 fn destroy(&mut self) {
283 match self {
284 WaylandSurfaceState::Xdg(WaylandXdgSurfaceState {
285 xdg_surface,
286 toplevel,
287 decoration: _decoration,
288 dialog,
289 }) => {
290 // drop the dialog before toplevel so compositor can explicitly unapply it's effects
291 if let Some(dialog) = dialog {
292 dialog.destroy();
293 }
294
295 // The role object (toplevel) must always be destroyed before the xdg_surface.
296 // See https://wayland.app/protocols/xdg-shell#xdg_surface:request:destroy
297 toplevel.destroy();
298 xdg_surface.destroy();
299 }
300 WaylandSurfaceState::LayerShell(WaylandLayerSurfaceState { layer_surface }) => {
301 layer_surface.destroy();
302 }
303 }
304 }
305}
306
307#[derive(Clone)]
308pub struct WaylandWindowStatePtr {
309 state: Rc<RefCell<WaylandWindowState>>,
310 callbacks: Rc<RefCell<Callbacks>>,
311}
312
313impl WaylandWindowState {
314 pub(crate) fn new(
315 handle: AnyWindowHandle,
316 surface: wl_surface::WlSurface,
317 surface_state: WaylandSurfaceState,
318 appearance: WindowAppearance,
319 viewport: Option<wp_viewport::WpViewport>,
320 client: WaylandClientStatePtr,
321 globals: Globals,
322 gpu_context: &mut Option<WgpuContext>,
323 options: WindowParams,
324 parent: Option<WaylandWindowStatePtr>,
325 ) -> anyhow::Result<Self> {
326 let renderer = {
327 let raw_window = RawWindow {
328 window: surface.id().as_ptr().cast::<c_void>(),
329 display: surface
330 .backend()
331 .upgrade()
332 .unwrap()
333 .display_ptr()
334 .cast::<c_void>(),
335 };
336 let config = WgpuSurfaceConfig {
337 size: Size {
338 width: DevicePixels(options.bounds.size.width.0 as i32),
339 height: DevicePixels(options.bounds.size.height.0 as i32),
340 },
341 transparent: true,
342 };
343 WgpuRenderer::new(gpu_context, &raw_window, config)?
344 };
345
346 if let WaylandSurfaceState::Xdg(ref xdg_state) = surface_state {
347 if let Some(title) = options.titlebar.and_then(|titlebar| titlebar.title) {
348 xdg_state.toplevel.set_title(title.to_string());
349 }
350 }
351
352 Ok(Self {
353 surface_state,
354 acknowledged_first_configure: false,
355 parent,
356 children: FxHashSet::default(),
357 surface,
358 app_id: None,
359 blur: None,
360 viewport,
361 globals,
362 outputs: HashMap::default(),
363 display: None,
364 renderer,
365 bounds: options.bounds,
366 scale: 1.0,
367 input_handler: None,
368 decorations: WindowDecorations::Client,
369 background_appearance: WindowBackgroundAppearance::Opaque,
370 fullscreen: false,
371 maximized: false,
372 tiling: Tiling::default(),
373 window_bounds: options.bounds,
374 in_progress_configure: None,
375 resize_throttle: false,
376 client,
377 appearance,
378 handle,
379 active: false,
380 hovered: false,
381 in_progress_window_controls: None,
382 window_controls: WindowControls::default(),
383 client_inset: None,
384 })
385 }
386
387 pub fn is_transparent(&self) -> bool {
388 self.decorations == WindowDecorations::Client
389 || self.background_appearance != WindowBackgroundAppearance::Opaque
390 }
391
392 pub fn primary_output_scale(&mut self) -> i32 {
393 let mut scale = 1;
394 let mut current_output = self.display.take();
395 for (id, output) in self.outputs.iter() {
396 if let Some((_, output_data)) = ¤t_output {
397 if output.scale > output_data.scale {
398 current_output = Some((id.clone(), output.clone()));
399 }
400 } else {
401 current_output = Some((id.clone(), output.clone()));
402 }
403 scale = scale.max(output.scale);
404 }
405 self.display = current_output;
406 scale
407 }
408
409 pub fn inset(&self) -> Pixels {
410 match self.decorations {
411 WindowDecorations::Server => px(0.0),
412 WindowDecorations::Client => self.client_inset.unwrap_or(px(0.0)),
413 }
414 }
415}
416
417pub(crate) struct WaylandWindow(pub WaylandWindowStatePtr);
418pub enum ImeInput {
419 InsertText(String),
420 SetMarkedText(String),
421 UnmarkText,
422 DeleteText,
423}
424
425impl Drop for WaylandWindow {
426 fn drop(&mut self) {
427 let mut state = self.0.state.borrow_mut();
428 let surface_id = state.surface.id();
429 if let Some(parent) = state.parent.as_ref() {
430 parent.state.borrow_mut().children.remove(&surface_id);
431 }
432
433 let client = state.client.clone();
434
435 state.renderer.destroy();
436
437 // Destroy blur first, this has no dependencies.
438 if let Some(blur) = &state.blur {
439 blur.release();
440 }
441
442 // Decorations must be destroyed before the xdg state.
443 // See https://wayland.app/protocols/xdg-decoration-unstable-v1#zxdg_toplevel_decoration_v1
444 if let Some(decoration) = &state.surface_state.decoration() {
445 decoration.destroy();
446 }
447
448 // Surface state might contain xdg_toplevel/xdg_surface which can be destroyed now that
449 // decorations are gone. layer_surface has no dependencies.
450 state.surface_state.destroy();
451
452 // Viewport must be destroyed before the wl_surface.
453 // See https://wayland.app/protocols/viewporter#wp_viewport
454 if let Some(viewport) = &state.viewport {
455 viewport.destroy();
456 }
457
458 // The wl_surface itself should always be destroyed last.
459 state.surface.destroy();
460
461 let state_ptr = self.0.clone();
462 state
463 .globals
464 .executor
465 .spawn(async move {
466 state_ptr.close();
467 client.drop_window(&surface_id)
468 })
469 .detach();
470 drop(state);
471 }
472}
473
474impl WaylandWindow {
475 fn borrow(&self) -> Ref<'_, WaylandWindowState> {
476 self.0.state.borrow()
477 }
478
479 fn borrow_mut(&self) -> RefMut<'_, WaylandWindowState> {
480 self.0.state.borrow_mut()
481 }
482
483 pub fn new(
484 handle: AnyWindowHandle,
485 globals: Globals,
486 gpu_context: &mut Option<WgpuContext>,
487 client: WaylandClientStatePtr,
488 params: WindowParams,
489 appearance: WindowAppearance,
490 parent: Option<WaylandWindowStatePtr>,
491 ) -> anyhow::Result<(Self, ObjectId)> {
492 let surface = globals.compositor.create_surface(&globals.qh, ());
493 let surface_state = WaylandSurfaceState::new(&surface, &globals, ¶ms, parent.clone())?;
494
495 if let Some(fractional_scale_manager) = globals.fractional_scale_manager.as_ref() {
496 fractional_scale_manager.get_fractional_scale(&surface, &globals.qh, surface.id());
497 }
498
499 let viewport = globals
500 .viewporter
501 .as_ref()
502 .map(|viewporter| viewporter.get_viewport(&surface, &globals.qh, ()));
503
504 let this = Self(WaylandWindowStatePtr {
505 state: Rc::new(RefCell::new(WaylandWindowState::new(
506 handle,
507 surface.clone(),
508 surface_state,
509 appearance,
510 viewport,
511 client,
512 globals,
513 gpu_context,
514 params,
515 parent,
516 )?)),
517 callbacks: Rc::new(RefCell::new(Callbacks::default())),
518 });
519
520 // Kick things off
521 surface.commit();
522
523 Ok((this, surface.id()))
524 }
525}
526
527impl WaylandWindowStatePtr {
528 pub fn handle(&self) -> AnyWindowHandle {
529 self.state.borrow().handle
530 }
531
532 pub fn surface(&self) -> wl_surface::WlSurface {
533 self.state.borrow().surface.clone()
534 }
535
536 pub fn toplevel(&self) -> Option<xdg_toplevel::XdgToplevel> {
537 self.state.borrow().surface_state.toplevel().cloned()
538 }
539
540 pub fn ptr_eq(&self, other: &Self) -> bool {
541 Rc::ptr_eq(&self.state, &other.state)
542 }
543
544 pub fn add_child(&self, child: ObjectId) {
545 let mut state = self.state.borrow_mut();
546 state.children.insert(child);
547 }
548
549 pub fn is_blocked(&self) -> bool {
550 let state = self.state.borrow();
551 !state.children.is_empty()
552 }
553
554 pub fn frame(&self) {
555 let mut state = self.state.borrow_mut();
556 state.surface.frame(&state.globals.qh, state.surface.id());
557 state.resize_throttle = false;
558 drop(state);
559
560 let mut cb = self.callbacks.borrow_mut();
561 if let Some(fun) = cb.request_frame.as_mut() {
562 fun(Default::default());
563 }
564 }
565
566 pub fn handle_xdg_surface_event(&self, event: xdg_surface::Event) {
567 if let xdg_surface::Event::Configure { serial } = event {
568 {
569 let mut state = self.state.borrow_mut();
570 if let Some(window_controls) = state.in_progress_window_controls.take() {
571 state.window_controls = window_controls;
572
573 drop(state);
574 let mut callbacks = self.callbacks.borrow_mut();
575 if let Some(appearance_changed) = callbacks.appearance_changed.as_mut() {
576 appearance_changed();
577 }
578 }
579 }
580 {
581 let mut state = self.state.borrow_mut();
582
583 if let Some(mut configure) = state.in_progress_configure.take() {
584 let got_unmaximized = state.maximized && !configure.maximized;
585 state.fullscreen = configure.fullscreen;
586 state.maximized = configure.maximized;
587 state.tiling = configure.tiling;
588 // Limit interactive resizes to once per vblank
589 if configure.resizing && state.resize_throttle {
590 return;
591 } else if configure.resizing {
592 state.resize_throttle = true;
593 }
594 if !configure.fullscreen && !configure.maximized {
595 configure.size = if got_unmaximized {
596 Some(state.window_bounds.size)
597 } else {
598 compute_outer_size(state.inset(), configure.size, state.tiling)
599 };
600 if let Some(size) = configure.size {
601 state.window_bounds = Bounds {
602 origin: Point::default(),
603 size,
604 };
605 }
606 }
607 drop(state);
608 if let Some(size) = configure.size {
609 self.resize(size);
610 }
611 }
612 }
613 let mut state = self.state.borrow_mut();
614 state.surface_state.ack_configure(serial);
615
616 let window_geometry = inset_by_tiling(
617 state.bounds.map_origin(|_| px(0.0)),
618 state.inset(),
619 state.tiling,
620 )
621 .map(|v| v.0 as i32)
622 .map_size(|v| if v <= 0 { 1 } else { v });
623
624 state.surface_state.set_geometry(
625 window_geometry.origin.x,
626 window_geometry.origin.y,
627 window_geometry.size.width,
628 window_geometry.size.height,
629 );
630
631 let request_frame_callback = !state.acknowledged_first_configure;
632 if request_frame_callback {
633 state.acknowledged_first_configure = true;
634 drop(state);
635 self.frame();
636 }
637 }
638 }
639
640 pub fn handle_toplevel_decoration_event(&self, event: zxdg_toplevel_decoration_v1::Event) {
641 if let zxdg_toplevel_decoration_v1::Event::Configure { mode } = event {
642 match mode {
643 WEnum::Value(zxdg_toplevel_decoration_v1::Mode::ServerSide) => {
644 self.state.borrow_mut().decorations = WindowDecorations::Server;
645 if let Some(mut appearance_changed) =
646 self.callbacks.borrow_mut().appearance_changed.as_mut()
647 {
648 appearance_changed();
649 }
650 }
651 WEnum::Value(zxdg_toplevel_decoration_v1::Mode::ClientSide) => {
652 self.state.borrow_mut().decorations = WindowDecorations::Client;
653 // Update background to be transparent
654 if let Some(mut appearance_changed) =
655 self.callbacks.borrow_mut().appearance_changed.as_mut()
656 {
657 appearance_changed();
658 }
659 }
660 WEnum::Value(_) => {
661 log::warn!("Unknown decoration mode");
662 }
663 WEnum::Unknown(v) => {
664 log::warn!("Unknown decoration mode: {}", v);
665 }
666 }
667 }
668 }
669
670 pub fn handle_fractional_scale_event(&self, event: wp_fractional_scale_v1::Event) {
671 if let wp_fractional_scale_v1::Event::PreferredScale { scale } = event {
672 self.rescale(scale as f32 / 120.0);
673 }
674 }
675
676 pub fn handle_toplevel_event(&self, event: xdg_toplevel::Event) -> bool {
677 match event {
678 xdg_toplevel::Event::Configure {
679 width,
680 height,
681 states,
682 } => {
683 let mut size = if width == 0 || height == 0 {
684 None
685 } else {
686 Some(size(px(width as f32), px(height as f32)))
687 };
688
689 let states = extract_states::<xdg_toplevel::State>(&states);
690
691 let mut tiling = Tiling::default();
692 let mut fullscreen = false;
693 let mut maximized = false;
694 let mut resizing = false;
695
696 for state in states {
697 match state {
698 xdg_toplevel::State::Maximized => {
699 maximized = true;
700 }
701 xdg_toplevel::State::Fullscreen => {
702 fullscreen = true;
703 }
704 xdg_toplevel::State::Resizing => resizing = true,
705 xdg_toplevel::State::TiledTop => {
706 tiling.top = true;
707 }
708 xdg_toplevel::State::TiledLeft => {
709 tiling.left = true;
710 }
711 xdg_toplevel::State::TiledRight => {
712 tiling.right = true;
713 }
714 xdg_toplevel::State::TiledBottom => {
715 tiling.bottom = true;
716 }
717 _ => {
718 // noop
719 }
720 }
721 }
722
723 if fullscreen || maximized {
724 tiling = Tiling::tiled();
725 }
726
727 let mut state = self.state.borrow_mut();
728 state.in_progress_configure = Some(InProgressConfigure {
729 size,
730 fullscreen,
731 maximized,
732 resizing,
733 tiling,
734 });
735
736 false
737 }
738 xdg_toplevel::Event::Close => {
739 let mut cb = self.callbacks.borrow_mut();
740 if let Some(mut should_close) = cb.should_close.take() {
741 let result = (should_close)();
742 cb.should_close = Some(should_close);
743 if result {
744 drop(cb);
745 self.close();
746 }
747 result
748 } else {
749 true
750 }
751 }
752 xdg_toplevel::Event::WmCapabilities { capabilities } => {
753 let mut window_controls = WindowControls::default();
754
755 let states = extract_states::<xdg_toplevel::WmCapabilities>(&capabilities);
756
757 for state in states {
758 match state {
759 xdg_toplevel::WmCapabilities::Maximize => {
760 window_controls.maximize = true;
761 }
762 xdg_toplevel::WmCapabilities::Minimize => {
763 window_controls.minimize = true;
764 }
765 xdg_toplevel::WmCapabilities::Fullscreen => {
766 window_controls.fullscreen = true;
767 }
768 xdg_toplevel::WmCapabilities::WindowMenu => {
769 window_controls.window_menu = true;
770 }
771 _ => {}
772 }
773 }
774
775 let mut state = self.state.borrow_mut();
776 state.in_progress_window_controls = Some(window_controls);
777 false
778 }
779 _ => false,
780 }
781 }
782
783 pub fn handle_layersurface_event(&self, event: zwlr_layer_surface_v1::Event) -> bool {
784 match event {
785 zwlr_layer_surface_v1::Event::Configure {
786 width,
787 height,
788 serial,
789 } => {
790 let mut size = if width == 0 || height == 0 {
791 None
792 } else {
793 Some(size(px(width as f32), px(height as f32)))
794 };
795
796 let mut state = self.state.borrow_mut();
797 state.in_progress_configure = Some(InProgressConfigure {
798 size,
799 fullscreen: false,
800 maximized: false,
801 resizing: false,
802 tiling: Tiling::default(),
803 });
804 drop(state);
805
806 // just do the same thing we'd do as an xdg_surface
807 self.handle_xdg_surface_event(xdg_surface::Event::Configure { serial });
808
809 false
810 }
811 zwlr_layer_surface_v1::Event::Closed => {
812 // unlike xdg, we don't have a choice here: the surface is closing.
813 true
814 }
815 _ => false,
816 }
817 }
818
819 #[allow(clippy::mutable_key_type)]
820 pub fn handle_surface_event(
821 &self,
822 event: wl_surface::Event,
823 outputs: HashMap<ObjectId, Output>,
824 ) {
825 let mut state = self.state.borrow_mut();
826
827 match event {
828 wl_surface::Event::Enter { output } => {
829 let id = output.id();
830
831 let Some(output) = outputs.get(&id) else {
832 return;
833 };
834
835 state.outputs.insert(id, output.clone());
836
837 let scale = state.primary_output_scale();
838
839 // We use `PreferredBufferScale` instead to set the scale if it's available
840 if state.surface.version() < wl_surface::EVT_PREFERRED_BUFFER_SCALE_SINCE {
841 state.surface.set_buffer_scale(scale);
842 drop(state);
843 self.rescale(scale as f32);
844 }
845 }
846 wl_surface::Event::Leave { output } => {
847 state.outputs.remove(&output.id());
848
849 let scale = state.primary_output_scale();
850
851 // We use `PreferredBufferScale` instead to set the scale if it's available
852 if state.surface.version() < wl_surface::EVT_PREFERRED_BUFFER_SCALE_SINCE {
853 state.surface.set_buffer_scale(scale);
854 drop(state);
855 self.rescale(scale as f32);
856 }
857 }
858 wl_surface::Event::PreferredBufferScale { factor } => {
859 // We use `WpFractionalScale` instead to set the scale if it's available
860 if state.globals.fractional_scale_manager.is_none() {
861 state.surface.set_buffer_scale(factor);
862 drop(state);
863 self.rescale(factor as f32);
864 }
865 }
866 _ => {}
867 }
868 }
869
870 pub fn handle_ime(&self, ime: ImeInput) {
871 if self.is_blocked() {
872 return;
873 }
874 let mut state = self.state.borrow_mut();
875 if let Some(mut input_handler) = state.input_handler.take() {
876 drop(state);
877 match ime {
878 ImeInput::InsertText(text) => {
879 input_handler.replace_text_in_range(None, &text);
880 }
881 ImeInput::SetMarkedText(text) => {
882 input_handler.replace_and_mark_text_in_range(None, &text, None);
883 }
884 ImeInput::UnmarkText => {
885 input_handler.unmark_text();
886 }
887 ImeInput::DeleteText => {
888 if let Some(marked) = input_handler.marked_text_range() {
889 input_handler.replace_text_in_range(Some(marked), "");
890 }
891 }
892 }
893 self.state.borrow_mut().input_handler = Some(input_handler);
894 }
895 }
896
897 pub fn get_ime_area(&self) -> Option<Bounds<Pixels>> {
898 let mut state = self.state.borrow_mut();
899 let mut bounds: Option<Bounds<Pixels>> = None;
900 if let Some(mut input_handler) = state.input_handler.take() {
901 drop(state);
902 if let Some(selection) = input_handler.marked_text_range() {
903 bounds = input_handler.bounds_for_range(selection.start..selection.start);
904 }
905 self.state.borrow_mut().input_handler = Some(input_handler);
906 }
907 bounds
908 }
909
910 pub fn set_size_and_scale(&self, size: Option<Size<Pixels>>, scale: Option<f32>) {
911 let (size, scale) = {
912 let mut state = self.state.borrow_mut();
913 if size.is_none_or(|size| size == state.bounds.size)
914 && scale.is_none_or(|scale| scale == state.scale)
915 {
916 return;
917 }
918 if let Some(size) = size {
919 state.bounds.size = size;
920 }
921 if let Some(scale) = scale {
922 state.scale = scale;
923 }
924 let device_bounds = state.bounds.to_device_pixels(state.scale);
925 state.renderer.update_drawable_size(device_bounds.size);
926 (state.bounds.size, state.scale)
927 };
928
929 if let Some(ref mut fun) = self.callbacks.borrow_mut().resize {
930 fun(size, scale);
931 }
932
933 {
934 let state = self.state.borrow();
935 if let Some(viewport) = &state.viewport {
936 viewport.set_destination(size.width.0 as i32, size.height.0 as i32);
937 }
938 }
939 }
940
941 pub fn resize(&self, size: Size<Pixels>) {
942 self.set_size_and_scale(Some(size), None);
943 }
944
945 pub fn rescale(&self, scale: f32) {
946 self.set_size_and_scale(None, Some(scale));
947 }
948
949 pub fn close(&self) {
950 let state = self.state.borrow();
951 let client = state.client.get_client();
952 #[allow(clippy::mutable_key_type)]
953 let children = state.children.clone();
954 drop(state);
955
956 for child in children {
957 let mut client_state = client.borrow_mut();
958 let window = get_window(&mut client_state, &child);
959 drop(client_state);
960
961 if let Some(child) = window {
962 child.close();
963 }
964 }
965 let mut callbacks = self.callbacks.borrow_mut();
966 if let Some(fun) = callbacks.close.take() {
967 fun()
968 }
969 }
970
971 pub fn handle_input(&self, input: PlatformInput) {
972 if self.is_blocked() {
973 return;
974 }
975 if let Some(ref mut fun) = self.callbacks.borrow_mut().input
976 && !fun(input.clone()).propagate
977 {
978 return;
979 }
980 if let PlatformInput::KeyDown(event) = input
981 && event.keystroke.modifiers.is_subset_of(&Modifiers::shift())
982 && let Some(key_char) = &event.keystroke.key_char
983 {
984 let mut state = self.state.borrow_mut();
985 if let Some(mut input_handler) = state.input_handler.take() {
986 drop(state);
987 input_handler.replace_text_in_range(None, key_char);
988 self.state.borrow_mut().input_handler = Some(input_handler);
989 }
990 }
991 }
992
993 pub fn set_focused(&self, focus: bool) {
994 self.state.borrow_mut().active = focus;
995 if let Some(ref mut fun) = self.callbacks.borrow_mut().active_status_change {
996 fun(focus);
997 }
998 }
999
1000 pub fn set_hovered(&self, focus: bool) {
1001 if let Some(ref mut fun) = self.callbacks.borrow_mut().hover_status_change {
1002 fun(focus);
1003 }
1004 }
1005
1006 pub fn set_appearance(&mut self, appearance: WindowAppearance) {
1007 self.state.borrow_mut().appearance = appearance;
1008
1009 let mut callbacks = self.callbacks.borrow_mut();
1010 if let Some(ref mut fun) = callbacks.appearance_changed {
1011 (fun)()
1012 }
1013 }
1014
1015 pub fn primary_output_scale(&self) -> i32 {
1016 self.state.borrow_mut().primary_output_scale()
1017 }
1018}
1019
1020fn extract_states<'a, S: TryFrom<u32> + 'a>(states: &'a [u8]) -> impl Iterator<Item = S> + 'a
1021where
1022 <S as TryFrom<u32>>::Error: 'a,
1023{
1024 states
1025 .chunks_exact(4)
1026 .flat_map(TryInto::<[u8; 4]>::try_into)
1027 .map(u32::from_ne_bytes)
1028 .flat_map(S::try_from)
1029}
1030
1031impl rwh::HasWindowHandle for WaylandWindow {
1032 fn window_handle(&self) -> Result<rwh::WindowHandle<'_>, rwh::HandleError> {
1033 let surface = self.0.surface().id().as_ptr() as *mut libc::c_void;
1034 let c_ptr = NonNull::new(surface).ok_or(rwh::HandleError::Unavailable)?;
1035 let handle = rwh::WaylandWindowHandle::new(c_ptr);
1036 let raw_handle = rwh::RawWindowHandle::Wayland(handle);
1037 Ok(unsafe { rwh::WindowHandle::borrow_raw(raw_handle) })
1038 }
1039}
1040
1041impl rwh::HasDisplayHandle for WaylandWindow {
1042 fn display_handle(&self) -> Result<rwh::DisplayHandle<'_>, rwh::HandleError> {
1043 let display = self
1044 .0
1045 .surface()
1046 .backend()
1047 .upgrade()
1048 .ok_or(rwh::HandleError::Unavailable)?
1049 .display_ptr() as *mut libc::c_void;
1050
1051 let c_ptr = NonNull::new(display).ok_or(rwh::HandleError::Unavailable)?;
1052 let handle = rwh::WaylandDisplayHandle::new(c_ptr);
1053 let raw_handle = rwh::RawDisplayHandle::Wayland(handle);
1054 Ok(unsafe { rwh::DisplayHandle::borrow_raw(raw_handle) })
1055 }
1056}
1057
1058impl PlatformWindow for WaylandWindow {
1059 fn bounds(&self) -> Bounds<Pixels> {
1060 self.borrow().bounds
1061 }
1062
1063 fn is_maximized(&self) -> bool {
1064 self.borrow().maximized
1065 }
1066
1067 fn window_bounds(&self) -> WindowBounds {
1068 let state = self.borrow();
1069 if state.fullscreen {
1070 WindowBounds::Fullscreen(state.window_bounds)
1071 } else if state.maximized {
1072 WindowBounds::Maximized(state.window_bounds)
1073 } else {
1074 drop(state);
1075 WindowBounds::Windowed(self.bounds())
1076 }
1077 }
1078
1079 fn inner_window_bounds(&self) -> WindowBounds {
1080 let state = self.borrow();
1081 if state.fullscreen {
1082 WindowBounds::Fullscreen(state.window_bounds)
1083 } else if state.maximized {
1084 WindowBounds::Maximized(state.window_bounds)
1085 } else {
1086 let inset = state.inset();
1087 drop(state);
1088 WindowBounds::Windowed(self.bounds().inset(inset))
1089 }
1090 }
1091
1092 fn content_size(&self) -> Size<Pixels> {
1093 self.borrow().bounds.size
1094 }
1095
1096 fn resize(&mut self, size: Size<Pixels>) {
1097 let state = self.borrow();
1098 let state_ptr = self.0.clone();
1099
1100 // Keep window geometry consistent with configure handling. On Wayland, window geometry is
1101 // surface-local: resizing should not attempt to translate the window; the compositor
1102 // controls placement. We also account for client-side decoration insets and tiling.
1103 let window_geometry = inset_by_tiling(
1104 Bounds {
1105 origin: Point::default(),
1106 size,
1107 },
1108 state.inset(),
1109 state.tiling,
1110 )
1111 .map(|v| v.0 as i32)
1112 .map_size(|v| if v <= 0 { 1 } else { v });
1113
1114 state.surface_state.set_geometry(
1115 window_geometry.origin.x,
1116 window_geometry.origin.y,
1117 window_geometry.size.width,
1118 window_geometry.size.height,
1119 );
1120
1121 state
1122 .globals
1123 .executor
1124 .spawn(async move { state_ptr.resize(size) })
1125 .detach();
1126 }
1127
1128 fn scale_factor(&self) -> f32 {
1129 self.borrow().scale
1130 }
1131
1132 fn appearance(&self) -> WindowAppearance {
1133 self.borrow().appearance
1134 }
1135
1136 fn display(&self) -> Option<Rc<dyn PlatformDisplay>> {
1137 let state = self.borrow();
1138 state.display.as_ref().map(|(id, display)| {
1139 Rc::new(WaylandDisplay {
1140 id: id.clone(),
1141 name: display.name.clone(),
1142 bounds: display.bounds.to_pixels(state.scale),
1143 }) as Rc<dyn PlatformDisplay>
1144 })
1145 }
1146
1147 fn mouse_position(&self) -> Point<Pixels> {
1148 self.borrow()
1149 .client
1150 .get_client()
1151 .borrow()
1152 .mouse_location
1153 .unwrap_or_default()
1154 }
1155
1156 fn modifiers(&self) -> Modifiers {
1157 self.borrow().client.get_client().borrow().modifiers
1158 }
1159
1160 fn capslock(&self) -> Capslock {
1161 self.borrow().client.get_client().borrow().capslock
1162 }
1163
1164 fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
1165 self.borrow_mut().input_handler = Some(input_handler);
1166 }
1167
1168 fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
1169 self.borrow_mut().input_handler.take()
1170 }
1171
1172 fn prompt(
1173 &self,
1174 _level: PromptLevel,
1175 _msg: &str,
1176 _detail: Option<&str>,
1177 _answers: &[PromptButton],
1178 ) -> Option<Receiver<usize>> {
1179 None
1180 }
1181
1182 fn activate(&self) {
1183 // Try to request an activation token. Even though the activation is likely going to be rejected,
1184 // KWin and Mutter can use the app_id to visually indicate we're requesting attention.
1185 let state = self.borrow();
1186 if let (Some(activation), Some(app_id)) = (&state.globals.activation, state.app_id.clone())
1187 {
1188 state.client.set_pending_activation(state.surface.id());
1189 let token = activation.get_activation_token(&state.globals.qh, ());
1190 // The serial isn't exactly important here, since the activation is probably going to be rejected anyway.
1191 let serial = state.client.get_serial(SerialKind::MousePress);
1192 token.set_app_id(app_id);
1193 token.set_serial(serial, &state.globals.seat);
1194 token.set_surface(&state.surface);
1195 token.commit();
1196 }
1197 }
1198
1199 fn is_active(&self) -> bool {
1200 self.borrow().active
1201 }
1202
1203 fn is_hovered(&self) -> bool {
1204 self.borrow().hovered
1205 }
1206
1207 fn set_title(&mut self, title: &str) {
1208 if let Some(toplevel) = self.borrow().surface_state.toplevel() {
1209 toplevel.set_title(title.to_string());
1210 }
1211 }
1212
1213 fn set_app_id(&mut self, app_id: &str) {
1214 let mut state = self.borrow_mut();
1215 if let Some(toplevel) = state.surface_state.toplevel() {
1216 toplevel.set_app_id(app_id.to_owned());
1217 }
1218 state.app_id = Some(app_id.to_owned());
1219 }
1220
1221 fn set_background_appearance(&self, background_appearance: WindowBackgroundAppearance) {
1222 let mut state = self.borrow_mut();
1223 state.background_appearance = background_appearance;
1224 update_window(state);
1225 }
1226
1227 fn background_appearance(&self) -> WindowBackgroundAppearance {
1228 self.borrow().background_appearance
1229 }
1230
1231 fn is_subpixel_rendering_supported(&self) -> bool {
1232 let client = self.borrow().client.get_client();
1233 let state = client.borrow();
1234 state
1235 .gpu_context
1236 .as_ref()
1237 .is_some_and(|ctx| ctx.supports_dual_source_blending())
1238 }
1239
1240 fn minimize(&self) {
1241 if let Some(toplevel) = self.borrow().surface_state.toplevel() {
1242 toplevel.set_minimized();
1243 }
1244 }
1245
1246 fn zoom(&self) {
1247 let state = self.borrow();
1248 if let Some(toplevel) = state.surface_state.toplevel() {
1249 if !state.maximized {
1250 toplevel.set_maximized();
1251 } else {
1252 toplevel.unset_maximized();
1253 }
1254 }
1255 }
1256
1257 fn toggle_fullscreen(&self) {
1258 let mut state = self.borrow();
1259 if let Some(toplevel) = state.surface_state.toplevel() {
1260 if !state.fullscreen {
1261 toplevel.set_fullscreen(None);
1262 } else {
1263 toplevel.unset_fullscreen();
1264 }
1265 }
1266 }
1267
1268 fn is_fullscreen(&self) -> bool {
1269 self.borrow().fullscreen
1270 }
1271
1272 fn on_request_frame(&self, callback: Box<dyn FnMut(RequestFrameOptions)>) {
1273 self.0.callbacks.borrow_mut().request_frame = Some(callback);
1274 }
1275
1276 fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> crate::DispatchEventResult>) {
1277 self.0.callbacks.borrow_mut().input = Some(callback);
1278 }
1279
1280 fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
1281 self.0.callbacks.borrow_mut().active_status_change = Some(callback);
1282 }
1283
1284 fn on_hover_status_change(&self, callback: Box<dyn FnMut(bool)>) {
1285 self.0.callbacks.borrow_mut().hover_status_change = Some(callback);
1286 }
1287
1288 fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
1289 self.0.callbacks.borrow_mut().resize = Some(callback);
1290 }
1291
1292 fn on_moved(&self, callback: Box<dyn FnMut()>) {
1293 self.0.callbacks.borrow_mut().moved = Some(callback);
1294 }
1295
1296 fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>) {
1297 self.0.callbacks.borrow_mut().should_close = Some(callback);
1298 }
1299
1300 fn on_close(&self, callback: Box<dyn FnOnce()>) {
1301 self.0.callbacks.borrow_mut().close = Some(callback);
1302 }
1303
1304 fn on_hit_test_window_control(&self, _callback: Box<dyn FnMut() -> Option<WindowControlArea>>) {
1305 }
1306
1307 fn on_appearance_changed(&self, callback: Box<dyn FnMut()>) {
1308 self.0.callbacks.borrow_mut().appearance_changed = Some(callback);
1309 }
1310
1311 fn draw(&self, scene: &Scene) {
1312 let mut state = self.borrow_mut();
1313 state.renderer.draw(scene);
1314 }
1315
1316 fn completed_frame(&self) {
1317 let state = self.borrow();
1318 state.surface.commit();
1319 }
1320
1321 fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
1322 let state = self.borrow();
1323 state.renderer.sprite_atlas().clone()
1324 }
1325
1326 fn show_window_menu(&self, position: Point<Pixels>) {
1327 let state = self.borrow();
1328 let serial = state.client.get_serial(SerialKind::MousePress);
1329 if let Some(toplevel) = state.surface_state.toplevel() {
1330 toplevel.show_window_menu(
1331 &state.globals.seat,
1332 serial,
1333 position.x.0 as i32,
1334 position.y.0 as i32,
1335 );
1336 }
1337 }
1338
1339 fn start_window_move(&self) {
1340 let state = self.borrow();
1341 let serial = state.client.get_serial(SerialKind::MousePress);
1342 if let Some(toplevel) = state.surface_state.toplevel() {
1343 toplevel._move(&state.globals.seat, serial);
1344 }
1345 }
1346
1347 fn start_window_resize(&self, edge: crate::ResizeEdge) {
1348 let state = self.borrow();
1349 if let Some(toplevel) = state.surface_state.toplevel() {
1350 toplevel.resize(
1351 &state.globals.seat,
1352 state.client.get_serial(SerialKind::MousePress),
1353 edge.to_xdg(),
1354 )
1355 }
1356 }
1357
1358 fn window_decorations(&self) -> Decorations {
1359 let state = self.borrow();
1360 match state.decorations {
1361 WindowDecorations::Server => Decorations::Server,
1362 WindowDecorations::Client => Decorations::Client {
1363 tiling: state.tiling,
1364 },
1365 }
1366 }
1367
1368 fn request_decorations(&self, decorations: WindowDecorations) {
1369 let mut state = self.borrow_mut();
1370 match state.surface_state.decoration().as_ref() {
1371 Some(decoration) => {
1372 decoration.set_mode(decorations.to_xdg());
1373 state.decorations = decorations;
1374 update_window(state);
1375 }
1376 None => {
1377 if matches!(decorations, WindowDecorations::Server) {
1378 log::info!(
1379 "Server-side decorations requested, but the Wayland server does not support them. Falling back to client-side decorations."
1380 );
1381 }
1382 state.decorations = WindowDecorations::Client;
1383 update_window(state);
1384 }
1385 }
1386 }
1387
1388 fn window_controls(&self) -> WindowControls {
1389 self.borrow().window_controls
1390 }
1391
1392 fn set_client_inset(&self, inset: Pixels) {
1393 let mut state = self.borrow_mut();
1394 if Some(inset) != state.client_inset {
1395 state.client_inset = Some(inset);
1396 update_window(state);
1397 }
1398 }
1399
1400 fn update_ime_position(&self, bounds: Bounds<Pixels>) {
1401 let state = self.borrow();
1402 state.client.update_ime_position(bounds);
1403 }
1404
1405 fn gpu_specs(&self) -> Option<GpuSpecs> {
1406 self.borrow().renderer.gpu_specs().into()
1407 }
1408}
1409
1410fn update_window(mut state: RefMut<WaylandWindowState>) {
1411 let opaque = !state.is_transparent();
1412
1413 state.renderer.update_transparency(!opaque);
1414 let mut opaque_area = state.window_bounds.map(|v| v.0 as i32);
1415 opaque_area.inset(state.inset().0 as i32);
1416
1417 let region = state
1418 .globals
1419 .compositor
1420 .create_region(&state.globals.qh, ());
1421 region.add(
1422 opaque_area.origin.x,
1423 opaque_area.origin.y,
1424 opaque_area.size.width,
1425 opaque_area.size.height,
1426 );
1427
1428 // Note that rounded corners make this rectangle API hard to work with.
1429 // As this is common when using CSD, let's just disable this API.
1430 if state.background_appearance == WindowBackgroundAppearance::Opaque
1431 && state.decorations == WindowDecorations::Server
1432 {
1433 // Promise the compositor that this region of the window surface
1434 // contains no transparent pixels. This allows the compositor to skip
1435 // updating whatever is behind the surface for better performance.
1436 state.surface.set_opaque_region(Some(®ion));
1437 } else {
1438 state.surface.set_opaque_region(None);
1439 }
1440
1441 if let Some(ref blur_manager) = state.globals.blur_manager {
1442 if state.background_appearance == WindowBackgroundAppearance::Blurred {
1443 if state.blur.is_none() {
1444 let blur = blur_manager.create(&state.surface, &state.globals.qh, ());
1445 state.blur = Some(blur);
1446 }
1447 state.blur.as_ref().unwrap().commit();
1448 } else {
1449 // It probably doesn't hurt to clear the blur for opaque windows
1450 blur_manager.unset(&state.surface);
1451 if let Some(b) = state.blur.take() {
1452 b.release()
1453 }
1454 }
1455 }
1456
1457 region.destroy();
1458}
1459
1460impl WindowDecorations {
1461 fn to_xdg(self) -> zxdg_toplevel_decoration_v1::Mode {
1462 match self {
1463 WindowDecorations::Client => zxdg_toplevel_decoration_v1::Mode::ClientSide,
1464 WindowDecorations::Server => zxdg_toplevel_decoration_v1::Mode::ServerSide,
1465 }
1466 }
1467}
1468
1469impl ResizeEdge {
1470 fn to_xdg(self) -> xdg_toplevel::ResizeEdge {
1471 match self {
1472 ResizeEdge::Top => xdg_toplevel::ResizeEdge::Top,
1473 ResizeEdge::TopRight => xdg_toplevel::ResizeEdge::TopRight,
1474 ResizeEdge::Right => xdg_toplevel::ResizeEdge::Right,
1475 ResizeEdge::BottomRight => xdg_toplevel::ResizeEdge::BottomRight,
1476 ResizeEdge::Bottom => xdg_toplevel::ResizeEdge::Bottom,
1477 ResizeEdge::BottomLeft => xdg_toplevel::ResizeEdge::BottomLeft,
1478 ResizeEdge::Left => xdg_toplevel::ResizeEdge::Left,
1479 ResizeEdge::TopLeft => xdg_toplevel::ResizeEdge::TopLeft,
1480 }
1481 }
1482}
1483
1484/// The configuration event is in terms of the window geometry, which we are constantly
1485/// updating to account for the client decorations. But that's not the area we want to render
1486/// to, due to our intrusize CSD. So, here we calculate the 'actual' size, by adding back in the insets
1487fn compute_outer_size(
1488 inset: Pixels,
1489 new_size: Option<Size<Pixels>>,
1490 tiling: Tiling,
1491) -> Option<Size<Pixels>> {
1492 new_size.map(|mut new_size| {
1493 if !tiling.top {
1494 new_size.height += inset;
1495 }
1496 if !tiling.bottom {
1497 new_size.height += inset;
1498 }
1499 if !tiling.left {
1500 new_size.width += inset;
1501 }
1502 if !tiling.right {
1503 new_size.width += inset;
1504 }
1505
1506 new_size
1507 })
1508}
1509
1510fn inset_by_tiling(mut bounds: Bounds<Pixels>, inset: Pixels, tiling: Tiling) -> Bounds<Pixels> {
1511 if !tiling.top {
1512 bounds.origin.y += inset;
1513 bounds.size.height -= inset;
1514 }
1515 if !tiling.bottom {
1516 bounds.size.height -= inset;
1517 }
1518 if !tiling.left {
1519 bounds.origin.x += inset;
1520 bounds.size.width -= inset;
1521 }
1522 if !tiling.right {
1523 bounds.size.width -= inset;
1524 }
1525
1526 bounds
1527}