/// <summary>
            /// Retrieves the moved and dirty regions of the currently duplicated frame, populating the specified desktop frame.
            /// </summary>
            /// <param name="frame"></param>
            /// <param name="frameInfo"></param>
            public void RetrieveFrameMetadata(DesktopFrame frame, OutduplFrameInfo frameInfo)
            {
                if (frameInfo.TotalMetadataBufferSize > 0)
                {
                    var movedRectangles = new OutduplMoveRect[frameInfo.TotalMetadataBufferSize];

                    // Get moved regions
                    _outputDuplication.GetFrameMoveRects(movedRectangles.Length, movedRectangles, out int movedRegionsLength);
                    var movedRegions = new MovedRegion[movedRegionsLength / Marshal.SizeOf(typeof(OutduplMoveRect))];
                    for (int i = 0; i < movedRegions.Length; i++)
                    {
                        movedRegions[i] = new MovedRegion()
                        {
                            Source      = new Point(movedRectangles[i].SourcePoint.X, movedRectangles[i].SourcePoint.Y),
                            Destination = new Rectangle(movedRectangles[i].DestinationRect.Left, movedRectangles[i].DestinationRect.Top, movedRectangles[i].DestinationRect.Right, movedRectangles[i].DestinationRect.Bottom)
                        };
                    }
                    frame.MovedRegions = movedRegions;

                    // Get dirty regions
                    var dirtyRectangles = new RawRect[frameInfo.TotalMetadataBufferSize];
                    _outputDuplication.GetFrameDirtyRects(dirtyRectangles.Length, dirtyRectangles, out int dirtyRegionsLength);
                    var updatedRegions = new Rectangle[dirtyRegionsLength / Marshal.SizeOf(typeof(Rectangle))];
                    for (int i = 0; i < updatedRegions.Length; i++)
                    {
                        updatedRegions[i] = new Rectangle(dirtyRectangles[i].Left, dirtyRectangles[i].Top, dirtyRectangles[i].Right, dirtyRectangles[i].Bottom);
                    }
                    frame.UpdatedRegions = updatedRegions;
                }
                else
                {
                    frame.MovedRegions   = Array.Empty <MovedRegion>();
                    frame.UpdatedRegions = Array.Empty <Rectangle>();
                }
            }
            /// <summary>
            /// Retrieves the next frame and copies the texture into the _desktopImageTexture.
            /// </summary>
            /// <param name="frameInformation"></param>
            /// <returns></returns>
            public bool TryRetrieveFrame(out OutduplFrameInfo frameInformation)
            {
                IDXGIResource desktopResource = null;

                try
                {
                    _outputDuplication.AcquireNextFrame(100, out frameInformation, out desktopResource);
                    using var tempTexture = desktopResource.QueryInterface <ID3D11Texture2D>();
                    _d3dDevice.ImmediateContext.CopyResource(_desktopImageTexture, tempTexture);
                    return(true);
                }
                catch (SharpGenException ex)
                {
                    if (ex.ResultCode.Failure && ex.Descriptor.NativeApiCode != "DXGI_ERROR_ACCESS_LOST" && ex.Descriptor.NativeApiCode != "DXGI_ERROR_WAIT_TIMEOUT")
                    {
                        throw new DesktopDuplicationException("Failed to acquire next frame.");
                    }
                }
                finally
                {
                    if (desktopResource != null)
                    {
                        desktopResource.Dispose();
                    }
                }

                frameInformation = new OutduplFrameInfo();
                return(false);
            }
            /// <summary>
            /// Retrieves the cursor information contained in the currently duplicated frame, populating the specified desktop frame.
            /// </summary>
            /// <param name="frame"></param>
            /// <param name="frameInfo"></param>
            public void RetrieveCursorMetadata(DesktopFrame frame, OutduplFrameInfo frameInfo)
            {
                // A non-zero mouse update timestamp indicates that there is a mouse position update and optionally a shape change
                if (frameInfo.LastMouseUpdateTime == 0)
                {
                    frame.PointerPosition.Visible = _pointerInfo.Visible;
                    frame.PointerPosition.X       = _pointerInfo.Position.X;
                    frame.PointerPosition.Y       = _pointerInfo.Position.Y;
                    return;
                }

                bool updatePosition = true;

                // Make sure we don't update pointer position wrongly
                // If pointer is invisible, make sure we did not get an update from another output that the last time that said pointer
                // was visible, if so, don't set it to invisible or update.

                if (!frameInfo.PointerPosition.Visible && (_pointerInfo.WhoUpdatedPositionLast != _outputDeviceIndex))
                {
                    updatePosition = false;
                }

                // If two outputs both say they have a visible, only update if new update has newer timestamp
                if (frameInfo.PointerPosition.Visible && _pointerInfo.Visible &&
                    (_pointerInfo.WhoUpdatedPositionLast != _outputDeviceIndex) &&
                    (_pointerInfo.LastTimeStamp > frameInfo.LastMouseUpdateTime))
                {
                    updatePosition = false;
                }

                // Update position
                if (updatePosition)
                {
                    _pointerInfo.Position = new Point(frameInfo.PointerPosition.Position.X, frameInfo.PointerPosition.Position.Y);
                    _pointerInfo.WhoUpdatedPositionLast = _outputDeviceIndex;
                    _pointerInfo.LastTimeStamp          = frameInfo.LastMouseUpdateTime;
                    _pointerInfo.Visible = frameInfo.PointerPosition.Visible;
                }

                frame.PointerPosition.Visible = _pointerInfo.Visible;
                frame.PointerPosition.X       = _pointerInfo.Position.X;
                frame.PointerPosition.Y       = _pointerInfo.Position.Y;

                // No new shape
                if (frameInfo.PointerShapeBufferSize == 0)
                {
                    return;
                }

                if (_pointerInfo.ShapeBuffer == null || frameInfo.PointerShapeBufferSize != _pointerInfo.ShapeBuffer.Length)
                {
                    _pointerInfo.ShapeBuffer = new byte[frameInfo.PointerShapeBufferSize];
                }

                try
                {
                    // Create a new pinned region of memory, copy the contents of the PtrShapeBuffer to it, use it to retrieve the pointer shape and then free the pinned region.
                    unsafe
                    {
                        fixed(byte *ptrShapeBufferPtr = _pointerInfo.ShapeBuffer)
                        {
                            _outputDuplication.GetFramePointerShape(frameInfo.PointerShapeBufferSize, (IntPtr)ptrShapeBufferPtr, out int bufferSize, out _pointerInfo.ShapeInfo);
                        }
                    }
                }
                catch (SharpGenException ex)
                {
                    if (ex.ResultCode.Failure)
                    {
                        throw new DesktopDuplicationException("Failed to get frame pointer shape.");
                    }
                }

                var pointerShapeSpan = new ReadOnlySpan <byte>(_pointerInfo.ShapeBuffer);

                if (!pointerShapeSpan.Trim((byte)0x00).IsEmpty)
                {
                    frame.PointerShape.Width    = _pointerInfo.ShapeInfo.Width;
                    frame.PointerShape.Height   = _pointerInfo.ShapeInfo.Height;
                    frame.PointerShape.Pitch    = _pointerInfo.ShapeInfo.Pitch;
                    frame.PointerShape.Type     = _pointerInfo.ShapeInfo.Type;
                    frame.PointerShape.HotSpotX = _pointerInfo.ShapeInfo.HotSpot.X;
                    frame.PointerShape.HotSpotY = _pointerInfo.ShapeInfo.HotSpot.Y;
                    frame.PointerShapeBuffer    = _pointerInfo.ShapeBuffer;
                }
            }