git.lucas.co / cce-ui
GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git

src/wayland.rs (4.7K)

  1 use raw_window_handle::{
  2     DisplayHandle, HandleError, HasDisplayHandle, HasWindowHandle, RawDisplayHandle,
  3     RawWindowHandle, WaylandDisplayHandle, WaylandWindowHandle, WindowHandle,
  4 };
  5 use smithay_client_toolkit::output::OutputState;
  6 
  7 #[derive(Debug, Clone, Copy)]
  8 pub struct WaylandSurfaceHandle {
  9     pub display_ptr: *mut std::ffi::c_void,
 10     pub surface_ptr: *mut std::ffi::c_void,
 11 }
 12 
 13 unsafe impl Send for WaylandSurfaceHandle {}
 14 unsafe impl Sync for WaylandSurfaceHandle {}
 15 
 16 impl HasDisplayHandle for WaylandSurfaceHandle {
 17     fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
 18         let raw = RawDisplayHandle::Wayland(WaylandDisplayHandle::new(
 19             std::ptr::NonNull::new(self.display_ptr).ok_or(HandleError::Unavailable)?,
 20         ));
 21         unsafe { Ok(DisplayHandle::borrow_raw(raw)) }
 22     }
 23 }
 24 
 25 impl HasWindowHandle for WaylandSurfaceHandle {
 26     fn window_handle(&self) -> Result<WindowHandle<'_>, HandleError> {
 27         let raw = RawWindowHandle::Wayland(WaylandWindowHandle::new(
 28             std::ptr::NonNull::new(self.surface_ptr).ok_or(HandleError::Unavailable)?,
 29         ));
 30         unsafe { Ok(WindowHandle::borrow_raw(raw)) }
 31     }
 32 }
 33 
 34 /// Helper function to detect the initial display scale factor from Wayland output state.
 35 /// Iterates over all active outputs and returns the maximum scale factor found (defaulting to 1.0).
 36 pub fn detect_scale_factor(output_state: &OutputState) -> f64 {
 37     if let Some(forced) = crate::scale::forced_scale() {
 38         return forced as f64;
 39     }
 40     let mut max_scale = 1.0;
 41     for output in output_state.outputs() {
 42         if let Some(info) = output_state.info(&output) {
 43             let scale = info.scale_factor as f64;
 44             if scale > max_scale {
 45                 max_scale = scale;
 46             }
 47         }
 48     }
 49     max_scale
 50 }
 51 
 52 /// The display metric (logical px per mm) for the output `detect_scale_factor`
 53 /// chose — the same selection rule, so scale and metric describe one
 54 /// display. Measured from the output's `wl_output` geometry (EDID physical
 55 /// size, or the compositor's configured override in its place — the client
 56 /// cannot tell the two apart, and reports "measured" for both; `ccectl
 57 /// outputs` says which) against its logical size: xdg-output's when the
 58 /// compositor sends one (exact under fractional scale), else the current
 59 /// mode divided by the integer `wl_output` scale. Outputs with no physical
 60 /// size (headless, a virtual output, an EDID-less projector) or an
 61 /// implausible one fall back to the assumed CSS metric, flagged as such.
 62 pub fn detect_metric(output_state: &OutputState, scale: f64) -> crate::units::Metric {
 63     use crate::units::{Metric, MetricSource};
 64     let scale_f = scale as f32;
 65     let mut best: Option<Metric> = None;
 66     for output in output_state.outputs() {
 67         let Some(info) = output_state.info(&output) else { continue };
 68         if (info.scale_factor as f64) < scale && crate::scale::forced_scale().is_none() {
 69             // Not the display the scale was taken from.
 70             continue;
 71         }
 72         let (mm_w, mm_h) = info.physical_size;
 73         if mm_w <= 0 || mm_h <= 0 {
 74             continue;
 75         }
 76         let logical = match info.logical_size {
 77             Some((w, h)) if w > 0 && h > 0 => (w as f32, h as f32),
 78             _ => {
 79                 let Some(mode) = info.modes.iter().find(|m| m.current) else { continue };
 80                 let s = (info.scale_factor.max(1)) as f32;
 81                 (mode.dimensions.0 as f32 / s, mode.dimensions.1 as f32 / s)
 82             }
 83         };
 84         if let Some(m) = Metric::from_sizes(scale_f, logical, (mm_w as f32, mm_h as f32), MetricSource::Measured) {
 85             best = Some(m);
 86             break;
 87         }
 88     }
 89     best.unwrap_or_else(|| Metric::assumed(scale_f))
 90 }
 91 
 92 /// Helper to convert a logical pointer position (from Wayland/SCTK events)
 93 /// to physical pixel coordinates based on the display scale factor.
 94 pub fn scale_pointer_pos(pos: (f64, f64), scale: f64) -> (f32, f32) {
 95     ((pos.0 * scale) as f32, (pos.1 * scale) as f32)
 96 }
 97 
 98 #[macro_export]
 99 macro_rules! delegate_wl_callback {
100     ($name:ty) => {
101         impl wayland_client::Dispatch<wayland_client::protocol::wl_callback::WlCallback, ()> for $name {
102             fn event(
103                 state: &mut Self,
104                 _proxy: &wayland_client::protocol::wl_callback::WlCallback,
105                 event: wayland_client::protocol::wl_callback::Event,
106                 _data: &(),
107                 _conn: &wayland_client::Connection,
108                 _qh: &wayland_client::QueueHandle<Self>,
109             ) {
110                 if let wayland_client::protocol::wl_callback::Event::Done { .. } = event {
111                     state.frame_callback_pending = false;
112                 }
113             }
114         }
115     };
116 }
117