#screen-capture #display #color-space #coregraphics #displaystream

core-graphics2

Safe bindings to CoreGraphics framework, including display stream

13 unstable releases (3 breaking)

0.4.1 Oct 24, 2024
0.4.0 Sep 30, 2024
0.3.4 Sep 29, 2024
0.3.3 Aug 6, 2024
0.1.5 May 26, 2024

#39 in macOS and iOS APIs

Download history 162/week @ 2024-11-29 228/week @ 2024-12-06 204/week @ 2024-12-13 88/week @ 2024-12-20 54/week @ 2024-12-27 131/week @ 2025-01-03 193/week @ 2025-01-10 214/week @ 2025-01-17 224/week @ 2025-01-24 250/week @ 2025-01-31 244/week @ 2025-02-07 203/week @ 2025-02-14 141/week @ 2025-02-21 177/week @ 2025-02-28 297/week @ 2025-03-07 416/week @ 2025-03-14

1,066 downloads per month
Used in 12 crates (5 directly)

MIT/Apache

245KB
6K SLoC

core-graphics2

Version Documentation License License

Rust safe bindings to CoreGraphics framework

Example

[dependencies]
core-graphics2 = { version = "0.1", default-features = false, features = ["display", "display-stream", "link"] }
use core_foundation::{
    base::{CFType, TCFType},
    boolean::CFBoolean,
    dictionary::CFMutableDictionary,
    number::CFNumber,
    string::CFString,
};
use core_graphics2::{
    color_space::{CGColorSpace, CGColorSpaceNames},
    display::CGDisplay,
    display_stream::*,
};
use core_video::pixel_buffer::{self, CVPixelBuffer};
use dispatch2::{Queue, QueueAttribute};
use io_surface::IOSurface;

let display = CGDisplay::main();
let output_width = display.pixels_wide();
let output_height = display.pixels_high();
let pixel_format = pixel_buffer::kCVPixelFormatType_420YpCbCr8BiPlanarFullRange;
let mut properties = CFMutableDictionary::<CFString, CFType>::new();
properties.add(&CGDisplayStreamProperties::ShowCursor.into(), &CFBoolean::true_value().as_CFType());
if let Some(color_space) = CGColorSpace::from_name(&CGColorSpaceNames::SRGB.into()) {
    properties.add(&CGDisplayStreamProperties::ColorSpace.into(), &color_space.as_CFType());
}
properties.add(&CGDisplayStreamProperties::MinimumFrameTime.into(), &CFNumber::from(1.0 / 60.0).as_CFType());
let ycbcr_matrix: CFString = CGDisplayStreamYCbCrMatrices::ITU_R_709_2.into();
properties.add(&CGDisplayStreamProperties::YCbCrMatrix.into(), &ycbcr_matrix.as_CFType());

let closure = move |status: CGDisplayStreamFrameStatus, timestamp: u64, surface: Option<IOSurface>, update: Option<CGDisplayStreamUpdate>| match status {
    CGDisplayStreamFrameStatus::Stopped => {
        println!("status: Stopped");
    }
    CGDisplayStreamFrameStatus::FrameComplete => {
        println!("status: {:?}, timestamp: {}", status, timestamp);
        if let Some(update) = update {
            println!("refreshed rects: {:?}", update.rects(CGDisplayStreamUpdateRectType::RefreshedRects));
            println!("moved rects: {:?}", update.rects(CGDisplayStreamUpdateRectType::MovedRects));
            println!("dirty rects: {:?}", update.rects(CGDisplayStreamUpdateRectType::DirtyRects));
            println!("reduced dirty rects: {:?}", update.rects(CGDisplayStreamUpdateRectType::ReducedDirtyRects));
        }
        if let Some(surface) = surface {
            if let Ok(pixel_buffer) = CVPixelBuffer::from_io_surface(&surface, None) {
                println!("pixel_buffer: {:?}", pixel_buffer);
            }
        }
    }
    _ => {}
};

let queue = Queue::new("com.screen_capture.queue", QueueAttribute::Serial);
if let Ok(display_stream) = CGDisplayStream::new_with_dispatch_queue(
        display.id,
        output_width,
        output_height,
        pixel_format as i32,
        &properties.to_immutable(),
        &queue,
        closure,
) {
    display_stream.start();
}

Dependencies

~0.4–1.6MB
~20K SLoC