screencapturekit/lib.rs
1#![doc = include_str!("../README.md")]
2//!
3//! ---
4//!
5//! # API Documentation
6//!
7//! Safe, idiomatic Rust bindings for Apple's [ScreenCaptureKit] framework.
8//!
9//! Capture screen content, windows, and applications with high performance on macOS 12.3+.
10//!
11//! [ScreenCaptureKit]: https://developer.apple.com/documentation/screencapturekit
12//!
13//! ## Features
14//!
15//! - **Screen and window capture** - Capture displays, windows, or specific applications
16//! - **Audio capture** - System audio and microphone input (macOS 13.0+)
17//! - **Real-time frame processing** - High-performance callbacks with custom dispatch queues
18//! - **Async support** - Runtime-agnostic async API (Tokio, async-std, smol, etc.)
19//! - **Zero-copy GPU access** - Direct [`IOSurface`] access for Metal/OpenGL integration
20//! - **Screenshots** - Single-frame capture without streaming (macOS 14.0+)
21//! - **Recording** - Direct-to-file video recording (macOS 15.0+)
22//! - **Content Picker** - System UI for user content selection (macOS 14.0+)
23//!
24//! ## Installation
25//!
26//! Add to your `Cargo.toml`:
27//!
28//! ```toml
29//! [dependencies]
30//! screencapturekit = "1"
31//! ```
32//!
33//! For async support:
34//!
35//! ```toml
36//! [dependencies]
37//! screencapturekit = { version = "1", features = ["async"] }
38//! ```
39//!
40//! ## Quick Start
41//!
42//! ### 1. Request Permission
43//!
44//! Screen recording requires user permission. Add to your `Info.plist`:
45//!
46//! ```xml
47//! <key>NSScreenCaptureUsageDescription</key>
48//! <string>This app needs screen recording permission.</string>
49//! ```
50//!
51//! ### 2. Implement a Frame Handler
52//!
53//! You can use either a struct or a closure:
54//!
55//! **Struct-based handler:**
56//! ```rust,no_run
57//! use screencapturekit::prelude::*;
58//! use std::sync::atomic::{AtomicUsize, Ordering};
59//! use std::sync::Arc;
60//!
61//! struct FrameHandler {
62//! count: Arc<AtomicUsize>,
63//! }
64//!
65//! impl SCStreamOutputTrait for FrameHandler {
66//! fn did_output_sample_buffer(&self, sample: CMSampleBuffer, of_type: SCStreamOutputType) {
67//! match of_type {
68//! SCStreamOutputType::Screen => {
69//! let n = self.count.fetch_add(1, Ordering::Relaxed);
70//! if n % 60 == 0 {
71//! println!("Frame {n}");
72//! }
73//! }
74//! SCStreamOutputType::Audio => {
75//! println!("Got audio samples!");
76//! }
77//! _ => {}
78//! }
79//! }
80//! }
81//! ```
82//!
83//! **Closure-based handler:**
84//! ```rust,no_run
85//! use screencapturekit::prelude::*;
86//! use std::sync::atomic::{AtomicUsize, Ordering};
87//! use std::sync::Arc;
88//!
89//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
90//! # let content = SCShareableContent::get()?;
91//! # let display = content.displays().into_iter().next().unwrap();
92//! # let filter = SCContentFilter::create().with_display(&display).with_excluding_windows(&[]).build();
93//! # let config = SCStreamConfiguration::new();
94//! let frame_count = Arc::new(AtomicUsize::new(0));
95//! let count_clone = frame_count.clone();
96//!
97//! let mut stream = SCStream::new(&filter, &config);
98//! stream.add_output_handler(
99//! move |_sample: CMSampleBuffer, _of_type: SCStreamOutputType| {
100//! count_clone.fetch_add(1, Ordering::Relaxed);
101//! },
102//! SCStreamOutputType::Screen
103//! );
104//! # Ok(())
105//! # }
106//! ```
107//!
108//! ### 3. Start Capturing
109//!
110//! ```rust,no_run
111//! use screencapturekit::prelude::*;
112//!
113//! # struct MyHandler;
114//! # impl SCStreamOutputTrait for MyHandler {
115//! # fn did_output_sample_buffer(&self, _: CMSampleBuffer, _: SCStreamOutputType) {}
116//! # }
117//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
118//! // Get available displays
119//! let content = SCShareableContent::get()?;
120//! let display = content.displays().into_iter().next().ok_or("No display")?;
121//!
122//! // Configure what to capture
123//! let filter = SCContentFilter::create()
124//! .with_display(&display)
125//! .with_excluding_windows(&[])
126//! .build();
127//!
128//! // Configure how to capture
129//! let config = SCStreamConfiguration::new()
130//! .with_width(1920)
131//! .with_height(1080)
132//! .with_pixel_format(PixelFormat::BGRA)
133//! .with_shows_cursor(true);
134//!
135//! // Create stream and add handler
136//! let mut stream = SCStream::new(&filter, &config);
137//! stream.add_output_handler(MyHandler, SCStreamOutputType::Screen);
138//!
139//! // Start capturing
140//! stream.start_capture()?;
141//!
142//! // ... capture runs in background ...
143//! std::thread::sleep(std::time::Duration::from_secs(5));
144//!
145//! stream.stop_capture()?;
146//! # Ok(())
147//! # }
148//! ```
149//!
150//! ## Configuration Options
151//!
152//! Use the builder pattern for fluent configuration:
153//!
154//! ```rust
155//! use screencapturekit::prelude::*;
156//!
157//! // For 60 FPS, use CMTime to specify frame interval
158//! let frame_interval = CMTime::new(1, 60); // 1/60th of a second
159//!
160//! let config = SCStreamConfiguration::new()
161//! // Video settings
162//! .with_width(1920)
163//! .with_height(1080)
164//! .with_pixel_format(PixelFormat::BGRA)
165//! .with_shows_cursor(true)
166//! .with_minimum_frame_interval(&frame_interval)
167//!
168//! // Audio settings
169//! .with_captures_audio(true)
170//! .with_sample_rate(48000)
171//! .with_channel_count(2);
172//! ```
173//!
174//! ### Available Pixel Formats
175//!
176//! | Format | Description | Use Case |
177//! |--------|-------------|----------|
178//! | [`PixelFormat::BGRA`] | 32-bit BGRA | General purpose, easy to use |
179//! | [`PixelFormat::l10r`] | 10-bit RGB | HDR content |
180//! | [`PixelFormat::YCbCr_420v`] | YCbCr 4:2:0 | Video encoding (H.264/HEVC) |
181//! | [`PixelFormat::YCbCr_420f`] | YCbCr 4:2:0 full range | Video encoding |
182//!
183//! ## Accessing Frame Data
184//!
185//! ### Pixel Data (CPU)
186//!
187//! Lock the pixel buffer for direct CPU access:
188//!
189//! ```rust,no_run
190//! use screencapturekit::prelude::*;
191//! use screencapturekit::cv::{CVPixelBuffer, CVPixelBufferLockFlags, PixelBufferCursorExt};
192//! use std::io::{Read, Seek, SeekFrom};
193//!
194//! # fn handle(sample: CMSampleBuffer) {
195//! if let Some(buffer) = sample.image_buffer() {
196//! if let Ok(guard) = buffer.lock(CVPixelBufferLockFlags::READ_ONLY) {
197//! // Method 1: Direct slice access (fast)
198//! let pixels = guard.as_slice();
199//! let width = guard.width();
200//! let height = guard.height();
201//!
202//! // Method 2: Use cursor for reading specific pixels
203//! let mut cursor = guard.cursor();
204//!
205//! // Read first pixel (BGRA)
206//! if let Ok(pixel) = cursor.read_pixel() {
207//! println!("First pixel: {:?}", pixel);
208//! }
209//!
210//! // Seek to center pixel
211//! let center_x = width / 2;
212//! let center_y = height / 2;
213//! if cursor.seek_to_pixel(center_x, center_y, guard.bytes_per_row()).is_ok() {
214//! if let Ok(pixel) = cursor.read_pixel() {
215//! println!("Center pixel: {:?}", pixel);
216//! }
217//! }
218//! }
219//! }
220//! # }
221//! ```
222//!
223//! ### [`IOSurface`] (GPU)
224//!
225//! For Metal/OpenGL integration, access the underlying [`IOSurface`]:
226//!
227//! ```rust,no_run
228//! use screencapturekit::prelude::*;
229//! use screencapturekit::cm::IOSurfaceLockOptions;
230//! use screencapturekit::cv::PixelBufferCursorExt;
231//!
232//! # fn handle(sample: CMSampleBuffer) {
233//! if let Some(buffer) = sample.image_buffer() {
234//! // Check if IOSurface-backed (usually true for ScreenCaptureKit)
235//! if buffer.is_backed_by_io_surface() {
236//! if let Some(surface) = buffer.io_surface() {
237//! println!("Dimensions: {}x{}", surface.width(), surface.height());
238//! println!("Pixel format: 0x{:08X}", surface.pixel_format());
239//! println!("Bytes per row: {}", surface.bytes_per_row());
240//! println!("In use: {}", surface.is_in_use());
241//!
242//! // Lock for CPU access to IOSurface data
243//! if let Ok(guard) = surface.lock(IOSurfaceLockOptions::READ_ONLY) {
244//! let mut cursor = guard.cursor();
245//! if let Ok(pixel) = cursor.read_pixel() {
246//! println!("First pixel: {:?}", pixel);
247//! }
248//! }
249//! }
250//! }
251//! }
252//! # }
253//! ```
254//!
255//! ## Audio + Video Capture
256//!
257//! Capture system audio alongside video:
258//!
259//! ```rust,no_run
260//! use screencapturekit::prelude::*;
261//! use std::sync::atomic::{AtomicUsize, Ordering};
262//! use std::sync::Arc;
263//!
264//! struct AVHandler {
265//! video_count: Arc<AtomicUsize>,
266//! audio_count: Arc<AtomicUsize>,
267//! }
268//!
269//! impl SCStreamOutputTrait for AVHandler {
270//! fn did_output_sample_buffer(&self, _sample: CMSampleBuffer, of_type: SCStreamOutputType) {
271//! match of_type {
272//! SCStreamOutputType::Screen => {
273//! self.video_count.fetch_add(1, Ordering::Relaxed);
274//! }
275//! SCStreamOutputType::Audio => {
276//! self.audio_count.fetch_add(1, Ordering::Relaxed);
277//! }
278//! SCStreamOutputType::Microphone => {
279//! // Requires macOS 15.0+ and .with_captures_microphone(true)
280//! }
281//! }
282//! }
283//! }
284//!
285//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
286//! let content = SCShareableContent::get()?;
287//! let display = content.displays().into_iter().next().ok_or("No display")?;
288//!
289//! let filter = SCContentFilter::create()
290//! .with_display(&display)
291//! .with_excluding_windows(&[])
292//! .build();
293//!
294//! let config = SCStreamConfiguration::new()
295//! .with_width(1920)
296//! .with_height(1080)
297//! .with_captures_audio(true) // Enable system audio
298//! .with_sample_rate(48000) // 48kHz
299//! .with_channel_count(2); // Stereo
300//!
301//! let handler = AVHandler {
302//! video_count: Arc::new(AtomicUsize::new(0)),
303//! audio_count: Arc::new(AtomicUsize::new(0)),
304//! };
305//!
306//! let mut stream = SCStream::new(&filter, &config);
307//! stream.add_output_handler(handler, SCStreamOutputType::Screen);
308//! stream.start_capture()?;
309//! # Ok(())
310//! # }
311//! ```
312//!
313//! ## Dynamic Stream Updates
314//!
315//! Update configuration or content filter while streaming:
316//!
317//! ```rust,no_run
318//! use screencapturekit::prelude::*;
319//!
320//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
321//! # let content = SCShareableContent::get()?;
322//! # let display = content.displays().into_iter().next().unwrap();
323//! # let filter = SCContentFilter::create().with_display(&display).with_excluding_windows(&[]).build();
324//! # let config = SCStreamConfiguration::new().with_width(640).with_height(480);
325//! # struct MyHandler;
326//! # impl SCStreamOutputTrait for MyHandler {
327//! # fn did_output_sample_buffer(&self, _: CMSampleBuffer, _: SCStreamOutputType) {}
328//! # }
329//! let mut stream = SCStream::new(&filter, &config);
330//! stream.add_output_handler(MyHandler, SCStreamOutputType::Screen);
331//! stream.start_capture()?;
332//!
333//! // Capture at initial resolution...
334//! std::thread::sleep(std::time::Duration::from_secs(2));
335//!
336//! // Update to higher resolution while streaming
337//! let new_config = SCStreamConfiguration::new()
338//! .with_width(1920)
339//! .with_height(1080);
340//! stream.update_configuration(&new_config)?;
341//!
342//! // Switch to a different window
343//! let windows = content.windows();
344//! if let Some(window) = windows.iter().find(|w| w.is_on_screen()) {
345//! let window_filter = SCContentFilter::create().with_window(window).build();
346//! stream.update_content_filter(&window_filter)?;
347//! }
348//!
349//! stream.stop_capture()?;
350//! # Ok(())
351//! # }
352//! ```
353//!
354//! ## Error Handling with Delegates
355//!
356//! Handle stream errors gracefully using delegates:
357//!
358//! ```rust,no_run
359//! use screencapturekit::prelude::*;
360//! use screencapturekit::stream::ErrorHandler;
361//!
362//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
363//! # let content = SCShareableContent::get()?;
364//! # let display = content.displays().into_iter().next().unwrap();
365//! # let filter = SCContentFilter::create().with_display(&display).with_excluding_windows(&[]).build();
366//! # let config = SCStreamConfiguration::new();
367//! // Create an error handler using a closure
368//! let error_handler = ErrorHandler::new(|error| {
369//! eprintln!("Stream error: {error}");
370//! });
371//!
372//! // Create stream with delegate
373//! let mut stream = SCStream::new_with_delegate(&filter, &config, error_handler);
374//! stream.add_output_handler(
375//! |_sample, _type| { /* process frames */ },
376//! SCStreamOutputType::Screen
377//! );
378//! stream.start_capture()?;
379//! # Ok(())
380//! # }
381//! ```
382//!
383//! ## Custom Dispatch Queues
384//!
385//! Control which thread/queue handles frame callbacks:
386//!
387//! ```rust,no_run
388//! use screencapturekit::prelude::*;
389//! use screencapturekit::dispatch_queue::{DispatchQueue, DispatchQoS};
390//!
391//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
392//! # let content = SCShareableContent::get()?;
393//! # let display = content.displays().into_iter().next().unwrap();
394//! # let filter = SCContentFilter::create().with_display(&display).with_excluding_windows(&[]).build();
395//! # let config = SCStreamConfiguration::new();
396//! let mut stream = SCStream::new(&filter, &config);
397//!
398//! // Create a high-priority queue for frame processing
399//! let queue = DispatchQueue::new("com.myapp.capture", DispatchQoS::UserInteractive);
400//!
401//! stream.add_output_handler_with_queue(
402//! |_sample, _type| { /* called on custom queue */ },
403//! SCStreamOutputType::Screen,
404//! Some(&queue)
405//! );
406//! # Ok(())
407//! # }
408//! ```
409//!
410//! ## Async API
411//!
412//! Enable the `async` feature for async/await support. The async API is
413//! **executor-agnostic** and works with Tokio, async-std, smol, or any runtime:
414//!
415//! ```ignore
416//! use screencapturekit::async_api::{AsyncSCShareableContent, AsyncSCStream};
417//! use screencapturekit::prelude::*;
418//!
419//! async fn capture() -> Result<(), Box<dyn std::error::Error>> {
420//! // Get content asynchronously (true async - no blocking)
421//! let content = AsyncSCShareableContent::get().await?;
422//! let display = &content.displays()[0];
423//!
424//! let filter = SCContentFilter::create()
425//! .with_display(display)
426//! .with_excluding_windows(&[])
427//! .build();
428//!
429//! let config = SCStreamConfiguration::new()
430//! .with_width(1920)
431//! .with_height(1080);
432//!
433//! // Create async stream with 30-frame buffer
434//! let stream = AsyncSCStream::new(&filter, &config, 30, SCStreamOutputType::Screen);
435//! stream.start_capture()?;
436//!
437//! // Async iteration over frames
438//! let mut count = 0;
439//! while count < 100 {
440//! if let Some(_frame) = stream.next().await {
441//! count += 1;
442//! }
443//! }
444//!
445//! stream.stop_capture()?;
446//! Ok(())
447//! }
448//!
449//! // Concurrent async operations
450//! async fn concurrent_queries() -> Result<(), Box<dyn std::error::Error>> {
451//! let (result1, result2) = tokio::join!(
452//! AsyncSCShareableContent::get(),
453//! AsyncSCShareableContent::with_options()
454//! .on_screen_windows_only(true)
455//! .get(),
456//! );
457//! Ok(())
458//! }
459//! ```
460//!
461//! ## Screenshots (macOS 14.0+)
462//!
463//! Take single screenshots without setting up a stream:
464//!
465//! ```ignore
466//! use screencapturekit::prelude::*;
467//! use screencapturekit::screenshot_manager::SCScreenshotManager;
468//!
469//! let content = SCShareableContent::get()?;
470//! let display = &content.displays()[0];
471//!
472//! let filter = SCContentFilter::create()
473//! .with_display(display)
474//! .with_excluding_windows(&[])
475//! .build();
476//!
477//! let config = SCStreamConfiguration::new()
478//! .with_width(1920)
479//! .with_height(1080);
480//!
481//! // Capture screenshot as CGImage
482//! let image = SCScreenshotManager::capture_image(&filter, &config)?;
483//! println!("Screenshot: {}x{}", image.width(), image.height());
484//!
485//! // Or capture as CMSampleBuffer for more control
486//! let sample_buffer = SCScreenshotManager::capture_sample_buffer(&filter, &config)?;
487//! ```
488//!
489//! ## Recording (macOS 15.0+)
490//!
491//! Record directly to a video file:
492//!
493//! ```ignore
494//! use screencapturekit::prelude::*;
495//! use screencapturekit::recording_output::{
496//! SCRecordingOutput, SCRecordingOutputConfiguration,
497//! SCRecordingOutputCodec, SCRecordingOutputFileType
498//! };
499//! use std::path::PathBuf;
500//!
501//! let content = SCShareableContent::get()?;
502//! let display = &content.displays()[0];
503//!
504//! let filter = SCContentFilter::create()
505//! .with_display(display)
506//! .with_excluding_windows(&[])
507//! .build();
508//!
509//! let stream_config = SCStreamConfiguration::new()
510//! .with_width(1920)
511//! .with_height(1080);
512//!
513//! // Configure recording output
514//! let output_path = PathBuf::from("/tmp/screen_recording.mp4");
515//! let recording_config = SCRecordingOutputConfiguration::new()
516//! .with_output_url(&output_path)
517//! .with_video_codec(SCRecordingOutputCodec::H264)
518//! .with_output_file_type(SCRecordingOutputFileType::MP4);
519//!
520//! let recording_output = SCRecordingOutput::new(&recording_config)
521//! .ok_or("Failed to create recording output")?;
522//!
523//! // Start stream and add recording
524//! let stream = SCStream::new(&filter, &stream_config);
525//! stream.add_recording_output(&recording_output)?;
526//! stream.start_capture()?;
527//!
528//! // Record for 10 seconds
529//! std::thread::sleep(std::time::Duration::from_secs(10));
530//!
531//! // Check recording stats
532//! let duration = recording_output.recorded_duration();
533//! let file_size = recording_output.recorded_file_size();
534//! println!("Recorded {}/{} seconds, {} bytes", duration.value, duration.timescale, file_size);
535//!
536//! stream.remove_recording_output(&recording_output)?;
537//! stream.stop_capture()?;
538//! ```
539//!
540//! ## Module Organization
541//!
542//! | Module | Description |
543//! |--------|-------------|
544//! | [`stream`] | Stream configuration and management ([`SCStream`], [`SCContentFilter`]) |
545//! | [`shareable_content`] | Display, window, and application enumeration |
546//! | [`cm`] | Core Media types ([`CMSampleBuffer`], [`CMTime`], [`IOSurface`]) |
547//! | [`cv`] | Core Video types ([`CVPixelBuffer`], lock guards) |
548//! | [`cg`] | Core Graphics types ([`CGRect`], [`CGSize`]) |
549//! | [`metal`] | Metal texture helpers for zero-copy GPU rendering |
550//! | [`dispatch_queue`] | Custom dispatch queues for callbacks |
551//! | [`error`] | Error types and result aliases |
552//! | `async_api` | Async wrappers (requires `async` feature) |
553//! | [`screenshot_manager`] | Single-frame capture (macOS 14.0+) |
554//! | `recording_output` | Direct file recording (macOS 15.0+) |
555//!
556//! [`SCStream`]: stream::sc_stream::SCStream
557//! [`SCContentFilter`]: stream::content_filter::SCContentFilter
558//! [`CMSampleBuffer`]: cm::CMSampleBuffer
559//! [`CMTime`]: cm::CMTime
560//! [`IOSurface`]: cm::IOSurface
561//! [`CVPixelBuffer`]: cv::CVPixelBuffer
562//! [`CGRect`]: cg::CGRect
563//! [`CGSize`]: cg::CGSize
564//!
565//! ## Feature Flags
566//!
567//! | Feature | Description |
568//! |---------|-------------|
569//! | `async` | Runtime-agnostic async API |
570//! | `macos_13_0` | macOS 13.0+ APIs (audio capture, synchronization clock) |
571//! | `macos_14_0` | macOS 14.0+ APIs (screenshots, content picker) |
572//! | `macos_14_2` | macOS 14.2+ APIs (menu bar, child windows, presenter overlay) |
573//! | `macos_14_4` | macOS 14.4+ APIs (current process shareable content) |
574//! | `macos_15_0` | macOS 15.0+ APIs (recording output, HDR, microphone) |
575//! | `macos_15_2` | macOS 15.2+ APIs (screenshot in rect, stream delegates) |
576//! | `macos_26_0` | macOS 26.0+ APIs (advanced screenshot config, HDR output) |
577//!
578//! Features are cumulative: enabling `macos_15_0` also enables all earlier versions.
579//!
580//! ## Platform Requirements
581//!
582//! - **macOS 12.3+** (Monterey) - Base `ScreenCaptureKit` support
583//! - **Screen Recording Permission** - Must be granted by user in System Preferences
584//! - **Hardened Runtime** - Required for notarized apps
585//!
586//! ## Examples
587//!
588//! See the [examples directory](https://github.com/doom-fish/screencapturekit-rs/tree/main/examples):
589//!
590//! | Example | Description |
591//! |---------|-------------|
592//! | `01_basic_capture` | Simplest screen capture |
593//! | `02_window_capture` | Capture specific windows |
594//! | `03_audio_capture` | Audio + video capture |
595//! | `04_pixel_access` | Read pixel data with cursor API |
596//! | `05_screenshot` | Single screenshot (macOS 14.0+) |
597//! | `06_iosurface` | Zero-copy GPU buffer access |
598//! | `07_list_content` | List available displays, windows, apps |
599//! | `08_async` | Async/await API with any runtime |
600//! | `09_closure_handlers` | Closure-based handlers |
601//! | `10_recording_output` | Direct video recording (macOS 15.0+) |
602//! | `11_content_picker` | System content picker UI (macOS 14.0+) |
603//! | `12_stream_updates` | Dynamic config/filter updates |
604//! | `13_advanced_config` | HDR, presets, microphone (macOS 15.0+) |
605//! | `14_app_capture` | Application-based filtering |
606//! | `15_memory_leak_check` | Memory leak detection |
607//! | `16_full_metal_app` | Full Metal GUI application |
608//! | `17_metal_textures` | Metal texture creation from `IOSurface` |
609//!
610//! ## Common Patterns
611//!
612//! ### Capture Window by Title
613//!
614//! ```rust,no_run
615//! use screencapturekit::prelude::*;
616//!
617//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
618//! let content = SCShareableContent::get()?;
619//! let windows = content.windows();
620//! let window = windows
621//! .iter()
622//! .find(|w| w.title().is_some_and(|t| t.contains("Safari")))
623//! .ok_or("Window not found")?;
624//!
625//! let filter = SCContentFilter::create()
626//! .with_window(window)
627//! .build();
628//! # Ok(())
629//! # }
630//! ```
631//!
632//! ### Capture Specific Application
633//!
634//! ```rust,no_run
635//! use screencapturekit::prelude::*;
636//!
637//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
638//! let content = SCShareableContent::get()?;
639//! let display = content.displays().into_iter().next().ok_or("No display")?;
640//!
641//! // Find app by bundle ID
642//! let apps = content.applications();
643//! let safari = apps
644//! .iter()
645//! .find(|app| app.bundle_identifier() == "com.apple.Safari")
646//! .ok_or("Safari not found")?;
647//!
648//! // Capture only windows from this app
649//! let filter = SCContentFilter::create()
650//! .with_display(&display)
651//! .with_including_applications(&[safari], &[]) // Include Safari, no excepted windows
652//! .build();
653//! # Ok(())
654//! # }
655//! ```
656//!
657//! ### Exclude Your Own App's Windows
658//!
659//! ```rust,no_run
660//! use screencapturekit::prelude::*;
661//!
662//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
663//! let content = SCShareableContent::get()?;
664//! let display = content.displays().into_iter().next().ok_or("No display")?;
665//!
666//! // Find our app's windows
667//! let windows = content.windows();
668//! let my_windows: Vec<&SCWindow> = windows
669//! .iter()
670//! .filter(|w| w.owning_application()
671//! .map(|app| app.bundle_identifier() == "com.mycompany.myapp")
672//! .unwrap_or(false))
673//! .collect();
674//!
675//! // Capture everything except our windows
676//! let filter = SCContentFilter::create()
677//! .with_display(&display)
678//! .with_excluding_windows(&my_windows)
679//! .build();
680//! # Ok(())
681//! # }
682//! ```
683//!
684//! ### List All Available Content
685//!
686//! ```rust,no_run
687//! use screencapturekit::prelude::*;
688//!
689//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
690//! let content = SCShareableContent::get()?;
691//!
692//! println!("=== Displays ===");
693//! for display in content.displays() {
694//! println!(" Display {}: {}x{}", display.display_id(), display.width(), display.height());
695//! }
696//!
697//! println!("\n=== Windows ===");
698//! for window in content.windows().iter().filter(|w| w.is_on_screen()) {
699//! println!(" [{}] {} - {}",
700//! window.window_id(),
701//! window.owning_application()
702//! .map(|app| app.application_name())
703//! .unwrap_or_default(),
704//! window.title().unwrap_or_default()
705//! );
706//! }
707//!
708//! println!("\n=== Applications ===");
709//! for app in content.applications() {
710//! println!(" {} ({})", app.application_name(), app.bundle_identifier());
711//! }
712//! # Ok(())
713//! # }
714//! ```
715//!
716//! [`PixelFormat::BGRA`]: stream::configuration::PixelFormat::BGRA
717//! [`PixelFormat::l10r`]: stream::configuration::PixelFormat::l10r
718//! [`PixelFormat::YCbCr_420v`]: stream::configuration::PixelFormat::YCbCr_420v
719//! [`PixelFormat::YCbCr_420f`]: stream::configuration::PixelFormat::YCbCr_420f
720
721#![doc(html_root_url = "https://docs.rs/screencapturekit")]
722#![cfg_attr(docsrs, feature(doc_cfg))]
723#![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
724#![allow(clippy::must_use_candidate)]
725#![allow(clippy::missing_const_for_fn)]
726
727pub mod audio_devices;
728pub mod cg;
729pub mod cm;
730#[cfg(feature = "macos_14_0")]
731pub mod content_sharing_picker;
732pub mod cv;
733pub mod dispatch_queue;
734pub mod error;
735pub mod ffi;
736pub mod metal;
737#[cfg(feature = "macos_15_0")]
738pub mod recording_output;
739pub mod screenshot_manager;
740pub mod shareable_content;
741pub mod stream;
742pub mod utils;
743
744#[cfg(feature = "async")]
745pub mod async_api;
746
747// Re-export commonly used types
748pub use cm::{
749 codec_types, media_types, AudioBuffer, AudioBufferList, CMFormatDescription, CMSampleBuffer,
750 CMSampleTimingInfo, CMTime, IOSurface, SCFrameStatus,
751};
752pub use cv::{CVPixelBuffer, CVPixelBufferPool};
753pub use utils::four_char_code::FourCharCode;
754
755/// Prelude module for convenient imports
756///
757/// Import everything you need with:
758/// ```rust
759/// use screencapturekit::prelude::*;
760/// ```
761pub mod prelude {
762 pub use crate::audio_devices::AudioInputDevice;
763 pub use crate::cg::{CGPoint, CGRect, CGSize};
764 pub use crate::cm::{CMSampleBuffer, CMTime};
765 pub use crate::dispatch_queue::{DispatchQoS, DispatchQueue};
766 pub use crate::error::{SCError, SCResult};
767 pub use crate::shareable_content::{
768 SCDisplay, SCRunningApplication, SCShareableContent, SCWindow,
769 };
770 pub use crate::stream::{
771 configuration::{PixelFormat, SCStreamConfiguration},
772 content_filter::SCContentFilter,
773 delegate_trait::SCStreamDelegateTrait,
774 output_trait::SCStreamOutputTrait,
775 output_type::SCStreamOutputType,
776 sc_stream::SCStream,
777 ErrorHandler,
778 };
779}