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