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        },
134        SurfaceTitle {
135            surface_id: u16,
136            title: String,
137        },
138        SurfaceAppId {
139            surface_id: u16,
140            app_id: String,
141        },
142        SurfaceResized {
143            surface_id: u16,
144            width: u16,
145            height: u16,
146        },
147        ClipboardContent {
148            surface_id: u16,
149            mime_type: String,
150            data: Vec<u8>,
151        },
152        SurfaceCursor {
153            surface_id: u16,
154            cursor: CursorImage,
155        },
156    }
157
158    pub enum CompositorCommand {
159        KeyInput {
160            surface_id: u16,
161            keycode: u32,
162            pressed: bool,
163        },
164        PointerMotion {
165            surface_id: u16,
166            x: f64,
167            y: f64,
168        },
169        PointerButton {
170            surface_id: u16,
171            button: u32,
172            pressed: bool,
173        },
174        PointerAxis {
175            surface_id: u16,
176            axis: u8,
177            value: f64,
178        },
179        SurfaceResize {
180            surface_id: u16,
181            width: u16,
182            height: u16,
183            scale_120: u16,
184        },
185        SurfaceFocus {
186            surface_id: u16,
187        },
188        SurfaceClose {
189            surface_id: u16,
190        },
191        ClipboardOffer {
192            mime_type: String,
193            data: Vec<u8>,
194        },
195        /// List available clipboard MIME types.
196        ClipboardListMimes {
197            reply: mpsc::SyncSender<Vec<String>>,
198        },
199        /// Read clipboard content for a specific MIME type.
200        ClipboardGet {
201            mime_type: String,
202            reply: mpsc::SyncSender<Option<Vec<u8>>>,
203        },
204        /// Composed text from the browser (e.g. IME or shifted characters
205        /// that don't match the compositor's US-QWERTY keymap).  The compositor
206        /// synthesises evdev key sequences for ASCII chars and uses
207        /// zwp_text_input_v3 commit_string for non-ASCII.
208        TextInput {
209            text: String,
210        },
211        ReleaseKeys {
212            keycodes: Vec<u32>,
213        },
214        Capture {
215            surface_id: u16,
216            /// Render scale in 120ths. 0 = current output scale.
217            scale_120: u16,
218            reply: mpsc::SyncSender<Option<(u32, u32, Vec<u8>)>>,
219        },
220        /// Fire pending wl_surface.frame callbacks for a surface so the
221        /// client will paint and commit its next frame.  Send this when
222        /// the server is ready to consume a new frame (streaming or capture).
223        RequestFrame {
224            surface_id: u16,
225        },
226        SetExternalOutputBuffers {
227            surface_id: u32,
228            buffers: Vec<ExternalOutputBuffer>,
229        },
230        /// Update the advertised output refresh rate (millihertz).
231        SetRefreshRate {
232            mhz: u32,
233        },
234        /// Set up a Vulkan Video encoder for a surface.
235        SetVulkanEncoder {
236            surface_id: u32,
237            codec: u8,
238            qp: u8,
239            width: u32,
240            height: u32,
241        },
242        /// Request a keyframe from the Vulkan Video encoder for a surface.
243        RequestVulkanKeyframe {
244            surface_id: u32,
245        },
246        /// Destroy the Vulkan Video encoder for a surface.
247        DestroyVulkanEncoder {
248            surface_id: u32,
249        },
250        Shutdown,
251    }
252
253    #[derive(Clone, Copy, Default)]
254    pub struct ExternalOutputPlane {
255        pub offset: u32,
256        pub pitch: u32,
257    }
258
259    pub struct ExternalOutputBuffer {
260        pub fd: Arc<OwnedFd>,
261        pub fourcc: u32,
262        pub modifier: u64,
263        pub stride: u32,
264        pub offset: u32,
265        pub width: u32,
266        pub height: u32,
267        pub va_surface_id: u32,
268        pub va_display: usize,
269        pub planes: Vec<ExternalOutputPlane>,
270    }
271
272    pub struct CompositorHandle {
273        pub event_rx: mpsc::Receiver<CompositorEvent>,
274        pub command_tx: mpsc::Sender<CompositorCommand>,
275        pub socket_name: String,
276        pub thread: std::thread::JoinHandle<()>,
277        pub shutdown: Arc<AtomicBool>,
278        /// Whether the compositor's Vulkan renderer supports Vulkan Video encode.
279        pub vulkan_video_encode: bool,
280        /// Whether the compositor's Vulkan renderer supports Vulkan Video AV1 encode.
281        pub vulkan_video_encode_av1: bool,
282    }
283
284    impl CompositorHandle {
285        /// Wake the compositor event loop immediately.
286        pub fn wake(&self) {}
287    }
288
289    pub fn spawn_compositor(
290        _verbose: bool,
291        _event_notify: Arc<dyn Fn() + Send + Sync>,
292        _gpu_device: &str,
293    ) -> CompositorHandle {
294        let (event_tx, event_rx) = mpsc::channel();
295        let (command_tx, _command_rx) = mpsc::channel();
296        let shutdown = Arc::new(AtomicBool::new(false));
297        // Drop the sender immediately so event_rx.recv() returns Err.
298        drop(event_tx);
299        CompositorHandle {
300            event_rx,
301            command_tx,
302            socket_name: String::new(),
303            thread: std::thread::spawn(|| {}),
304            shutdown,
305            vulkan_video_encode: false,
306            vulkan_video_encode_av1: false,
307        }
308    }
309}
310
311#[cfg(not(target_os = "linux"))]
312pub use stub::*;