Skip to main content

libdrmtap_sys/
lib.rs

1/*
2 * libdrmtap-sys — Raw FFI bindings for libdrmtap
3 * https://github.com/fxd0h/libdrmtap
4 *
5 * Copyright (c) 2026 Mariano Abad <weimaraner@gmail.com>
6 * SPDX-License-Identifier: MIT
7 *
8 * Hand-written FFI bindings matching include/drmtap.h
9 * These are intentionally minimal — use libdrmtap-rs for safe wrappers.
10 */
11
12#![allow(non_camel_case_types)]
13
14use std::os::raw::{c_char, c_int, c_void};
15
16/// Opaque capture context
17pub enum drmtap_ctx {}
18
19/// Configuration for opening a capture context
20#[repr(C)]
21pub struct drmtap_config {
22    pub device_path: *const c_char,
23    pub crtc_id: u32,
24    pub helper_path: *const c_char,
25    pub debug: c_int,
26}
27
28/// Information about a connected display
29#[repr(C)]
30#[derive(Clone, Copy)]
31pub struct drmtap_display {
32    pub crtc_id: u32,
33    pub connector_id: u32,
34    pub name: [c_char; 32],
35    pub x: u32,
36    pub y: u32,
37    pub width: u32,
38    pub height: u32,
39    pub refresh_hz: u32,
40    pub active: c_int,
41}
42
43/// Captured frame data
44#[repr(C)]
45pub struct drmtap_frame_info {
46    pub data: *mut c_void,
47    pub dma_buf_fd: c_int,
48    pub width: u32,
49    pub height: u32,
50    pub stride: u32,
51    pub format: u32,
52    pub modifier: u64,
53    pub fb_id: u32,
54    pub _priv: *mut c_void,
55}
56
57/// Cursor state
58#[repr(C)]
59pub struct drmtap_cursor_info {
60    pub x: i32,
61    pub y: i32,
62    pub hot_x: i32,
63    pub hot_y: i32,
64    pub width: u32,
65    pub height: u32,
66    pub pixels: *mut u32,
67    pub visible: c_int,
68    pub _priv: *mut c_void,
69}
70
71/// Dirty rectangle from frame differencing
72#[repr(C)]
73pub struct drmtap_rect {
74    pub x: u32,
75    pub y: u32,
76    pub w: u32,
77    pub h: u32,
78}
79
80/// Scanout EOTF (from the connector HDR_OUTPUT_METADATA, CTA-861 numbering)
81pub const DRMTAP_EOTF_SDR: u32 = 0;
82pub const DRMTAP_EOTF_PQ: u32 = 2;
83pub const DRMTAP_EOTF_HLG: u32 = 3;
84
85/// Descriptor of an externally-supplied scanout DMA-BUF (split capture):
86/// metadata from the privileged exporter shipped over IPC into
87/// drmtap_convert_dmabuf() on the unprivileged side.
88#[repr(C)]
89pub struct drmtap_dmabuf_desc {
90    pub dma_buf_fd: c_int,
91    pub width: u32,
92    pub height: u32,
93    pub format: u32,
94    pub modifier: u64,
95    pub fb_id: u32,
96    pub num_planes: u32,
97    pub offsets: [u32; 4],
98    pub pitches: [u32; 4],
99    pub hdr_eotf: u32,
100    pub hdr_max_nits: u32,
101}
102
103extern "C" {
104    // Version
105    pub fn drmtap_version() -> c_int;
106
107    // Context lifecycle
108    pub fn drmtap_open(config: *const drmtap_config) -> *mut drmtap_ctx;
109    pub fn drmtap_close(ctx: *mut drmtap_ctx);
110
111    // Split capture (privileged export + unprivileged convert)
112    pub fn drmtap_open_render(render_node: *const c_char) -> *mut drmtap_ctx;
113    pub fn drmtap_grab_desc(
114        ctx: *mut drmtap_ctx,
115        desc: *mut drmtap_dmabuf_desc,
116        frame: *mut drmtap_frame_info,
117    ) -> c_int;
118    pub fn drmtap_convert_dmabuf(
119        ctx: *mut drmtap_ctx,
120        desc: *const drmtap_dmabuf_desc,
121        frame: *mut drmtap_frame_info,
122    ) -> c_int;
123
124    // Display enumeration
125    pub fn drmtap_list_displays(
126        ctx: *mut drmtap_ctx,
127        displays: *mut drmtap_display,
128        max_count: c_int,
129    ) -> c_int;
130    pub fn drmtap_displays_changed(ctx: *mut drmtap_ctx) -> c_int;
131
132    // Frame capture
133    pub fn drmtap_grab(ctx: *mut drmtap_ctx, frame: *mut drmtap_frame_info) -> c_int;
134    pub fn drmtap_grab_mapped(ctx: *mut drmtap_ctx, frame: *mut drmtap_frame_info) -> c_int;
135    pub fn drmtap_frame_release(ctx: *mut drmtap_ctx, frame: *mut drmtap_frame_info);
136
137    // Cursor
138    pub fn drmtap_get_cursor(ctx: *mut drmtap_ctx, cursor: *mut drmtap_cursor_info) -> c_int;
139    pub fn drmtap_cursor_release(ctx: *mut drmtap_ctx, cursor: *mut drmtap_cursor_info);
140
141    // Info
142    pub fn drmtap_error(ctx: *const drmtap_ctx) -> *const c_char;
143    pub fn drmtap_gpu_driver(ctx: *mut drmtap_ctx) -> *const c_char;
144
145    // Pixel conversion
146    pub fn drmtap_deswizzle(
147        src: *const c_void,
148        dst: *mut c_void,
149        width: u32,
150        height: u32,
151        src_stride: u32,
152        dst_stride: u32,
153        modifier: u64,
154        src_size: usize,
155    ) -> c_int;
156    pub fn drmtap_convert_format(
157        src: *const c_void,
158        dst: *mut c_void,
159        width: u32,
160        height: u32,
161        src_stride: u32,
162        dst_stride: u32,
163        src_format: u32,
164        dst_format: u32,
165    ) -> c_int;
166
167    // Frame differencing
168    pub fn drmtap_diff_frames(
169        frame_a: *const c_void,
170        frame_b: *const c_void,
171        width: u32,
172        height: u32,
173        stride: u32,
174        rects_out: *mut drmtap_rect,
175        max_rects: c_int,
176        tile_size: c_int,
177    ) -> c_int;
178}
179
180#[cfg(test)]
181mod tests {
182    use super::*;
183
184    #[test]
185    fn test_version() {
186        // The C library version (DRMTAP_VERSION_* in csrc/drmtap.h) is kept
187        // equal to this crate's version. Derive the expected packed value from
188        // CARGO_PKG_VERSION so the two can never silently drift apart.
189        let major: i32 = env!("CARGO_PKG_VERSION_MAJOR").parse().unwrap();
190        let minor: i32 = env!("CARGO_PKG_VERSION_MINOR").parse().unwrap();
191        let patch: i32 = env!("CARGO_PKG_VERSION_PATCH").parse().unwrap();
192        let expected = (major << 16) | (minor << 8) | patch;
193        let v = unsafe { drmtap_version() };
194        assert_eq!(v, expected);
195    }
196
197    #[test]
198    fn test_config_size() {
199        // Verify struct sizes are reasonable
200        assert!(std::mem::size_of::<drmtap_config>() > 0);
201        assert!(std::mem::size_of::<drmtap_display>() > 0);
202        assert!(std::mem::size_of::<drmtap_frame_info>() > 0);
203        assert!(std::mem::size_of::<drmtap_cursor_info>() > 0);
204    }
205}