use layer_shika_domain::value_objects::popup_config::PopupConfig; use log::info; use slint::PhysicalSize; use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1; use std::rc::Rc; use wayland_client::{ protocol::{wl_compositor::WlCompositor, wl_seat::WlSeat, wl_surface::WlSurface}, QueueHandle, }; use wayland_protocols::wp::fractional_scale::v1::client::{ wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1, wp_fractional_scale_v1::WpFractionalScaleV1, }; use wayland_protocols::wp::viewporter::client::{ wp_viewport::WpViewport, wp_viewporter::WpViewporter, }; use wayland_protocols::xdg::shell::client::{ xdg_popup::XdgPopup, xdg_positioner::{Anchor, ConstraintAdjustment, Gravity, XdgPositioner}, xdg_surface::XdgSurface, xdg_wm_base::XdgWmBase, }; use super::surface_state::WindowState; #[allow(dead_code)] pub struct PopupSurfaceParams<'a> { pub compositor: &'a WlCompositor, pub xdg_wm_base: &'a XdgWmBase, pub parent_layer_surface: &'a ZwlrLayerSurfaceV1, pub fractional_scale_manager: Option<&'a WpFractionalScaleManagerV1>, pub viewporter: Option<&'a WpViewporter>, pub queue_handle: &'a QueueHandle, pub popup_config: PopupConfig, pub physical_size: PhysicalSize, pub scale_factor: f32, } #[allow(dead_code)] pub struct PopupSurface { pub surface: Rc, pub xdg_surface: Rc, pub xdg_popup: Rc, pub fractional_scale: Option>, pub viewport: Option>, } #[allow(dead_code)] impl PopupSurface { pub fn create(params: &PopupSurfaceParams<'_>) -> Self { let surface = Rc::new(params.compositor.create_surface(params.queue_handle, ())); let xdg_surface = Rc::new(params.xdg_wm_base.get_xdg_surface( &surface, params.queue_handle, (), )); let positioner = Self::create_positioner(params); let xdg_popup = Rc::new(xdg_surface.get_popup(None, &positioner, params.queue_handle, ())); info!("Attaching popup to layer surface via get_popup"); params.parent_layer_surface.get_popup(&xdg_popup); let fractional_scale = params.fractional_scale_manager.map(|manager| { info!("Creating fractional scale object for popup surface"); Rc::new(manager.get_fractional_scale(&surface, params.queue_handle, ())) }); let viewport = params.viewporter.map(|vp| { info!("Creating viewport for popup surface"); Rc::new(vp.get_viewport(&surface, params.queue_handle, ())) }); #[allow(clippy::cast_possible_wrap)] #[allow(clippy::cast_precision_loss)] #[allow(clippy::cast_possible_truncation)] if let Some(ref vp) = viewport { let logical_width = (params.physical_size.width as f32 / params.scale_factor) as i32; let logical_height = (params.physical_size.height as f32 / params.scale_factor) as i32; info!( "Setting viewport destination to logical size: {}x{} (physical: {}x{}, scale: {})", logical_width, logical_height, params.physical_size.width, params.physical_size.height, params.scale_factor ); vp.set_destination(logical_width, logical_height); } surface.set_buffer_scale(1); Self { surface, xdg_surface, xdg_popup, fractional_scale, viewport, } } #[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_wrap)] #[allow(clippy::cast_sign_loss)] #[allow(clippy::cast_precision_loss)] fn create_positioner(params: &PopupSurfaceParams<'_>) -> XdgPositioner { let positioner = params .xdg_wm_base .create_positioner(params.queue_handle, ()); let calculated_x = params.popup_config.calculated_top_left_x() as i32; let calculated_y = params.popup_config.calculated_top_left_y() as i32; info!( "Popup positioning: reference=({}, {}), mode={:?}, calculated_top_left=({}, {})", params.popup_config.reference_x(), params.popup_config.reference_y(), params.popup_config.positioning_mode(), calculated_x, calculated_y ); let logical_width = (params.physical_size.width as f32 / params.scale_factor) as i32; let logical_height = (params.physical_size.height as f32 / params.scale_factor) as i32; positioner.set_anchor_rect(calculated_x, calculated_y, 1, 1); positioner.set_size(logical_width, logical_height); positioner.set_anchor(Anchor::TopLeft); positioner.set_gravity(Gravity::BottomRight); positioner.set_constraint_adjustment(ConstraintAdjustment::None); positioner } pub fn grab(&self, seat: &WlSeat, serial: u32) { info!("Grabbing popup with serial {serial}"); self.xdg_popup.grab(seat, serial); } pub fn destroy(&self) { info!("Destroying popup surface"); self.xdg_popup.destroy(); self.xdg_surface.destroy(); self.surface.destroy(); } }