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