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    }
67
68    /// A bitstream a compositor-resident encoder produced for exactly one
69    /// client.  Owned per `(surface_id, client_id)`, never shared.
70    pub struct EncodedFrame {
71        pub surface_id: u16,
72        pub client_id: u64,
73        pub width: u32,
74        pub height: u32,
75        pub data: Arc<Vec<u8>>,
76        pub is_keyframe: bool,
77        pub codec_flag: u8,
78    }
79
80    impl PixelData {
81        pub fn to_rgba(&self, _width: u32, _height: u32) -> Vec<u8> {
82            match self {
83                PixelData::Rgba(data) => data.as_ref().clone(),
84                PixelData::Bgra(data) => {
85                    let mut rgba = Vec::with_capacity(data.len());
86                    for px in data.chunks_exact(4) {
87                        rgba.extend_from_slice(&[px[2], px[1], px[0], px[3]]);
88                    }
89                    rgba
90                }
91                _ => Vec::new(),
92            }
93        }
94
95        pub fn is_empty(&self) -> bool {
96            match self {
97                PixelData::Bgra(v) | PixelData::Rgba(v) => v.is_empty(),
98                PixelData::Nv12 { data, .. } => data.is_empty(),
99                PixelData::DmaBuf { .. }
100                | PixelData::VaSurface { .. }
101                | PixelData::Nv12DmaBuf { .. } => false,
102            }
103        }
104
105        pub fn is_dmabuf(&self) -> bool {
106            matches!(self, PixelData::DmaBuf { .. })
107        }
108
109        pub fn is_va_surface(&self) -> bool {
110            matches!(self, PixelData::VaSurface { .. })
111        }
112    }
113
114    #[derive(Clone)]
115    pub enum CursorImage {
116        Named(String),
117        Custom {
118            hotspot_x: u16,
119            hotspot_y: u16,
120            width: u16,
121            height: u16,
122            rgba: Vec<u8>,
123        },
124        Hidden,
125    }
126
127    pub enum CompositorEvent {
128        SurfaceCreated {
129            surface_id: u16,
130            title: String,
131            app_id: String,
132            parent_id: u16,
133            width: u16,
134            height: u16,
135        },
136        SurfaceDestroyed {
137            surface_id: u16,
138        },
139        SurfaceCommit {
140            surface_id: u16,
141            width: u32,
142            height: u32,
143            pixels: PixelData,
144            timestamp_ms: u32,
145        },
146        SurfaceEncoded {
147            frame: EncodedFrame,
148            timestamp_ms: u32,
149        },
150        VulkanEncoderUnavailable {
151            surface_id: u16,
152            client_id: u64,
153        },
154        SurfaceTitle {
155            surface_id: u16,
156            title: String,
157        },
158        SurfaceAppId {
159            surface_id: u16,
160            app_id: String,
161        },
162        SurfaceResized {
163            surface_id: u16,
164            width: u16,
165            height: u16,
166        },
167        ClipboardContent {
168            surface_id: u16,
169            mime_type: String,
170            data: Vec<u8>,
171        },
172        SurfaceCursor {
173            surface_id: u16,
174            cursor: CursorImage,
175        },
176    }
177
178    pub enum CompositorCommand {
179        KeyInput {
180            surface_id: u16,
181            keycode: u32,
182            pressed: bool,
183        },
184        PointerMotion {
185            surface_id: u16,
186            x: f64,
187            y: f64,
188        },
189        PointerButton {
190            surface_id: u16,
191            button: u32,
192            pressed: bool,
193        },
194        PointerAxis {
195            surface_id: u16,
196            axis: u8,
197            value: f64,
198        },
199        SurfaceResize {
200            surface_id: u16,
201            width: u16,
202            height: u16,
203            scale_120: u16,
204        },
205        SurfaceFocus {
206            surface_id: u16,
207        },
208        SurfaceClose {
209            surface_id: u16,
210        },
211        ClipboardOffer {
212            mime_type: String,
213            data: Vec<u8>,
214        },
215        /// List available clipboard MIME types.
216        ClipboardListMimes {
217            reply: mpsc::SyncSender<Vec<String>>,
218        },
219        /// Read clipboard content for a specific MIME type.
220        ClipboardGet {
221            mime_type: String,
222            reply: mpsc::SyncSender<Option<Vec<u8>>>,
223        },
224        /// Composed text from the browser (e.g. IME or shifted characters
225        /// that don't match the compositor's US-QWERTY keymap).  The compositor
226        /// synthesises evdev key sequences for ASCII chars and uses
227        /// zwp_text_input_v3 commit_string for non-ASCII.
228        TextInput {
229            text: String,
230        },
231        ReleaseKeys {
232            keycodes: Vec<u32>,
233        },
234        Capture {
235            surface_id: u16,
236            /// Render scale in 120ths. 0 = current output scale.
237            scale_120: u16,
238            reply: mpsc::SyncSender<Option<(u32, u32, Vec<u8>)>>,
239        },
240        /// Fire pending wl_surface.frame callbacks for a surface so the
241        /// client will paint and commit its next frame.  Send this when
242        /// the server is ready to consume a new frame (streaming or capture).
243        RequestFrame {
244            surface_id: u16,
245        },
246        SetExternalOutputBuffers {
247            surface_id: u32,
248            target_w: u32,
249            target_h: u32,
250            buffers: Vec<ExternalOutputBuffer>,
251        },
252        RegisterDownscaleTarget {
253            surface_id: u32,
254            target_w: u32,
255            target_h: u32,
256        },
257        ClearDownscaleTarget {
258            surface_id: u32,
259            target_w: u32,
260            target_h: u32,
261        },
262        /// Update the advertised output refresh rate (millihertz).
263        SetRefreshRate {
264            mhz: u32,
265        },
266        /// Set up a Vulkan Video encoder for one `(surface, client)` pair.
267        SetVulkanEncoder {
268            surface_id: u32,
269            client_id: u64,
270            codec: u8,
271            qp: u8,
272            width: u32,
273            height: u32,
274        },
275        /// Retarget one client's encoder quantizer without rebuilding it.
276        SetVulkanEncoderQp {
277            surface_id: u32,
278            client_id: u64,
279            qp: u8,
280        },
281        /// Request a keyframe from one client's Vulkan Video encoder.
282        RequestVulkanKeyframe {
283            surface_id: u32,
284            client_id: u64,
285        },
286        /// Destroy Vulkan Video encoders for a surface: one client's when
287        /// `client_id` is `Some`, every client's when it is `None`.
288        DestroyVulkanEncoder {
289            surface_id: u32,
290            client_id: Option<u64>,
291        },
292        Shutdown,
293    }
294
295    #[derive(Clone, Copy, Default)]
296    pub struct ExternalOutputPlane {
297        pub offset: u32,
298        pub pitch: u32,
299    }
300
301    pub struct ExternalOutputBuffer {
302        pub fd: Arc<OwnedFd>,
303        pub fourcc: u32,
304        pub modifier: u64,
305        pub stride: u32,
306        pub offset: u32,
307        pub width: u32,
308        pub height: u32,
309        pub va_surface_id: u32,
310        pub va_display: usize,
311        pub planes: Vec<ExternalOutputPlane>,
312    }
313
314    pub struct CompositorHandle {
315        pub event_rx: mpsc::Receiver<CompositorEvent>,
316        pub command_tx: mpsc::Sender<CompositorCommand>,
317        pub socket_name: String,
318        pub thread: std::thread::JoinHandle<()>,
319        pub shutdown: Arc<AtomicBool>,
320        /// Whether the compositor's Vulkan renderer supports Vulkan Video encode.
321        pub vulkan_video_encode: bool,
322        /// Whether the compositor's Vulkan renderer supports Vulkan Video AV1 encode.
323        pub vulkan_video_encode_av1: bool,
324    }
325
326    impl CompositorHandle {
327        /// Wake the compositor event loop immediately.
328        pub fn wake(&self) {}
329    }
330
331    pub fn spawn_compositor(
332        _verbose: bool,
333        _event_notify: Arc<dyn Fn() + Send + Sync>,
334        _gpu_device: &str,
335    ) -> CompositorHandle {
336        let (event_tx, event_rx) = mpsc::channel();
337        let (command_tx, _command_rx) = mpsc::channel();
338        let shutdown = Arc::new(AtomicBool::new(false));
339        // Drop the sender immediately so event_rx.recv() returns Err.
340        drop(event_tx);
341        CompositorHandle {
342            event_rx,
343            command_tx,
344            socket_name: String::new(),
345            thread: std::thread::spawn(|| {}),
346            shutdown,
347            vulkan_video_encode: false,
348            vulkan_video_encode_av1: false,
349        }
350    }
351}
352
353#[cfg(not(target_os = "linux"))]
354pub use stub::*;