Skip to main content

blit_compositor/
lib.rs

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