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