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            buffers: Vec<ExternalOutputBuffer>,
230        },
231        /// Update the advertised output refresh rate (millihertz).
232        SetRefreshRate {
233            mhz: u32,
234        },
235        /// Set up a Vulkan Video encoder for a surface.
236        SetVulkanEncoder {
237            surface_id: u32,
238            codec: u8,
239            qp: u8,
240            width: u32,
241            height: u32,
242        },
243        /// Request a keyframe from the Vulkan Video encoder for a surface.
244        RequestVulkanKeyframe {
245            surface_id: u32,
246        },
247        /// Destroy the Vulkan Video encoder for a surface.
248        DestroyVulkanEncoder {
249            surface_id: u32,
250        },
251        Shutdown,
252    }
253
254    #[derive(Clone, Copy, Default)]
255    pub struct ExternalOutputPlane {
256        pub offset: u32,
257        pub pitch: u32,
258    }
259
260    pub struct ExternalOutputBuffer {
261        pub fd: Arc<OwnedFd>,
262        pub fourcc: u32,
263        pub modifier: u64,
264        pub stride: u32,
265        pub offset: u32,
266        pub width: u32,
267        pub height: u32,
268        pub va_surface_id: u32,
269        pub va_display: usize,
270        pub planes: Vec<ExternalOutputPlane>,
271    }
272
273    pub struct CompositorHandle {
274        pub event_rx: mpsc::Receiver<CompositorEvent>,
275        pub command_tx: mpsc::Sender<CompositorCommand>,
276        pub socket_name: String,
277        pub thread: std::thread::JoinHandle<()>,
278        pub shutdown: Arc<AtomicBool>,
279        /// Whether the compositor's Vulkan renderer supports Vulkan Video encode.
280        pub vulkan_video_encode: bool,
281        /// Whether the compositor's Vulkan renderer supports Vulkan Video AV1 encode.
282        pub vulkan_video_encode_av1: bool,
283    }
284
285    impl CompositorHandle {
286        /// Wake the compositor event loop immediately.
287        pub fn wake(&self) {}
288    }
289
290    pub fn spawn_compositor(
291        _verbose: bool,
292        _event_notify: Arc<dyn Fn() + Send + Sync>,
293        _gpu_device: &str,
294    ) -> CompositorHandle {
295        let (event_tx, event_rx) = mpsc::channel();
296        let (command_tx, _command_rx) = mpsc::channel();
297        let shutdown = Arc::new(AtomicBool::new(false));
298        // Drop the sender immediately so event_rx.recv() returns Err.
299        drop(event_tx);
300        CompositorHandle {
301            event_rx,
302            command_tx,
303            socket_name: String::new(),
304            thread: std::thread::spawn(|| {}),
305            shutdown,
306            vulkan_video_encode: false,
307            vulkan_video_encode_av1: false,
308        }
309    }
310}
311
312#[cfg(not(target_os = "linux"))]
313pub use stub::*;