Skip to main content

blit_compositor/
lib.rs

1#[cfg(target_os = "linux")]
2mod imp;
3#[cfg(target_os = "linux")]
4mod input_region;
5// Compiled everywhere so its tests run on any host; only `imp` consumes it.
6#[cfg_attr(not(target_os = "linux"), allow(dead_code))]
7mod pointer_focus;
8#[cfg(target_os = "linux")]
9mod positioner;
10#[cfg(target_os = "linux")]
11mod render;
12#[cfg(target_os = "linux")]
13mod vulkan_encode;
14#[cfg(target_os = "linux")]
15mod vulkan_render;
16#[cfg(target_os = "linux")]
17pub use imp::*;
18
19#[cfg(not(target_os = "linux"))]
20mod stub {
21    use std::sync::Arc;
22    use std::sync::atomic::AtomicBool;
23    use std::sync::mpsc;
24
25    pub mod drm_fourcc {
26        pub const ARGB8888: u32 = u32::from_le_bytes(*b"AR24");
27        pub const XRGB8888: u32 = u32::from_le_bytes(*b"XR24");
28        pub const ABGR8888: u32 = u32::from_le_bytes(*b"AB24");
29        pub const XBGR8888: u32 = u32::from_le_bytes(*b"XB24");
30        pub const NV12: u32 = u32::from_le_bytes(*b"NV12");
31    }
32
33    /// Placeholder for `std::os::fd::OwnedFd` on non-Unix platforms.
34    #[derive(Debug)]
35    pub struct OwnedFd(());
36
37    #[derive(Clone)]
38    pub enum PixelData {
39        Bgra(Arc<Vec<u8>>),
40        Rgba(Arc<Vec<u8>>),
41        Nv12 {
42            data: Arc<Vec<u8>>,
43            y_stride: usize,
44            uv_stride: usize,
45        },
46        DmaBuf {
47            fd: Arc<OwnedFd>,
48            fourcc: u32,
49            modifier: u64,
50            stride: u32,
51            offset: u32,
52        },
53        Nv12DmaBuf {
54            fd: Arc<OwnedFd>,
55            stride: u32,
56            uv_offset: u32,
57            width: u32,
58            height: u32,
59            sync_fd: Option<Arc<OwnedFd>>,
60        },
61        VaSurface {
62            surface_id: u32,
63            va_display: usize,
64            _fd: Arc<OwnedFd>,
65        },
66        Encoded {
67            data: Arc<Vec<u8>>,
68            is_keyframe: bool,
69            codec_flag: u8,
70        },
71    }
72
73    impl PixelData {
74        pub fn to_rgba(&self, _width: u32, _height: u32) -> Vec<u8> {
75            match self {
76                PixelData::Rgba(data) => data.as_ref().clone(),
77                PixelData::Bgra(data) => {
78                    let mut rgba = Vec::with_capacity(data.len());
79                    for px in data.chunks_exact(4) {
80                        rgba.extend_from_slice(&[px[2], px[1], px[0], px[3]]);
81                    }
82                    rgba
83                }
84                _ => Vec::new(),
85            }
86        }
87
88        pub fn is_empty(&self) -> bool {
89            match self {
90                PixelData::Bgra(v) | PixelData::Rgba(v) => v.is_empty(),
91                PixelData::Nv12 { data, .. } => data.is_empty(),
92                PixelData::DmaBuf { .. }
93                | PixelData::VaSurface { .. }
94                | PixelData::Nv12DmaBuf { .. } => false,
95                PixelData::Encoded { data, .. } => data.is_empty(),
96            }
97        }
98
99        pub fn is_dmabuf(&self) -> bool {
100            matches!(self, PixelData::DmaBuf { .. })
101        }
102
103        pub fn is_va_surface(&self) -> bool {
104            matches!(self, PixelData::VaSurface { .. })
105        }
106    }
107
108    #[derive(Clone)]
109    pub enum CursorImage {
110        Named(String),
111        Custom {
112            hotspot_x: u16,
113            hotspot_y: u16,
114            width: u16,
115            height: u16,
116            rgba: Vec<u8>,
117        },
118        Hidden,
119    }
120
121    pub enum CompositorEvent {
122        SurfaceCreated {
123            surface_id: u16,
124            title: String,
125            app_id: String,
126            parent_id: u16,
127            width: u16,
128            height: u16,
129        },
130        SurfaceDestroyed {
131            surface_id: u16,
132        },
133        SurfaceCommit {
134            surface_id: u16,
135            width: u32,
136            height: u32,
137            pixels: PixelData,
138            timestamp_ms: u32,
139        },
140        SurfaceTitle {
141            surface_id: u16,
142            title: String,
143        },
144        SurfaceAppId {
145            surface_id: u16,
146            app_id: String,
147        },
148        SurfaceResized {
149            surface_id: u16,
150            width: u16,
151            height: u16,
152        },
153        ClipboardContent {
154            surface_id: u16,
155            mime_type: String,
156            data: Vec<u8>,
157        },
158        SurfaceCursor {
159            surface_id: u16,
160            cursor: CursorImage,
161        },
162    }
163
164    pub enum CompositorCommand {
165        KeyInput {
166            surface_id: u16,
167            keycode: u32,
168            pressed: bool,
169        },
170        PointerMotion {
171            surface_id: u16,
172            x: f64,
173            y: f64,
174        },
175        PointerButton {
176            surface_id: u16,
177            button: u32,
178            pressed: bool,
179        },
180        PointerAxis {
181            surface_id: u16,
182            axis: u8,
183            value: f64,
184        },
185        SurfaceResize {
186            surface_id: u16,
187            width: u16,
188            height: u16,
189            scale_120: u16,
190        },
191        SurfaceFocus {
192            surface_id: u16,
193        },
194        SurfaceClose {
195            surface_id: u16,
196        },
197        ClipboardOffer {
198            mime_type: String,
199            data: Vec<u8>,
200        },
201        /// List available clipboard MIME types.
202        ClipboardListMimes {
203            reply: mpsc::SyncSender<Vec<String>>,
204        },
205        /// Read clipboard content for a specific MIME type.
206        ClipboardGet {
207            mime_type: String,
208            reply: mpsc::SyncSender<Option<Vec<u8>>>,
209        },
210        /// Composed text from the browser (e.g. IME or shifted characters
211        /// that don't match the compositor's US-QWERTY keymap).  The compositor
212        /// synthesises evdev key sequences for ASCII chars and uses
213        /// zwp_text_input_v3 commit_string for non-ASCII.
214        TextInput {
215            text: String,
216        },
217        ReleaseKeys {
218            keycodes: Vec<u32>,
219        },
220        Capture {
221            surface_id: u16,
222            /// Render scale in 120ths. 0 = current output scale.
223            scale_120: u16,
224            reply: mpsc::SyncSender<Option<(u32, u32, Vec<u8>)>>,
225        },
226        /// Fire pending wl_surface.frame callbacks for a surface so the
227        /// client will paint and commit its next frame.  Send this when
228        /// the server is ready to consume a new frame (streaming or capture).
229        RequestFrame {
230            surface_id: u16,
231        },
232        SetExternalOutputBuffers {
233            surface_id: u32,
234            target_w: u32,
235            target_h: u32,
236            buffers: Vec<ExternalOutputBuffer>,
237        },
238        RegisterDownscaleTarget {
239            surface_id: u32,
240            target_w: u32,
241            target_h: u32,
242        },
243        ClearDownscaleTarget {
244            surface_id: u32,
245            target_w: u32,
246            target_h: u32,
247        },
248        /// Update the advertised output refresh rate (millihertz).
249        SetRefreshRate {
250            mhz: u32,
251        },
252        /// Set up a Vulkan Video encoder for a surface.
253        SetVulkanEncoder {
254            surface_id: u32,
255            codec: u8,
256            qp: u8,
257            width: u32,
258            height: u32,
259        },
260        /// Request a keyframe from the Vulkan Video encoder for a surface.
261        RequestVulkanKeyframe {
262            surface_id: u32,
263        },
264        /// Destroy the Vulkan Video encoder for a surface.
265        DestroyVulkanEncoder {
266            surface_id: u32,
267        },
268        Shutdown,
269    }
270
271    #[derive(Clone, Copy, Default)]
272    pub struct ExternalOutputPlane {
273        pub offset: u32,
274        pub pitch: u32,
275    }
276
277    pub struct ExternalOutputBuffer {
278        pub fd: Arc<OwnedFd>,
279        pub fourcc: u32,
280        pub modifier: u64,
281        pub stride: u32,
282        pub offset: u32,
283        pub width: u32,
284        pub height: u32,
285        pub va_surface_id: u32,
286        pub va_display: usize,
287        pub planes: Vec<ExternalOutputPlane>,
288    }
289
290    pub struct CompositorHandle {
291        pub event_rx: mpsc::Receiver<CompositorEvent>,
292        pub command_tx: mpsc::Sender<CompositorCommand>,
293        pub socket_name: String,
294        pub thread: std::thread::JoinHandle<()>,
295        pub shutdown: Arc<AtomicBool>,
296        /// Whether the compositor's Vulkan renderer supports Vulkan Video encode.
297        pub vulkan_video_encode: bool,
298        /// Whether the compositor's Vulkan renderer supports Vulkan Video AV1 encode.
299        pub vulkan_video_encode_av1: bool,
300    }
301
302    impl CompositorHandle {
303        /// Wake the compositor event loop immediately.
304        pub fn wake(&self) {}
305    }
306
307    pub fn spawn_compositor(
308        _verbose: bool,
309        _event_notify: Arc<dyn Fn() + Send + Sync>,
310        _gpu_device: &str,
311    ) -> CompositorHandle {
312        let (event_tx, event_rx) = mpsc::channel();
313        let (command_tx, _command_rx) = mpsc::channel();
314        let shutdown = Arc::new(AtomicBool::new(false));
315        // Drop the sender immediately so event_rx.recv() returns Err.
316        drop(event_tx);
317        CompositorHandle {
318            event_rx,
319            command_tx,
320            socket_name: String::new(),
321            thread: std::thread::spawn(|| {}),
322            shutdown,
323            vulkan_video_encode: false,
324            vulkan_video_encode_av1: false,
325        }
326    }
327}
328
329#[cfg(not(target_os = "linux"))]
330pub use stub::*;