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
80extern "C" {
81    // Version
82    pub fn drmtap_version() -> c_int;
83
84    // Context lifecycle
85    pub fn drmtap_open(config: *const drmtap_config) -> *mut drmtap_ctx;
86    pub fn drmtap_close(ctx: *mut drmtap_ctx);
87
88    // Display enumeration
89    pub fn drmtap_list_displays(
90        ctx: *mut drmtap_ctx,
91        displays: *mut drmtap_display,
92        max_count: c_int,
93    ) -> c_int;
94    pub fn drmtap_displays_changed(ctx: *mut drmtap_ctx) -> c_int;
95
96    // Frame capture
97    pub fn drmtap_grab(ctx: *mut drmtap_ctx, frame: *mut drmtap_frame_info) -> c_int;
98    pub fn drmtap_grab_mapped(ctx: *mut drmtap_ctx, frame: *mut drmtap_frame_info) -> c_int;
99    pub fn drmtap_frame_release(ctx: *mut drmtap_ctx, frame: *mut drmtap_frame_info);
100
101    // Cursor
102    pub fn drmtap_get_cursor(ctx: *mut drmtap_ctx, cursor: *mut drmtap_cursor_info) -> c_int;
103    pub fn drmtap_cursor_release(ctx: *mut drmtap_ctx, cursor: *mut drmtap_cursor_info);
104
105    // Info
106    pub fn drmtap_error(ctx: *const drmtap_ctx) -> *const c_char;
107    pub fn drmtap_gpu_driver(ctx: *mut drmtap_ctx) -> *const c_char;
108
109    // Pixel conversion
110    pub fn drmtap_deswizzle(
111        src: *const c_void,
112        dst: *mut c_void,
113        width: u32,
114        height: u32,
115        src_stride: u32,
116        dst_stride: u32,
117        modifier: u64,
118        src_size: usize,
119    ) -> c_int;
120    pub fn drmtap_convert_format(
121        src: *const c_void,
122        dst: *mut c_void,
123        width: u32,
124        height: u32,
125        src_stride: u32,
126        dst_stride: u32,
127        src_format: u32,
128        dst_format: u32,
129    ) -> c_int;
130
131    // Frame differencing
132    pub fn drmtap_diff_frames(
133        frame_a: *const c_void,
134        frame_b: *const c_void,
135        width: u32,
136        height: u32,
137        stride: u32,
138        rects_out: *mut drmtap_rect,
139        max_rects: c_int,
140        tile_size: c_int,
141    ) -> c_int;
142}
143
144#[cfg(test)]
145mod tests {
146    use super::*;
147
148    #[test]
149    fn test_version() {
150        // The C library version (DRMTAP_VERSION_* in csrc/drmtap.h) is kept
151        // equal to this crate's version. Derive the expected packed value from
152        // CARGO_PKG_VERSION so the two can never silently drift apart.
153        let major: i32 = env!("CARGO_PKG_VERSION_MAJOR").parse().unwrap();
154        let minor: i32 = env!("CARGO_PKG_VERSION_MINOR").parse().unwrap();
155        let patch: i32 = env!("CARGO_PKG_VERSION_PATCH").parse().unwrap();
156        let expected = (major << 16) | (minor << 8) | patch;
157        let v = unsafe { drmtap_version() };
158        assert_eq!(v, expected);
159    }
160
161    #[test]
162    fn test_config_size() {
163        // Verify struct sizes are reasonable
164        assert!(std::mem::size_of::<drmtap_config>() > 0);
165        assert!(std::mem::size_of::<drmtap_display>() > 0);
166        assert!(std::mem::size_of::<drmtap_frame_info>() > 0);
167        assert!(std::mem::size_of::<drmtap_cursor_info>() > 0);
168    }
169}