Skip to main content

dxgi_capture_rs/
lib.rs

1//! High-performance screen capturing with DXGI Desktop Duplication API for Windows.
2//!
3//! This library provides a Rust interface to the Windows DXGI Desktop Duplication API,
4//! enabling efficient screen capture with minimal performance overhead.
5//!
6//! # Features
7//!
8//! - **High Performance**: Direct access to DXGI Desktop Duplication API
9//! - **Multiple Monitor Support**: Capture from any available display
10//! - **Flexible Output**: Get pixel data as [`BGRA8`] or raw component bytes
11//! - **Frame Metadata**: Access dirty rectangles, moved rectangles, and timing information
12//! - **Comprehensive Error Handling**: Robust error types for production use
13//! - **Windows Optimized**: Specifically designed for Windows platforms
14//!
15//! # Platform Requirements
16//!
17//! - Windows 8 or later (DXGI 1.2+ required)
18//! - Compatible graphics driver supporting Desktop Duplication
19//! - Active desktop session (not suitable for headless environments)
20//!
21//! # Quick Start
22//!
23//! ```rust,no_run
24//! use dxgi_capture_rs::{DXGIManager, CaptureError};
25//!
26//! fn main() -> Result<(), Box<dyn std::error::Error>> {
27//!     let mut manager = DXGIManager::new(1000)?;
28//!     
29//!     match manager.capture_frame() {
30//!         Ok((pixels, (width, height))) => {
31//!             println!("Captured {}x{} frame", width, height);
32//!             // Process pixels (Vec<BGRA8>)
33//!         }
34//!         Err(CaptureError::Timeout) => {
35//!             // No new frame - normal occurrence
36//!         }
37//!         Err(e) => eprintln!("Capture failed: {:?}", e),
38//!     }
39//!     Ok(())
40//! }
41//! ```
42//!
43//! # Multi-Monitor Support
44//!
45//! ```rust,no_run
46//! # use dxgi_capture_rs::DXGIManager;
47//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
48//! let mut manager = DXGIManager::new(1000)?;
49//!
50//! manager.set_capture_source_index(0); // Primary monitor
51//! let (pixels, dimensions) = manager.capture_frame()?;
52//!
53//! manager.set_capture_source_index(1); // Secondary monitor
54//! let (pixels, dimensions) = manager.capture_frame()?;
55//! # Ok(())
56//! # }
57//! ```
58//!
59//! # Frame Metadata for Streaming Applications
60//!
61//! The library provides detailed frame metadata including dirty rectangles and moved rectangles,
62//! which is crucial for optimizing streaming and remote desktop applications.
63//!
64//! ```rust,no_run
65//! # use dxgi_capture_rs::DXGIManager;
66//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
67//! let mut manager = DXGIManager::new(1000)?;
68//!
69//! match manager.capture_frame_with_metadata() {
70//!     Ok((pixels, (width, height), metadata)) => {
71//!         // Only process frame if there are actual changes
72//!         if metadata.has_updates() {
73//!             println!("Frame has {} dirty rects and {} move rects",
74//!                      metadata.dirty_rects.len(), metadata.move_rects.len());
75//!             
76//!             // Process moved rectangles first (as per Microsoft recommendation)
77//!             for move_rect in &metadata.move_rects {
78//!                 let (src_x, src_y) = move_rect.source_point;
79//!                 let (dst_left, dst_top, dst_right, dst_bottom) = move_rect.destination_rect;
80//!                 
81//!                 // Copy pixels from source to destination
82//!                 // This is much more efficient than re-encoding the entire area
83//!                 copy_rectangle(&pixels, src_x, src_y, dst_left, dst_top,
84//!                               dst_right - dst_left, dst_bottom - dst_top);
85//!             }
86//!             
87//!             // Then process dirty rectangles
88//!             for &(left, top, right, bottom) in &metadata.dirty_rects {
89//!                 let width = (right - left) as usize;
90//!                 let height = (bottom - top) as usize;
91//!                 
92//!                 // Only encode/transmit the changed region
93//!                 encode_region(&pixels, left as usize, top as usize, width, height);
94//!             }
95//!         }
96//!         
97//!         // Check for mouse cursor updates
98//!         if metadata.has_mouse_updates() {
99//!             if let Some((x, y)) = metadata.pointer_position {
100//!                 println!("Mouse cursor at ({}, {}), visible: {}", x, y, metadata.pointer_visible);
101//!             }
102//!         }
103//!     }
104//!     Err(e) => eprintln!("Capture failed: {:?}", e),
105//! }
106//!
107//! # fn copy_rectangle(pixels: &[dxgi_capture_rs::BGRA8], src_x: i32, src_y: i32,
108//! #                   dst_x: i32, dst_y: i32, width: i32, height: i32) {}
109//! # fn encode_region(pixels: &[dxgi_capture_rs::BGRA8], x: usize, y: usize, width: usize, height: usize) {}
110//! # Ok(())
111//! # }
112//! ```
113//!
114//! # Error Handling
115//!
116//! ```rust,no_run
117//! # use dxgi_capture_rs::{DXGIManager, CaptureError};
118//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
119//! let mut manager = DXGIManager::new(1000)?;
120//!
121//! match manager.capture_frame() {
122//!     Ok((pixels, dimensions)) => { /* Process successful capture */ }
123//!     Err(CaptureError::Timeout) => { /* No new frame - normal */ }
124//!     Err(CaptureError::AccessDenied) => { /* Protected content */ }
125//!     Err(CaptureError::AccessLost) => { /* Display mode changed */ }
126//!     Err(e) => eprintln!("Capture failed: {:?}", e),
127//! }
128//! # Ok(())
129//! # }
130//! ```
131//!
132//! # Performance Considerations
133//!
134//! - Use appropriate timeout values based on your frame rate requirements
135//! - Consider using [`DXGIManager::capture_frame_components`] for raw byte data
136//! - Memory usage scales with screen resolution
137//! - The library automatically handles screen rotation
138//! - Use metadata to optimize streaming by only processing changed regions
139//! - Process move rectangles before dirty rectangles for correct visual output
140//!
141//! # Thread Safety
142//!
143//! [`DXGIManager`] is not thread-safe. Create separate instances for each thread
144//! if you need concurrent capture operations.
145
146#![cfg(windows)]
147#![cfg_attr(docsrs, feature(doc_cfg))]
148#![cfg_attr(docsrs, doc(cfg(windows)))]
149
150use std::fmt;
151use std::{mem, slice};
152use windows::Win32::Graphics::Direct3D::D3D_FEATURE_LEVEL;
153use windows::{
154    Win32::{
155        Foundation::{HMODULE, RECT},
156        Graphics::{
157            Direct3D::{D3D_DRIVER_TYPE_UNKNOWN, D3D_FEATURE_LEVEL_9_1},
158            Direct3D11::{
159                D3D11_CPU_ACCESS_READ, D3D11_CREATE_DEVICE_BGRA_SUPPORT, D3D11_SDK_VERSION,
160                D3D11_TEXTURE2D_DESC, D3D11_USAGE_STAGING, D3D11CreateDevice, ID3D11Device,
161                ID3D11DeviceContext, ID3D11Texture2D,
162            },
163            Dxgi::{
164                Common::{
165                    DXGI_MODE_ROTATION_IDENTITY, DXGI_MODE_ROTATION_ROTATE90,
166                    DXGI_MODE_ROTATION_ROTATE180, DXGI_MODE_ROTATION_ROTATE270,
167                    DXGI_MODE_ROTATION_UNSPECIFIED,
168                },
169                CreateDXGIFactory1, DXGI_ERROR_ACCESS_DENIED, DXGI_ERROR_ACCESS_LOST,
170                DXGI_ERROR_NOT_FOUND, DXGI_ERROR_WAIT_TIMEOUT, DXGI_MAP_READ, DXGI_MAPPED_RECT,
171                DXGI_OUTDUPL_FRAME_INFO, DXGI_OUTDUPL_MOVE_RECT, DXGI_OUTPUT_DESC, IDXGIAdapter,
172                IDXGIAdapter1, IDXGIFactory1, IDXGIOutput, IDXGIOutput1, IDXGIOutputDuplication,
173                IDXGIResource, IDXGISurface1,
174            },
175        },
176    },
177    core::{Interface, Result as WindowsResult},
178};
179
180/// A pixel color in BGRA8 format.
181///
182/// Each channel can hold values from 0 to 255. The channels are ordered as BGRA
183/// to match the Windows DXGI format.
184#[derive(Copy, Clone, Debug, PartialOrd, PartialEq, Eq, Ord)]
185pub struct BGRA8 {
186    /// Blue channel (0-255)
187    pub b: u8,
188    /// Green channel (0-255)
189    pub g: u8,
190    /// Red channel (0-255)
191    pub r: u8,
192    /// Alpha channel (0-255, where 0 is transparent and 255 is opaque)
193    pub a: u8,
194}
195
196/// Represents a rectangle that has been moved from one location to another.
197///
198/// This structure describes a region that was moved from a source location to
199/// a destination location, which is useful for optimizing screen updates in
200/// streaming applications.
201#[derive(Copy, Clone, Debug, PartialEq, Eq)]
202pub struct MoveRect {
203    /// The source point where the content was moved from (top-left corner)
204    pub source_point: (i32, i32),
205    /// The destination rectangle where the content was moved to
206    pub destination_rect: (i32, i32, i32, i32), // (left, top, right, bottom)
207}
208
209/// Metadata about a captured frame.
210///
211/// This structure contains timing information, dirty regions, moved regions,
212/// and other metadata that can help optimize screen capture and streaming
213/// applications.
214#[derive(Clone, Debug)]
215pub struct FrameMetadata {
216    /// Timestamp of the last desktop image update (Windows performance counter)
217    pub last_present_time: i64,
218    /// Timestamp of the last mouse update (Windows performance counter)
219    pub last_mouse_update_time: i64,
220    /// Number of frames accumulated since the last processed frame
221    pub accumulated_frames: u32,
222    /// Whether dirty regions were coalesced and may contain unmodified pixels
223    pub rects_coalesced: bool,
224    /// Whether protected content was masked out in the captured frame
225    pub protected_content_masked_out: bool,
226    /// Mouse cursor position and visibility
227    pub pointer_position: Option<(i32, i32)>,
228    /// Whether the mouse cursor is visible
229    pub pointer_visible: bool,
230    /// List of dirty rectangles that have changed since the last frame
231    pub dirty_rects: Vec<(i32, i32, i32, i32)>, // (left, top, right, bottom)
232    /// List of move rectangles that have been moved since the last frame
233    pub move_rects: Vec<MoveRect>,
234}
235
236impl FrameMetadata {
237    /// Returns true if the frame contains any updates (dirty regions or moves)
238    pub fn has_updates(&self) -> bool {
239        !self.dirty_rects.is_empty() || !self.move_rects.is_empty()
240    }
241
242    /// Returns true if the mouse cursor has been updated
243    pub fn has_mouse_updates(&self) -> bool {
244        self.last_mouse_update_time > 0
245    }
246
247    /// Returns the total number of changed regions
248    pub fn total_change_count(&self) -> usize {
249        self.dirty_rects.len() + self.move_rects.len()
250    }
251}
252
253/// Errors that can occur during screen capture operations.
254#[derive(Debug)]
255pub enum CaptureError {
256    /// Access to the output duplication was denied.
257    ///
258    /// This typically occurs when attempting to capture protected content,
259    /// such as fullscreen video with DRM protection.
260    ///
261    /// **Recovery**: Check if protected content is being displayed.
262    AccessDenied,
263
264    /// Access to the duplicated output was lost.
265    ///
266    /// This occurs when the display configuration changes, such as:
267    /// - Switching between windowed and fullscreen mode
268    /// - Changing display resolution
269    /// - Connecting/disconnecting monitors
270    /// - Graphics driver updates
271    ///
272    /// **Recovery**: Recreate the [`DXGIManager`] instance.
273    AccessLost,
274
275    /// Failed to refresh the output duplication after a previous error.
276    ///
277    /// **Recovery**: Recreate the [`DXGIManager`] instance or wait before retrying.
278    RefreshFailure,
279
280    /// The capture operation timed out.
281    ///
282    /// This is a normal occurrence indicating that no new frame was available
283    /// within the specified timeout period.
284    ///
285    /// **Recovery**: This is not an error condition. Simply retry the capture.
286    Timeout,
287
288    /// A general or unexpected failure occurred.
289    ///
290    /// **Recovery**: Log the error message and consider recreating the [`DXGIManager`].
291    Fail(windows::core::Error),
292}
293
294impl fmt::Display for CaptureError {
295    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
296        match self {
297            CaptureError::AccessDenied => write!(f, "Access to output duplication was denied"),
298            CaptureError::AccessLost => write!(f, "Access to duplicated output was lost"),
299            CaptureError::RefreshFailure => write!(f, "Failed to refresh output duplication"),
300            CaptureError::Timeout => write!(f, "Capture operation timed out"),
301            CaptureError::Fail(msg) => write!(f, "Capture failed: {msg}"),
302        }
303    }
304}
305
306impl std::error::Error for CaptureError {}
307
308impl From<windows::core::Error> for CaptureError {
309    fn from(err: windows::core::Error) -> Self {
310        CaptureError::Fail(err)
311    }
312}
313
314/// Errors that can occur during output duplication initialization.
315#[derive(Debug)]
316pub enum OutputDuplicationError {
317    /// No suitable output display was found.
318    ///
319    /// This occurs when no displays are connected, all displays are disabled,
320    /// or the graphics driver doesn't support Desktop Duplication.
321    ///
322    /// **Recovery**: Ensure a display is connected and graphics drivers support Desktop Duplication.
323    NoOutput,
324
325    /// Failed to create the D3D11 device or duplicate the output.
326    ///
327    /// This can occur due to graphics driver issues, insufficient system resources,
328    /// or incompatible graphics hardware.
329    ///
330    /// **Recovery**: Check graphics driver installation and system resources.
331    DeviceError(windows::core::Error),
332}
333
334impl fmt::Display for OutputDuplicationError {
335    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
336        match self {
337            OutputDuplicationError::NoOutput => write!(f, "No suitable output display was found"),
338            OutputDuplicationError::DeviceError(err) => {
339                write!(f, "Failed to create D3D11 device: {err}")
340            }
341        }
342    }
343}
344
345impl std::error::Error for OutputDuplicationError {}
346
347impl From<windows::core::Error> for OutputDuplicationError {
348    fn from(err: windows::core::Error) -> Self {
349        OutputDuplicationError::DeviceError(err)
350    }
351}
352
353/// Checks whether a Windows HRESULT represents a failure condition.
354///
355/// # Deprecation
356///
357/// This function is a trivial wrapper around [`windows::core::HRESULT::is_err`].
358/// Use `hr.is_err()` directly instead.
359///
360/// # Examples
361///
362/// ```rust
363/// use dxgi_capture_rs::hr_failed;
364/// use windows::core::HRESULT;
365/// use windows::Win32::Foundation::{S_OK, E_FAIL};
366///
367/// // Success codes
368/// assert!(!hr_failed(S_OK));
369/// assert!(!hr_failed(HRESULT(1)));
370///
371/// // Failure codes
372/// assert!(hr_failed(E_FAIL));
373/// assert!(hr_failed(HRESULT(-1)));
374/// ```
375#[deprecated(since = "1.2.0", note = "Use `HRESULT::is_err()` directly instead")]
376pub fn hr_failed(hr: windows::core::HRESULT) -> bool {
377    hr.is_err()
378}
379
380// ---------------------------------------------------------------------------
381// Internal helpers
382// ---------------------------------------------------------------------------
383
384fn create_dxgi_factory_1() -> WindowsResult<IDXGIFactory1> {
385    unsafe { CreateDXGIFactory1() }
386}
387
388const DEFAULT_D3D11_FEATURES: [D3D_FEATURE_LEVEL; 1] = [D3D_FEATURE_LEVEL_9_1];
389
390fn d3d11_create_device(
391    adapter: Option<&IDXGIAdapter>,
392    feature_levels: Option<&[D3D_FEATURE_LEVEL]>,
393) -> WindowsResult<(ID3D11Device, ID3D11DeviceContext)> {
394    let mut device: Option<ID3D11Device> = None;
395    let mut device_context: Option<ID3D11DeviceContext> = None;
396
397    unsafe {
398        D3D11CreateDevice(
399            adapter,
400            D3D_DRIVER_TYPE_UNKNOWN,
401            HMODULE::default(),
402            D3D11_CREATE_DEVICE_BGRA_SUPPORT,
403            feature_levels,
404            D3D11_SDK_VERSION,
405            Some(&mut device),
406            None,
407            Some(&mut device_context),
408        )?;
409    }
410
411    Ok((device.unwrap(), device_context.unwrap()))
412}
413
414/// Enumerates the desktop-attached outputs for a given adapter and returns
415/// only the one at the requested index (if it exists).
416fn get_output_at_index(
417    adapter: &IDXGIAdapter1,
418    index: usize,
419) -> WindowsResult<Option<IDXGIOutput>> {
420    let mut current = 0usize;
421    for i in 0.. {
422        match unsafe { adapter.EnumOutputs(i) } {
423            Ok(output) => {
424                let desc: DXGI_OUTPUT_DESC = unsafe { output.GetDesc()? };
425                if desc.AttachedToDesktop.as_bool() {
426                    if current == index {
427                        return Ok(Some(output));
428                    }
429                    current += 1;
430                }
431            }
432            Err(_) => break,
433        }
434    }
435    Ok(None)
436}
437
438/// Maps a Windows error from a capture operation into the appropriate
439/// [`CaptureError`] variant.
440fn map_capture_error(e: windows::core::Error) -> CaptureError {
441    let code = e.code();
442    if code == DXGI_ERROR_ACCESS_LOST {
443        CaptureError::AccessLost
444    } else if code == DXGI_ERROR_WAIT_TIMEOUT {
445        CaptureError::Timeout
446    } else if code == DXGI_ERROR_ACCESS_DENIED {
447        CaptureError::AccessDenied
448    } else {
449        CaptureError::Fail(e)
450    }
451}
452
453// ---------------------------------------------------------------------------
454// DuplicatedOutput — internal handle to a single duplicated output
455// ---------------------------------------------------------------------------
456
457struct DuplicatedOutput {
458    device: ID3D11Device,
459    device_context: ID3D11DeviceContext,
460    output: IDXGIOutput1,
461    output_duplication: IDXGIOutputDuplication,
462}
463
464impl DuplicatedOutput {
465    fn get_desc(&self) -> WindowsResult<DXGI_OUTPUT_DESC> {
466        unsafe { self.output.GetDesc() }
467    }
468
469    /// Acquires a frame, optionally extracts metadata, copies it to a staging
470    /// texture, releases the DXGI frame, and returns the mapped surface.
471    fn capture_frame_to_surface(
472        &mut self,
473        timeout_ms: u32,
474        with_metadata: bool,
475    ) -> WindowsResult<(IDXGISurface1, Option<FrameMetadata>)> {
476        let mut resource: Option<IDXGIResource> = None;
477        let mut frame_info: DXGI_OUTDUPL_FRAME_INFO = unsafe { mem::zeroed() };
478
479        unsafe {
480            self.output_duplication
481                .AcquireNextFrame(timeout_ms, &mut frame_info, &mut resource)?
482        };
483
484        let metadata = if with_metadata {
485            Some(self.extract_frame_metadata(&frame_info)?)
486        } else {
487            None
488        };
489
490        let texture: ID3D11Texture2D = resource.unwrap().cast()?;
491        let mut desc = D3D11_TEXTURE2D_DESC::default();
492        unsafe { texture.GetDesc(&mut desc) };
493        desc.Usage = D3D11_USAGE_STAGING;
494        desc.BindFlags = 0;
495        desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ.0 as u32;
496        desc.MiscFlags = 0;
497
498        let mut staged_texture: Option<ID3D11Texture2D> = None;
499        unsafe {
500            self.device
501                .CreateTexture2D(&desc, None, Some(&mut staged_texture))?
502        };
503        let staged_texture = staged_texture.unwrap();
504
505        unsafe { self.device_context.CopyResource(&staged_texture, &texture) };
506
507        unsafe { self.output_duplication.ReleaseFrame()? };
508
509        let surface: IDXGISurface1 = staged_texture.cast()?;
510        Ok((surface, metadata))
511    }
512
513    fn extract_frame_metadata(
514        &self,
515        frame_info: &DXGI_OUTDUPL_FRAME_INFO,
516    ) -> WindowsResult<FrameMetadata> {
517        let mut dirty_rects = Vec::new();
518        let mut move_rects = Vec::new();
519
520        if frame_info.TotalMetadataBufferSize > 0 {
521            // Get dirty rectangles
522            let mut dirty_rects_buffer_size = 0u32;
523            let dirty_result = unsafe {
524                self.output_duplication.GetFrameDirtyRects(
525                    0,
526                    std::ptr::null_mut(),
527                    &mut dirty_rects_buffer_size,
528                )
529            };
530
531            if dirty_result.is_ok() && dirty_rects_buffer_size > 0 {
532                let dirty_rect_count = dirty_rects_buffer_size / mem::size_of::<RECT>() as u32;
533                let mut dirty_rects_buffer: Vec<RECT> =
534                    vec![RECT::default(); dirty_rect_count as usize];
535                unsafe {
536                    let get_result = self.output_duplication.GetFrameDirtyRects(
537                        dirty_rects_buffer_size,
538                        dirty_rects_buffer.as_mut_ptr(),
539                        &mut dirty_rects_buffer_size,
540                    );
541                    if get_result.is_ok() {
542                        dirty_rects = dirty_rects_buffer
543                            .into_iter()
544                            .map(|rect| (rect.left, rect.top, rect.right, rect.bottom))
545                            .collect();
546                    }
547                }
548            }
549
550            // Get move rectangles
551            let mut move_rects_buffer_size = 0u32;
552            let move_result = unsafe {
553                self.output_duplication.GetFrameMoveRects(
554                    0,
555                    std::ptr::null_mut(),
556                    &mut move_rects_buffer_size,
557                )
558            };
559
560            if move_result.is_ok() && move_rects_buffer_size > 0 {
561                let move_rect_count =
562                    move_rects_buffer_size / mem::size_of::<DXGI_OUTDUPL_MOVE_RECT>() as u32;
563                let mut move_rects_buffer: Vec<DXGI_OUTDUPL_MOVE_RECT> =
564                    vec![unsafe { mem::zeroed() }; move_rect_count as usize];
565                unsafe {
566                    let get_result = self.output_duplication.GetFrameMoveRects(
567                        move_rects_buffer_size,
568                        move_rects_buffer.as_mut_ptr(),
569                        &mut move_rects_buffer_size,
570                    );
571                    if get_result.is_ok() {
572                        move_rects = move_rects_buffer
573                            .into_iter()
574                            .map(|move_rect| MoveRect {
575                                source_point: (move_rect.SourcePoint.x, move_rect.SourcePoint.y),
576                                destination_rect: (
577                                    move_rect.DestinationRect.left,
578                                    move_rect.DestinationRect.top,
579                                    move_rect.DestinationRect.right,
580                                    move_rect.DestinationRect.bottom,
581                                ),
582                            })
583                            .collect();
584                    }
585                }
586            }
587        }
588
589        let pointer_position = if frame_info.PointerPosition.Visible.as_bool() {
590            Some((
591                frame_info.PointerPosition.Position.x,
592                frame_info.PointerPosition.Position.y,
593            ))
594        } else {
595            None
596        };
597
598        Ok(FrameMetadata {
599            last_present_time: frame_info.LastPresentTime,
600            last_mouse_update_time: frame_info.LastMouseUpdateTime,
601            accumulated_frames: frame_info.AccumulatedFrames,
602            rects_coalesced: frame_info.RectsCoalesced.as_bool(),
603            protected_content_masked_out: frame_info.ProtectedContentMaskedOut.as_bool(),
604            pointer_position,
605            pointer_visible: frame_info.PointerPosition.Visible.as_bool(),
606            dirty_rects,
607            move_rects,
608        })
609    }
610}
611
612// ---------------------------------------------------------------------------
613// DXGIManager — public API
614// ---------------------------------------------------------------------------
615
616/// The main manager for handling DXGI desktop duplication.
617///
618/// `DXGIManager` provides a high-level interface to the Windows DXGI Desktop
619/// Duplication API, enabling efficient screen capture operations. It manages
620/// the underlying DXGI resources and provides methods to capture screen content
621/// as pixel data.
622///
623/// # Usage
624///
625/// The typical workflow involves:
626/// 1. Creating a manager with [`DXGIManager::new`]
627/// 2. Optionally configuring the capture source and timeout
628/// 3. Capturing frames using [`DXGIManager::capture_frame`] or [`DXGIManager::capture_frame_components`]
629///
630/// # Examples
631///
632/// ## Basic Usage
633///
634/// ```rust,no_run
635/// use dxgi_capture_rs::DXGIManager;
636///
637/// let mut manager = DXGIManager::new(1000)?;
638/// let (width, height) = manager.geometry();
639///
640/// match manager.capture_frame() {
641///     Ok((pixels, (w, h))) => {
642///         println!("Captured {}x{} frame with {} pixels", w, h, pixels.len());
643///     }
644///     Err(e) => {
645///         eprintln!("Capture failed: {:?}", e);
646///     }
647/// }
648/// # Ok::<(), Box<dyn std::error::Error>>(())
649/// ```
650///
651/// ## Multi-Monitor Setup
652///
653/// ```rust,no_run
654/// use dxgi_capture_rs::DXGIManager;
655///
656/// let mut manager = DXGIManager::new(1000)?;
657///
658/// // Capture from primary display (default)
659/// manager.set_capture_source_index(0);
660/// let primary_frame = manager.capture_frame();
661///
662/// // Capture from secondary display (if available)
663/// manager.set_capture_source_index(1);
664/// let secondary_frame = manager.capture_frame();
665/// # Ok::<(), Box<dyn std::error::Error>>(())
666/// ```
667///
668/// ## Timeout Configuration
669///
670/// ```rust,no_run
671/// use dxgi_capture_rs::DXGIManager;
672///
673/// let mut manager = DXGIManager::new(500)?;
674///
675/// // Adjust timeout for different scenarios
676/// manager.set_timeout_ms(100);  // Fast polling
677/// manager.set_timeout_ms(2000); // Slower polling
678/// manager.set_timeout_ms(0);    // No timeout (immediate return)
679/// # Ok::<(), Box<dyn std::error::Error>>(())
680/// ```
681///
682/// # Thread Safety
683///
684/// `DXGIManager` is not thread-safe. If you need to capture from multiple
685/// threads, create separate instances for each thread.
686///
687/// # Resource Management
688///
689/// The manager automatically handles cleanup of DXGI resources when dropped.
690/// However, if you encounter [`CaptureError::AccessLost`], you should create
691/// a new manager instance to re-establish the connection to the display system.
692pub struct DXGIManager {
693    factory: IDXGIFactory1,
694    duplicated_output: Option<DuplicatedOutput>,
695    capture_source_index: usize,
696    timeout_ms: u32,
697}
698
699impl DXGIManager {
700    /// Creates a new `DXGIManager` instance.
701    ///
702    /// This initializes the DXGI factory and sets up the necessary resources
703    /// for screen capture. The `timeout_ms` parameter specifies the default
704    /// timeout for frame capture operations.
705    ///
706    /// # Errors
707    ///
708    /// Returns an error if the DXGI manager cannot be initialized, which
709    /// typically occurs if the required graphics components are not available.
710    pub fn new(timeout_ms: u32) -> Result<Self, OutputDuplicationError> {
711        let factory = create_dxgi_factory_1()?;
712        let mut manager = Self {
713            factory,
714            duplicated_output: None,
715            capture_source_index: 0,
716            timeout_ms,
717        };
718        manager.acquire_output_duplication()?;
719        Ok(manager)
720    }
721
722    /// Returns the screen geometry (width, height) of the current capture source.
723    ///
724    /// Returns the width and height of the display being captured, in pixels.
725    /// This corresponds to the resolution of the selected capture source.
726    ///
727    /// # Returns
728    ///
729    /// A tuple `(width, height)` where:
730    /// - `width` is the horizontal resolution in pixels
731    /// - `height` is the vertical resolution in pixels
732    ///
733    /// # Examples
734    ///
735    /// ```rust,no_run
736    /// use dxgi_capture_rs::DXGIManager;
737    ///
738    /// let manager = DXGIManager::new(1000)?;
739    /// let (width, height) = manager.geometry();
740    /// println!("Display resolution: {}x{}", width, height);
741    /// # Ok::<(), Box<dyn std::error::Error>>(())
742    /// ```
743    pub fn geometry(&self) -> (usize, usize) {
744        if let Some(ref output) = self.duplicated_output {
745            let output_desc = output.get_desc().expect("Failed to get output description");
746            let RECT {
747                left,
748                top,
749                right,
750                bottom,
751            } = output_desc.DesktopCoordinates;
752            ((right - left) as usize, (bottom - top) as usize)
753        } else {
754            (0, 0)
755        }
756    }
757
758    /// Sets the capture source index to select which display to capture from.
759    ///
760    /// In multi-monitor setups, this method allows you to choose which display
761    /// to capture from. Index 0 always refers to the primary display, while
762    /// indices 1 and higher refer to secondary displays.
763    ///
764    /// # Arguments
765    ///
766    /// * `cs` - The capture source index:
767    ///   - `0` = Primary display (default)
768    ///   - `1` = First secondary display
769    ///   - `2` = Second secondary display, etc.
770    ///
771    /// # Examples
772    ///
773    /// ```rust,no_run
774    /// use dxgi_capture_rs::DXGIManager;
775    ///
776    /// let mut manager = DXGIManager::new(1000)?;
777    ///
778    /// // Capture from primary display (default)
779    /// manager.set_capture_source_index(0);
780    /// let primary_frame = manager.capture_frame();
781    ///
782    /// // Switch to secondary display
783    /// manager.set_capture_source_index(1);
784    /// let secondary_frame = manager.capture_frame();
785    /// # Ok::<(), Box<dyn std::error::Error>>(())
786    /// ```
787    ///
788    /// # Notes
789    ///
790    /// - Setting an invalid index (e.g., for a non-existent display) will not
791    ///   cause an immediate error, but subsequent capture operations may fail
792    /// - This method automatically reinitializes the capture system for the new display
793    /// - The geometry may change when switching between displays of different resolutions
794    pub fn set_capture_source_index(&mut self, cs: usize) {
795        let previous_index = self.capture_source_index;
796        self.capture_source_index = cs;
797
798        if self.acquire_output_duplication().is_err() && cs == 0 && cs != previous_index {
799            self.capture_source_index = previous_index;
800            let _ = self.acquire_output_duplication();
801        }
802    }
803
804    /// Gets the current capture source index.
805    ///
806    /// Returns the index of the display currently being used for capture operations.
807    ///
808    /// # Returns
809    ///
810    /// The current capture source index:
811    /// - `0` = Primary display
812    /// - `1` = First secondary display  
813    /// - `2` = Second secondary display, etc.
814    ///
815    /// # Examples
816    ///
817    /// ```rust,no_run
818    /// use dxgi_capture_rs::DXGIManager;
819    ///
820    /// let mut manager = DXGIManager::new(1000)?;
821    ///
822    /// // Initially set to primary display
823    /// assert_eq!(manager.get_capture_source_index(), 0);
824    ///
825    /// // Switch to secondary display
826    /// manager.set_capture_source_index(1);
827    /// assert_eq!(manager.get_capture_source_index(), 1);
828    /// # Ok::<(), Box<dyn std::error::Error>>(())
829    /// ```
830    pub fn get_capture_source_index(&self) -> usize {
831        self.capture_source_index
832    }
833
834    /// Sets the timeout for capture operations.
835    ///
836    /// This timeout determines how long capture operations will wait for a new
837    /// frame to become available before returning with a timeout error.
838    ///
839    /// # Arguments
840    ///
841    /// * `timeout_ms` - The timeout in milliseconds:
842    ///   - `0` = No timeout (immediate return if no frame available)
843    ///   - `1-1000` = Short timeout for real-time applications
844    ///   - `1000-5000` = Medium timeout for interactive applications
845    ///   - `>5000` = Long timeout for less frequent captures
846    ///
847    /// # Examples
848    ///
849    /// ```rust,no_run
850    /// use dxgi_capture_rs::DXGIManager;
851    ///
852    /// let mut manager = DXGIManager::new(1000)?;
853    ///
854    /// // Set short timeout for real-time capture
855    /// manager.set_timeout_ms(100);
856    ///
857    /// // Set no timeout for immediate return
858    /// manager.set_timeout_ms(0);
859    ///
860    /// // Set longer timeout for less frequent captures
861    /// manager.set_timeout_ms(5000);
862    /// # Ok::<(), Box<dyn std::error::Error>>(())
863    /// ```
864    pub fn set_timeout_ms(&mut self, timeout_ms: u32) {
865        self.timeout_ms = timeout_ms
866    }
867
868    /// Gets the current timeout value for capture operations.
869    ///
870    /// Returns the timeout in milliseconds that capture operations will wait
871    /// for a new frame to become available.
872    ///
873    /// # Returns
874    ///
875    /// The current timeout in milliseconds:
876    /// - `0` = No timeout (immediate return)
877    /// - `>0` = Timeout in milliseconds
878    ///
879    /// # Examples
880    ///
881    /// ```rust,no_run
882    /// use dxgi_capture_rs::DXGIManager;
883    ///
884    /// let mut manager = DXGIManager::new(1000)?;
885    ///
886    /// // Check initial timeout
887    /// assert_eq!(manager.get_timeout_ms(), 1000);
888    ///
889    /// // Change timeout and verify
890    /// manager.set_timeout_ms(500);
891    /// assert_eq!(manager.get_timeout_ms(), 500);
892    /// # Ok::<(), Box<dyn std::error::Error>>(())
893    /// ```
894    pub fn get_timeout_ms(&self) -> u32 {
895        self.timeout_ms
896    }
897
898    /// Reinitializes the output duplication for the selected capture source.
899    ///
900    /// This method is automatically called when needed, but can be called manually
901    /// to recover from certain error conditions. It reinitializes the DXGI
902    /// Desktop Duplication system for the currently selected capture source.
903    ///
904    /// # Returns
905    ///
906    /// Returns `Ok(())` on success, or `Err(OutputDuplicationError)` if the
907    /// reinitialization fails.
908    ///
909    /// # Errors
910    ///
911    /// - [`OutputDuplicationError::NoOutput`] if no suitable display is found
912    /// - [`OutputDuplicationError::DeviceError`] if device creation fails
913    ///
914    /// # Examples
915    ///
916    /// ```rust,no_run
917    /// use dxgi_capture_rs::{DXGIManager, CaptureError};
918    ///
919    /// let mut manager = DXGIManager::new(1000)?;
920    ///
921    /// // Manually reinitialize if needed
922    /// match manager.acquire_output_duplication() {
923    ///     Ok(()) => println!("Successfully reinitialized"),
924    ///     Err(e) => println!("Failed to reinitialize: {:?}", e),
925    /// }
926    /// # Ok::<(), Box<dyn std::error::Error>>(())
927    /// ```
928    pub fn acquire_output_duplication(&mut self) -> Result<(), OutputDuplicationError> {
929        // Drop any existing output duplication first, releasing the COM
930        // resources before attempting to acquire new ones.
931        self.duplicated_output = None;
932
933        for i in 0.. {
934            let adapter = match unsafe { self.factory.EnumAdapters1(i) } {
935                Ok(adapter) => adapter,
936                Err(e) if e.code() == DXGI_ERROR_NOT_FOUND => break,
937                Err(e) => return Err(e.into()),
938            };
939
940            let (mut d3d11_device, mut device_context) =
941                match d3d11_create_device(Some(&adapter.cast()?), Some(&DEFAULT_D3D11_FEATURES)) {
942                    Ok(device) => device,
943                    Err(_) => continue,
944                };
945
946            // Only look up and duplicate the single output we actually need.
947            let output = match get_output_at_index(&adapter, self.capture_source_index)? {
948                Some(output) => output,
949                None => continue,
950            };
951
952            let output1: IDXGIOutput1 = output.cast()?;
953
954            let output_duplication = match unsafe { output1.DuplicateOutput(&d3d11_device) } {
955                Ok(dup) => dup,
956                Err(_) => {
957                    // Retry creating the device without any features.
958                    match d3d11_create_device(Some(&adapter.cast()?), None) {
959                        Ok((new_device, new_context)) => {
960                            // Retry duplication with the newly created device.
961                            match unsafe { output1.DuplicateOutput(&new_device) } {
962                                Ok(output_dup) => {
963                                    d3d11_device = new_device;
964                                    device_context = new_context;
965                                    output_dup
966                                }
967                                Err(_) => continue,
968                            }
969                        }
970                        Err(_) => continue,
971                    }
972                }
973            };
974
975            self.duplicated_output = Some(DuplicatedOutput {
976                device: d3d11_device,
977                device_context,
978                output: output1,
979                output_duplication,
980            });
981            return Ok(());
982        }
983        Err(OutputDuplicationError::NoOutput)
984    }
985
986    // -----------------------------------------------------------------------
987    // Internal capture helpers
988    // -----------------------------------------------------------------------
989
990    /// Acquires a frame surface, optionally with metadata.  On recoverable
991    /// DXGI errors the internal `duplicated_output` is reset so the next
992    /// capture attempt will re-acquire.
993    fn acquire_surface(
994        &mut self,
995        with_metadata: bool,
996    ) -> Result<(IDXGISurface1, Option<FrameMetadata>), CaptureError> {
997        if self.duplicated_output.is_none() && self.acquire_output_duplication().is_err() {
998            return Err(CaptureError::RefreshFailure);
999        }
1000
1001        let timeout_ms = self.timeout_ms;
1002        let dup = self.duplicated_output.as_mut().unwrap();
1003
1004        match dup.capture_frame_to_surface(timeout_ms, with_metadata) {
1005            Ok(result) => Ok(result),
1006            Err(e) => {
1007                let err = map_capture_error(e);
1008                // On non-timeout errors, drop the output so it is re-acquired.
1009                if !matches!(err, CaptureError::Timeout) {
1010                    self.duplicated_output = None;
1011                }
1012                Err(err)
1013            }
1014        }
1015    }
1016
1017    /// Reads pixel data from a mapped surface, handling rotation. This is the
1018    /// single source of truth for the rotation-aware copy logic. `T` is either
1019    /// [`BGRA8`] or `u8`.
1020    fn copy_surface_data<T: Copy + Send + Sync + Sized>(
1021        &self,
1022        surface: &IDXGISurface1,
1023    ) -> Result<(Vec<T>, (usize, usize)), CaptureError> {
1024        let mut rect = DXGI_MAPPED_RECT::default();
1025        unsafe { surface.Map(&mut rect, DXGI_MAP_READ)? };
1026
1027        let desc = self
1028            .duplicated_output
1029            .as_ref()
1030            .ok_or(CaptureError::RefreshFailure)?
1031            .get_desc()?;
1032        let width = (desc.DesktopCoordinates.right - desc.DesktopCoordinates.left) as usize;
1033        let height = (desc.DesktopCoordinates.bottom - desc.DesktopCoordinates.top) as usize;
1034
1035        let pitch = rect.Pitch as usize;
1036        let source = rect.pBits;
1037
1038        let (rotated_width, rotated_height) = match desc.Rotation {
1039            DXGI_MODE_ROTATION_ROTATE90 | DXGI_MODE_ROTATION_ROTATE270 => (height, width),
1040            _ => (width, height),
1041        };
1042
1043        let bytes_per_pixel = mem::size_of::<BGRA8>() / mem::size_of::<T>();
1044        let source_slice = unsafe {
1045            slice::from_raw_parts(source as *const T, pitch * height / mem::size_of::<T>())
1046        };
1047
1048        let mut data_vec: Vec<T> =
1049            Vec::with_capacity(rotated_width * rotated_height * bytes_per_pixel);
1050
1051        match desc.Rotation {
1052            DXGI_MODE_ROTATION_IDENTITY | DXGI_MODE_ROTATION_UNSPECIFIED => {
1053                for i in 0..height {
1054                    let start = i * pitch / mem::size_of::<T>();
1055                    let end = start + width * bytes_per_pixel;
1056                    data_vec.extend_from_slice(&source_slice[start..end]);
1057                }
1058            }
1059            DXGI_MODE_ROTATION_ROTATE90 => {
1060                for i in 0..width {
1061                    for j in (0..height).rev() {
1062                        let index = j * pitch / mem::size_of::<T>() + i * bytes_per_pixel;
1063                        data_vec.extend_from_slice(&source_slice[index..index + bytes_per_pixel]);
1064                    }
1065                }
1066            }
1067            DXGI_MODE_ROTATION_ROTATE180 => {
1068                for i in (0..height).rev() {
1069                    for j in (0..width).rev() {
1070                        let index = i * pitch / mem::size_of::<T>() + j * bytes_per_pixel;
1071                        data_vec.extend_from_slice(&source_slice[index..index + bytes_per_pixel]);
1072                    }
1073                }
1074            }
1075            DXGI_MODE_ROTATION_ROTATE270 => {
1076                for i in (0..width).rev() {
1077                    for j in 0..height {
1078                        let index = j * pitch / mem::size_of::<T>() + i * bytes_per_pixel;
1079                        data_vec.extend_from_slice(&source_slice[index..index + bytes_per_pixel]);
1080                    }
1081                }
1082            }
1083            _ => {}
1084        }
1085
1086        unsafe { surface.Unmap()? };
1087
1088        Ok((data_vec, (rotated_width, rotated_height)))
1089    }
1090
1091    // -----------------------------------------------------------------------
1092    // Public capture methods
1093    // -----------------------------------------------------------------------
1094
1095    /// Captures a single frame and returns it as a `Vec<BGRA8>`.
1096    ///
1097    /// This method captures the current screen content and returns it as a vector
1098    /// of [`BGRA8`] pixels along with the frame dimensions. The method waits for
1099    /// a new frame to become available, up to the configured timeout.
1100    ///
1101    /// # Returns
1102    ///
1103    /// On success, returns `Ok((pixels, (width, height)))` where:
1104    /// - `pixels` is a `Vec<BGRA8>` containing the pixel data
1105    /// - `width` and `height` are the frame dimensions in pixels
1106    /// - Pixels are stored in row-major order (left-to-right, top-to-bottom)
1107    ///
1108    /// # Examples
1109    ///
1110    /// ```rust,no_run
1111    /// use dxgi_capture_rs::{DXGIManager, CaptureError};
1112    ///
1113    /// let mut manager = DXGIManager::new(1000)?;
1114    ///
1115    /// match manager.capture_frame() {
1116    ///     Ok((pixels, (width, height))) => {
1117    ///         println!("Captured {}x{} frame with {} pixels", width, height, pixels.len());
1118    ///     }
1119    ///     Err(CaptureError::Timeout) => {
1120    ///         // No new frame available within timeout
1121    ///     }
1122    ///     Err(e) => eprintln!("Capture failed: {:?}", e),
1123    /// }
1124    /// # Ok::<(), Box<dyn std::error::Error>>(())
1125    /// ```
1126    pub fn capture_frame(&mut self) -> Result<(Vec<BGRA8>, (usize, usize)), CaptureError> {
1127        let (surface, _) = self.acquire_surface(false)?;
1128        self.copy_surface_data(&surface)
1129    }
1130
1131    /// Captures a single frame and returns it as a `Vec<u8>`.
1132    ///
1133    /// This method captures the current screen content and returns it as a vector
1134    /// of raw bytes representing the pixel components. Each pixel is represented
1135    /// by 4 consecutive bytes in BGRA order.
1136    ///
1137    /// # Returns
1138    ///
1139    /// On success, returns `Ok((components, (width, height)))` where:
1140    /// - `components` is a `Vec<u8>` containing the raw pixel component data
1141    /// - `width` and `height` are the frame dimensions in pixels
1142    /// - Components are stored as [B, G, R, A, B, G, R, A, ...] in row-major order
1143    ///
1144    /// # Examples
1145    ///
1146    /// ```rust,no_run
1147    /// use dxgi_capture_rs::DXGIManager;
1148    ///
1149    /// let mut manager = DXGIManager::new(1000)?;
1150    ///
1151    /// match manager.capture_frame_components() {
1152    ///     Ok((components, (width, height))) => {
1153    ///         println!("Captured {}x{} frame with {} bytes", width, height, components.len());
1154    ///     }
1155    ///     Err(e) => eprintln!("Capture failed: {:?}", e),
1156    /// }
1157    /// # Ok::<(), Box<dyn std::error::Error>>(())
1158    /// ```
1159    pub fn capture_frame_components(&mut self) -> Result<(Vec<u8>, (usize, usize)), CaptureError> {
1160        let (surface, _) = self.acquire_surface(false)?;
1161        self.copy_surface_data(&surface)
1162    }
1163
1164    /// Captures a single frame with minimal overhead for performance-critical applications.
1165    ///
1166    /// This method provides the fastest possible screen capture by minimizing memory
1167    /// allocations and copying. Returns raw pixel data without rotation handling.
1168    ///
1169    /// # Returns
1170    ///
1171    /// On success, returns `Ok((pixels, (width, height)))` where:
1172    /// - `pixels` is a `Vec<u8>` containing raw BGRA pixel data
1173    /// - `width` and `height` are the frame dimensions in pixels
1174    /// - Data is in the native orientation (no rotation correction)
1175    ///
1176    /// # Examples
1177    ///
1178    /// ```rust,no_run
1179    /// use dxgi_capture_rs::DXGIManager;
1180    ///
1181    /// let mut manager = DXGIManager::new(100)?;
1182    ///
1183    /// match manager.capture_frame_fast() {
1184    ///     Ok((pixels, (width, height))) => {
1185    ///         println!("Fast captured {}x{} frame", width, height);
1186    ///     }
1187    ///     Err(e) => eprintln!("Fast capture failed: {:?}", e),
1188    /// }
1189    /// # Ok::<(), Box<dyn std::error::Error>>(())
1190    /// ```
1191    pub fn capture_frame_fast(&mut self) -> Result<(Vec<u8>, (usize, usize)), CaptureError> {
1192        let (surface, _) = self.acquire_surface(false)?;
1193
1194        let mut rect = DXGI_MAPPED_RECT::default();
1195        unsafe { surface.Map(&mut rect, DXGI_MAP_READ)? };
1196
1197        let desc = self
1198            .duplicated_output
1199            .as_ref()
1200            .ok_or(CaptureError::RefreshFailure)?
1201            .get_desc()?;
1202        let width = (desc.DesktopCoordinates.right - desc.DesktopCoordinates.left) as usize;
1203        let height = (desc.DesktopCoordinates.bottom - desc.DesktopCoordinates.top) as usize;
1204
1205        let pitch = rect.Pitch as usize;
1206        let source = rect.pBits;
1207
1208        let bytes_per_row = width * 4;
1209        let mut data_vec = Vec::with_capacity(width * height * 4);
1210
1211        unsafe {
1212            if pitch == bytes_per_row {
1213                let total_bytes = width * height * 4;
1214                let source_slice = slice::from_raw_parts(source as *const u8, total_bytes);
1215                data_vec.extend_from_slice(source_slice);
1216            } else {
1217                let source_slice = slice::from_raw_parts(source as *const u8, pitch * height);
1218                for row in 0..height {
1219                    let row_start = row * pitch;
1220                    let row_end = row_start + bytes_per_row;
1221                    data_vec.extend_from_slice(&source_slice[row_start..row_end]);
1222                }
1223            }
1224        }
1225
1226        unsafe { surface.Unmap()? };
1227
1228        Ok((data_vec, (width, height)))
1229    }
1230
1231    /// Captures a single frame and returns it as `Vec<BGRA8>` along with frame metadata.
1232    ///
1233    /// This method captures the current screen content and returns it as a vector
1234    /// of [`BGRA8`] pixels along with comprehensive metadata about the frame, including
1235    /// dirty rectangles, moved rectangles, and timing information.
1236    ///
1237    /// # Returns
1238    ///
1239    /// On success, returns `Ok((pixels, (width, height), metadata))` where:
1240    /// - `pixels` is a `Vec<BGRA8>` containing the pixel data
1241    /// - `width` and `height` are the frame dimensions in pixels
1242    /// - `metadata` is a [`FrameMetadata`] struct containing detailed frame information
1243    ///
1244    /// # Examples
1245    ///
1246    /// ```rust,no_run
1247    /// use dxgi_capture_rs::{DXGIManager, CaptureError};
1248    ///
1249    /// let mut manager = DXGIManager::new(1000)?;
1250    ///
1251    /// match manager.capture_frame_with_metadata() {
1252    ///     Ok((pixels, (width, height), metadata)) => {
1253    ///         println!("Captured {}x{} frame with {} dirty rects and {} move rects",
1254    ///                  width, height, metadata.dirty_rects.len(), metadata.move_rects.len());
1255    ///     }
1256    ///     Err(CaptureError::Timeout) => {
1257    ///         // No new frame available within timeout
1258    ///     }
1259    ///     Err(e) => eprintln!("Capture failed: {:?}", e),
1260    /// }
1261    /// # Ok::<(), Box<dyn std::error::Error>>(())
1262    /// ```
1263    pub fn capture_frame_with_metadata(&mut self) -> CaptureFrameWithMetadataResult {
1264        let (surface, metadata) = self.acquire_surface(true)?;
1265        let (data, dims) = self.copy_surface_data::<BGRA8>(&surface)?;
1266        Ok((data, dims, metadata.unwrap()))
1267    }
1268
1269    /// Captures a single frame and returns it as `Vec<u8>` along with frame metadata.
1270    ///
1271    /// This method captures the current screen content and returns it as a vector
1272    /// of raw bytes representing the pixel components along with comprehensive
1273    /// metadata about the frame.
1274    ///
1275    /// # Returns
1276    ///
1277    /// On success, returns `Ok((components, (width, height), metadata))` where:
1278    /// - `components` is a `Vec<u8>` containing the raw pixel component data
1279    /// - `width` and `height` are the frame dimensions in pixels
1280    /// - `metadata` is a [`FrameMetadata`] struct containing detailed frame information
1281    ///
1282    /// # Examples
1283    ///
1284    /// ```rust,no_run
1285    /// use dxgi_capture_rs::DXGIManager;
1286    ///
1287    /// let mut manager = DXGIManager::new(1000)?;
1288    ///
1289    /// match manager.capture_frame_components_with_metadata() {
1290    ///     Ok((components, (width, height), metadata)) => {
1291    ///         println!("Captured {}x{} frame with {} bytes", width, height, components.len());
1292    ///         if metadata.has_updates() {
1293    ///             println!("Frame has {} total changes", metadata.total_change_count());
1294    ///         }
1295    ///     }
1296    ///     Err(e) => eprintln!("Capture failed: {:?}", e),
1297    /// }
1298    /// # Ok::<(), Box<dyn std::error::Error>>(())
1299    /// ```
1300    pub fn capture_frame_components_with_metadata(
1301        &mut self,
1302    ) -> CaptureFrameComponentsWithMetadataResult {
1303        let (surface, metadata) = self.acquire_surface(true)?;
1304        let (data, dims) = self.copy_surface_data::<u8>(&surface)?;
1305        Ok((data, dims, metadata.unwrap()))
1306    }
1307}
1308
1309pub type CaptureFrameWithMetadataResult =
1310    Result<(Vec<BGRA8>, (usize, usize), FrameMetadata), CaptureError>;
1311
1312pub type CaptureFrameComponentsWithMetadataResult =
1313    Result<(Vec<u8>, (usize, usize), FrameMetadata), CaptureError>;