From ffd2fc1824c6ffd8a7a3cba3517043a4cd33cc58 Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Fri, 3 Nov 2017 00:18:37 +1100 Subject: [PATCH] [host] Fixed the DXGIManager to capture the cursor properly. Note: This class will be entirely re-written when I find some time, it is very poorly implemented, full of assumptions and creates a new texture for every single frame :facepalm:. --- .gitmodules | 3 - host/Capture/DXGI.h | 2 +- host/kvm-ivshmem-host.vcxproj | 12 +- host/kvm-ivshmem-host.vcxproj.filters | 4 +- vendor/DXGICapture/DXGIManager.cpp | 753 ++++++++++++++++++++++++++ vendor/DXGICapture/DXGIManager.h | 89 +++ vendor/DXGICapture/README | 5 + vendor/DXGICaptureSample | 1 - 8 files changed, 856 insertions(+), 13 deletions(-) create mode 100644 vendor/DXGICapture/DXGIManager.cpp create mode 100644 vendor/DXGICapture/DXGIManager.h create mode 100644 vendor/DXGICapture/README delete mode 160000 vendor/DXGICaptureSample diff --git a/.gitmodules b/.gitmodules index e6f85da7..b63750f8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ [submodule "vendor/kvm-guest-drivers-windows"] path = vendor/kvm-guest-drivers-windows url = https://github.com/virtio-win/kvm-guest-drivers-windows.git -[submodule "vendor/DXGICaptureSample"] - path = vendor/DXGICaptureSample - url = https://github.com/pgurenko/DXGICaptureSample.git diff --git a/host/Capture/DXGI.h b/host/Capture/DXGI.h index 1b8a4437..782e62da 100644 --- a/host/Capture/DXGI.h +++ b/host/Capture/DXGI.h @@ -19,7 +19,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA #pragma once #include "ICapture.h" -#include +#include #define W32_LEAN_AND_MEAN #include diff --git a/host/kvm-ivshmem-host.vcxproj b/host/kvm-ivshmem-host.vcxproj index 30f12e94..ca634f4c 100644 --- a/host/kvm-ivshmem-host.vcxproj +++ b/host/kvm-ivshmem-host.vcxproj @@ -96,7 +96,7 @@ Windows true - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;setupapi.lib;d3d11.lib;dxgi.lib;gdiplus.lib;%(AdditionalDependencies) + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;setupapi.lib;d3d11.lib;dxgi.lib;%(AdditionalDependencies) %(AdditionalLibraryDirectories) @@ -113,7 +113,7 @@ Windows true - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;setupapi.lib;d3d11.lib;dxgi.lib;gdiplus.lib;%(AdditionalDependencies) + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;setupapi.lib;d3d11.lib;dxgi.lib;%(AdditionalDependencies) %(AdditionalLibraryDirectories) @@ -134,7 +134,7 @@ true true true - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;setupapi.lib;d3d11.lib;dxgi.lib;gdiplus.lib;%(AdditionalDependencies) + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;setupapi.lib;d3d11.lib;dxgi.lib;%(AdditionalDependencies) %(AdditionalLibraryDirectories) @@ -155,12 +155,12 @@ true true true - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;setupapi.lib;d3d11.lib;dxgi.lib;gdiplus.lib;%(AdditionalDependencies) + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;setupapi.lib;d3d11.lib;dxgi.lib;%(AdditionalDependencies) %(AdditionalLibraryDirectories) - + @@ -169,7 +169,7 @@ - + diff --git a/host/kvm-ivshmem-host.vcxproj.filters b/host/kvm-ivshmem-host.vcxproj.filters index 4cf57003..7ae3d6f0 100644 --- a/host/kvm-ivshmem-host.vcxproj.filters +++ b/host/kvm-ivshmem-host.vcxproj.filters @@ -39,7 +39,7 @@ Source Files\Capture - + Source Files\Capture @@ -68,7 +68,7 @@ Header Files\Capture - + Header Files\Capture diff --git a/vendor/DXGICapture/DXGIManager.cpp b/vendor/DXGICapture/DXGIManager.cpp new file mode 100644 index 00000000..e9932de1 --- /dev/null +++ b/vendor/DXGICapture/DXGIManager.cpp @@ -0,0 +1,753 @@ +#include "DXGIManager.h" +#include "common\debug.h" + +DXGIPointerInfo::DXGIPointerInfo(BYTE* pPointerShape, UINT uiPointerShapeBufSize, DXGI_OUTDUPL_FRAME_INFO fi, DXGI_OUTDUPL_POINTER_SHAPE_INFO psi) + : m_pPointerShape(pPointerShape), + m_uiPointerShapeBufSize(uiPointerShapeBufSize), + m_FI(fi), + m_PSI(psi) +{ +} + +DXGIPointerInfo::~DXGIPointerInfo() +{ + if(m_pPointerShape) + { + delete [] m_pPointerShape; + } +} + +BYTE* DXGIPointerInfo::GetBuffer() +{ + return m_pPointerShape; +} + +UINT DXGIPointerInfo::GetBufferSize() +{ + return m_uiPointerShapeBufSize; +} + +DXGI_OUTDUPL_FRAME_INFO& DXGIPointerInfo::GetFrameInfo() +{ + return m_FI; +} + +DXGI_OUTDUPL_POINTER_SHAPE_INFO& DXGIPointerInfo::GetShapeInfo() +{ + return m_PSI; +} + +DXGIOutputDuplication::DXGIOutputDuplication(IDXGIAdapter1* pAdapter, + ID3D11Device* pD3DDevice, + ID3D11DeviceContext* pD3DDeviceContext, + IDXGIOutput1* pDXGIOutput1, + IDXGIOutputDuplication* pDXGIOutputDuplication) + : m_Adapter(pAdapter), + m_D3DDevice(pD3DDevice), + m_D3DDeviceContext(pD3DDeviceContext), + m_DXGIOutput1(pDXGIOutput1), + m_DXGIOutputDuplication(pDXGIOutputDuplication) +{ +} + +HRESULT DXGIOutputDuplication::GetDesc(DXGI_OUTPUT_DESC& desc) +{ + m_DXGIOutput1->GetDesc(&desc); + return S_OK; +} + +HRESULT DXGIOutputDuplication::AcquireNextFrame(IDXGISurface1** pDXGISurface, DXGIPointerInfo*& pDXGIPointer) +{ + DXGI_OUTDUPL_FRAME_INFO fi; + CComPtr spDXGIResource; + HRESULT hr = m_DXGIOutputDuplication->AcquireNextFrame(20, &fi, &spDXGIResource); + if(FAILED(hr)) + { + DEBUG_INFO("m_DXGIOutputDuplication->AcquireNextFrame failed with hr=0x%08x", hr); + return hr; + } + + CComQIPtr spTextureResource = spDXGIResource; + + D3D11_TEXTURE2D_DESC desc; + spTextureResource->GetDesc(&desc); + + D3D11_TEXTURE2D_DESC texDesc; + ZeroMemory( &texDesc, sizeof(texDesc) ); + texDesc.Width = desc.Width; + texDesc.Height = desc.Height; + texDesc.MipLevels = 1; + texDesc.ArraySize = 1; + texDesc.SampleDesc.Count = 1; + texDesc.SampleDesc.Quality = 0; + texDesc.Usage = D3D11_USAGE_STAGING; + texDesc.Format = desc.Format; + texDesc.BindFlags = 0; + texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + texDesc.MiscFlags = 0; + + CComPtr spD3D11Texture2D = NULL; + hr = m_D3DDevice->CreateTexture2D(&texDesc, NULL, &spD3D11Texture2D); + if(FAILED(hr)) + return hr; + + m_D3DDeviceContext->CopyResource(spD3D11Texture2D, spTextureResource); + + CComQIPtr spDXGISurface = spD3D11Texture2D; + + *pDXGISurface = spDXGISurface.Detach(); + + if (pDXGIPointer) + pDXGIPointer->GetFrameInfo().PointerPosition.Visible = fi.PointerPosition.Visible; + + // Updating mouse pointer, if visible + if(fi.PointerPosition.Visible) + { + BYTE* pPointerShape = new BYTE[fi.PointerShapeBufferSize]; + + DXGI_OUTDUPL_POINTER_SHAPE_INFO psi = {}; + UINT uiPointerShapeBufSize = fi.PointerShapeBufferSize; + hr = m_DXGIOutputDuplication->GetFramePointerShape(uiPointerShapeBufSize, pPointerShape, &uiPointerShapeBufSize, &psi); + if(hr == DXGI_ERROR_MORE_DATA) + { + pPointerShape = new BYTE[uiPointerShapeBufSize]; + + hr = m_DXGIOutputDuplication->GetFramePointerShape(uiPointerShapeBufSize, pPointerShape, &uiPointerShapeBufSize, &psi); + } + + if(hr == S_OK) + { + DEBUG_INFO("PointerPosition Visible=%d x=%d y=%d w=%d h=%d type=%d\n", fi.PointerPosition.Visible, fi.PointerPosition.Position.x, fi.PointerPosition.Position.y, psi.Width, psi.Height, psi.Type); + + if((psi.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME || + psi.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR || + psi.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR) && + psi.Width <= 128 && psi.Height <= 128) + { + // Here we can obtain pointer shape + if(pDXGIPointer) + { + delete pDXGIPointer; + } + + pDXGIPointer = new DXGIPointerInfo(pPointerShape, uiPointerShapeBufSize, fi, psi); + + pPointerShape = NULL; + } + + DXGI_OUTPUT_DESC outDesc; + GetDesc(outDesc); + + if(pDXGIPointer) + { + pDXGIPointer->GetFrameInfo().PointerPosition.Position.x = outDesc.DesktopCoordinates.left + fi.PointerPosition.Position.x; + pDXGIPointer->GetFrameInfo().PointerPosition.Position.y = outDesc.DesktopCoordinates.top + fi.PointerPosition.Position.y; + } + } + + if(pPointerShape) + { + delete [] pPointerShape; + } + } + + return hr; +} + +HRESULT DXGIOutputDuplication::ReleaseFrame() +{ + m_DXGIOutputDuplication->ReleaseFrame(); + return S_OK; +} + +bool DXGIOutputDuplication::IsPrimary() +{ + DXGI_OUTPUT_DESC outdesc; + m_DXGIOutput1->GetDesc(&outdesc); + + MONITORINFO mi; + mi.cbSize = sizeof(MONITORINFO); + GetMonitorInfo(outdesc.Monitor, &mi); + if(mi.dwFlags & MONITORINFOF_PRIMARY) + { + return true; + } + return false; +} + +DXGIManager::DXGIManager() +{ + m_CaptureSource = CSUndefined; + SetRect(&m_rcCurrentOutput, 0, 0, 0, 0); + m_pBuf = NULL; + m_pDXGIPointer = NULL; + m_bInitialized = false; +} + +DXGIManager::~DXGIManager() +{ + if(m_pBuf) + { + delete [] m_pBuf; + m_pBuf = NULL; + } + + if(m_pDXGIPointer) + { + delete m_pDXGIPointer; + m_pDXGIPointer = NULL; + } +} + +HRESULT DXGIManager::SetCaptureSource(CaptureSource cs) +{ + m_CaptureSource = cs; + return S_OK; +} + +CaptureSource DXGIManager::GetCaptureSource() +{ + return m_CaptureSource; +} + +HRESULT DXGIManager::Init() +{ + if(m_bInitialized) + return S_OK; + + HRESULT hr = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)(&m_spDXGIFactory1) ); + if( FAILED(hr) ) + { + DEBUG_ERROR("Failed to CreateDXGIFactory1 hr=%08x", hr); + return hr; + } + + // Getting all adapters + vector> vAdapters; + + CComPtr spAdapter; + for(int i=0; m_spDXGIFactory1->EnumAdapters1(i, &spAdapter) != DXGI_ERROR_NOT_FOUND; i++) + { + vAdapters.push_back(spAdapter); + spAdapter.Release(); + } + + // Iterating over all adapters to get all outputs + for(vector>::iterator AdapterIter = vAdapters.begin(); + AdapterIter != vAdapters.end(); + AdapterIter++) + { + vector> vOutputs; + + CComPtr spDXGIOutput; + for(int i=0; (*AdapterIter)->EnumOutputs(i, &spDXGIOutput) != DXGI_ERROR_NOT_FOUND; i++) + { + DXGI_OUTPUT_DESC outputDesc; + spDXGIOutput->GetDesc(&outputDesc); + + DEBUG_ERROR("Display output found. DeviceName=%ls AttachedToDesktop=%d Rotation=%d DesktopCoordinates={(%d,%d),(%d,%d)}", + outputDesc.DeviceName, + outputDesc.AttachedToDesktop, + outputDesc.Rotation, + outputDesc.DesktopCoordinates.left, + outputDesc.DesktopCoordinates.top, + outputDesc.DesktopCoordinates.right, + outputDesc.DesktopCoordinates.bottom); + + if(outputDesc.AttachedToDesktop) + { + vOutputs.push_back(spDXGIOutput); + } + + spDXGIOutput.Release(); + } + + if(vOutputs.size() == 0) + continue; + + // Creating device for each adapter that has the output + CComPtr spD3D11Device; + CComPtr spD3D11DeviceContext; + D3D_FEATURE_LEVEL fl = D3D_FEATURE_LEVEL_9_1; + hr = D3D11CreateDevice((*AdapterIter), D3D_DRIVER_TYPE_UNKNOWN, NULL, 0, NULL, 0, D3D11_SDK_VERSION, &spD3D11Device, &fl, &spD3D11DeviceContext); + if( FAILED(hr) ) + { + DEBUG_ERROR("Failed to create D3D11CreateDevice hr=%08x", hr); + return hr; + } + + for(std::vector>::iterator OutputIter = vOutputs.begin(); + OutputIter != vOutputs.end(); + OutputIter++) + { + CComQIPtr spDXGIOutput1 = *OutputIter; + if (!spDXGIOutput1) + { + DEBUG_ERROR("spDXGIOutput1 is NULL"); + continue; + } + + CComQIPtr spDXGIDevice = spD3D11Device; + if (!spDXGIDevice) + { + DEBUG_ERROR("spDXGIDevice is NULL"); + continue; + } + + CComPtr spDXGIOutputDuplication; + hr = spDXGIOutput1->DuplicateOutput(spDXGIDevice, &spDXGIOutputDuplication); + if (FAILED(hr)) + { + DEBUG_ERROR("Failed to duplicate output hr=%08x", hr); + continue; + } + + m_vOutputs.push_back( + DXGIOutputDuplication((*AdapterIter), + spD3D11Device, + spD3D11DeviceContext, + spDXGIOutput1, + spDXGIOutputDuplication)); + } + } + + hr = m_spWICFactory.CoCreateInstance(CLSID_WICImagingFactory); + if( FAILED(hr) ) + { + DEBUG_ERROR("Failed to create WICImagingFactory hr=%08x", hr); + return hr; + } + + m_bInitialized = true; + + return S_OK; +} + +HRESULT DXGIManager::GetOutputRect(RECT& rc) +{ + // Nulling rc just in case... + SetRect(&rc, 0, 0, 0, 0); + + HRESULT hr = Init(); + if(hr != S_OK) + return hr; + + vector vOutputs = GetOutputDuplication(); + + RECT rcShare; + SetRect(&rcShare, 0, 0, 0, 0); + + for(vector::iterator iter = vOutputs.begin(); + iter != vOutputs.end(); + iter++) + { + DXGIOutputDuplication& out = *iter; + + DXGI_OUTPUT_DESC outDesc; + out.GetDesc(outDesc); + RECT rcOutCoords = outDesc.DesktopCoordinates; + + UnionRect(&rcShare, &rcShare, &rcOutCoords); + } + + CopyRect(&rc, &rcShare); + + return S_OK; +} + +HRESULT DXGIManager::GetOutputBits(BYTE* pBits, RECT& rcDest) +{ + HRESULT hr = S_OK; + + DWORD dwDestWidth = rcDest.right - rcDest.left; + DWORD dwDestHeight = rcDest.bottom - rcDest.top; + + RECT rcOutput; + hr = GetOutputRect(rcOutput); + if( FAILED(hr) ) + return hr; + + DWORD dwOutputWidth = rcOutput.right - rcOutput.left; + DWORD dwOutputHeight = rcOutput.bottom - rcOutput.top; + + BYTE* pBuf = NULL; + if(rcOutput.right > (LONG)dwDestWidth || rcOutput.bottom > (LONG)dwDestHeight) + { + // Output is larger than pBits dimensions + if(!m_pBuf || !EqualRect(&m_rcCurrentOutput, &rcOutput)) + { + DWORD dwBufSize = dwOutputWidth*dwOutputHeight*4; + + if(m_pBuf) + { + delete [] m_pBuf; + m_pBuf = NULL; + } + + m_pBuf = new BYTE[dwBufSize]; + + CopyRect(&m_rcCurrentOutput, &rcOutput); + } + + pBuf = m_pBuf; + } + else + { + // Output is smaller than pBits dimensions + pBuf = pBits; + dwOutputWidth = dwDestWidth; + dwOutputHeight = dwDestHeight; + } + + vector vOutputs = GetOutputDuplication(); + + for(vector::iterator iter = vOutputs.begin(); + iter != vOutputs.end(); + iter++) + { + DXGIOutputDuplication& out = *iter; + + DXGI_OUTPUT_DESC outDesc; + out.GetDesc(outDesc); + RECT rcOutCoords = outDesc.DesktopCoordinates; + + CComPtr spDXGISurface1; + hr = out.AcquireNextFrame(&spDXGISurface1, m_pDXGIPointer); + if( FAILED(hr) ) + break; + + DXGI_MAPPED_RECT map; + spDXGISurface1->Map(&map, DXGI_MAP_READ); + + RECT rcDesktop = outDesc.DesktopCoordinates; + DWORD dwWidth = rcDesktop.right - rcDesktop.left; + DWORD dwHeight = rcDesktop.bottom - rcDesktop.top; + + OffsetRect(&rcDesktop, -rcOutput.left, -rcOutput.top); + + DWORD dwMapPitchPixels = map.Pitch/4; + + switch(outDesc.Rotation) + { + case DXGI_MODE_ROTATION_IDENTITY: + { + // Just copying + DWORD dwStripe = dwWidth*4; + for(unsigned int i=0; iUnmap(); + + out.ReleaseFrame(); + } + + if(FAILED(hr)) + return hr; + + // We have the pBuf filled with current desktop/monitor image. + if(pBuf != pBits) + { + DrawMousePointer(pBuf, rcOutput, rcOutput); + + // pBuf contains the image that should be resized + CComPtr spBitmap = NULL; + hr = m_spWICFactory->CreateBitmapFromMemory(dwOutputWidth, dwOutputHeight, GUID_WICPixelFormat32bppBGRA, dwOutputWidth*4, dwOutputWidth*dwOutputHeight*4, (BYTE*)pBuf, &spBitmap); + if( FAILED(hr) ) + return hr; + + CComPtr spBitmapScaler = NULL; + hr = m_spWICFactory->CreateBitmapScaler(&spBitmapScaler); + if( FAILED(hr) ) + return hr; + + dwOutputWidth = rcOutput.right - rcOutput.left; + dwOutputHeight = rcOutput.bottom - rcOutput.top; + + double aspect = (double)dwOutputWidth/(double)dwOutputHeight; + + DWORD scaledWidth = dwDestWidth; + DWORD scaledHeight = dwDestHeight; + + if(aspect > 1) + { + scaledWidth = dwDestWidth; + scaledHeight = (DWORD)(dwDestWidth/aspect); + } + else + { + scaledWidth = (DWORD)(aspect*dwDestHeight); + scaledHeight = dwDestHeight; + } + + spBitmapScaler->Initialize( + spBitmap, scaledWidth, scaledHeight, WICBitmapInterpolationModeNearestNeighbor); + + spBitmapScaler->CopyPixels(NULL, scaledWidth*4, dwDestWidth*dwDestHeight*4, pBits); + } + else + DrawMousePointer(pBuf, rcOutput, rcDest); + return hr; +} + +void DXGIManager::DrawMousePointer(BYTE* pDesktopBits, RECT rcDesktop, RECT rcDest) +{ + const DXGI_OUTDUPL_FRAME_INFO frameInfo = m_pDXGIPointer->GetFrameInfo(); + + if(!m_pDXGIPointer || !frameInfo.PointerPosition.Visible) + return; + + const DWORD dwDesktopWidth = rcDesktop.right - rcDesktop.left; + const DWORD dwDesktopHeight = rcDesktop.bottom - rcDesktop.top; + + const DWORD dwDestWidth = rcDest.right - rcDest.left; + const DWORD dwDestHeight = rcDest.bottom - rcDest.top; + + const DXGI_OUTDUPL_POINTER_SHAPE_INFO shapeInfo = m_pDXGIPointer->GetShapeInfo(); + const int PtrX = frameInfo.PointerPosition.Position.x - rcDesktop.left; + const int PtrY = frameInfo.PointerPosition.Position.y - rcDesktop.top; + + BYTE *PtrBuf = m_pDXGIPointer->GetBuffer(); + + switch(shapeInfo.Type) + { + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR: + { + // alpha blend the cursor + const int maxX = min(shapeInfo.Width , dwDesktopWidth - PtrX); + const int maxY = min(shapeInfo.Height, dwDesktopHeight - PtrY); + for(int y = 0; y < maxY; ++y) + { + for (int x = 0; x < maxX; ++x) + { + BYTE *srcPix = &PtrBuf[y * shapeInfo.Pitch + x * 4]; + BYTE *dstPix = &pDesktopBits[((PtrY + y) * dwDesktopWidth * 4) + (PtrX + x) * 4]; + + // if fully transparent just continue + if (srcPix[3] == 0x00) + continue; + + // if fully opaque just copy the pixel + if (srcPix[3] == 0xff) + { + dstPix[0] = srcPix[0]; + dstPix[1] = srcPix[1]; + dstPix[2] = srcPix[2]; + continue; + } + + //TODO: optimize this to use integer math + + float src[3]; + float dst[3]; + float alpha; + static const float conv = 1.0f / 255.0f; + + // convert to float + src[0] = (float)srcPix[0] * conv; + src[1] = (float)srcPix[1] * conv; + src[2] = (float)srcPix[2] * conv; + dst[0] = (float)dstPix[0] * conv; + dst[1] = (float)dstPix[1] * conv; + dst[2] = (float)dstPix[2] * conv; + alpha = (float)srcPix[3] * conv; + + // blend and convert back to 8bit + dstPix[0] = (BYTE)(max(0.0f, min(1.0f, alpha * src[0] + dst[0] * (1.0f - alpha))) * 255.0f); + dstPix[1] = (BYTE)(max(0.0f, min(1.0f, alpha * src[1] + dst[1] * (1.0f - alpha))) * 255.0f); + dstPix[2] = (BYTE)(max(0.0f, min(1.0f, alpha * src[2] + dst[2] * (1.0f - alpha))) * 255.0f); + } + } + break; + } + break; + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME: + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR: + { + RECT rcPointer; + + if(shapeInfo.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME) + { + SetRect(&rcPointer, PtrX, PtrY, PtrX + shapeInfo.Width, PtrY + shapeInfo.Height/2); + } + else + { + SetRect(&rcPointer, PtrX, PtrY, PtrX + shapeInfo.Width, PtrY + shapeInfo.Height); + } + + RECT rcDesktopPointer; + IntersectRect(&rcDesktopPointer, &rcPointer, &rcDesktop); + + CopyRect(&rcPointer, &rcDesktopPointer); + OffsetRect(&rcPointer, -PtrX, -PtrY); + + BYTE* pShapeBuffer = m_pDXGIPointer->GetBuffer(); + UINT* pDesktopBits32 = (UINT*)pDesktopBits; + + if(shapeInfo.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME) + { + for(int j = rcPointer.top, jDP = rcDesktopPointer.top; + j> (i % 8); + BYTE AndMask = pShapeBuffer[i/8 + (shapeInfo.Pitch)*j] & Mask; + BYTE XorMask = pShapeBuffer[i/8 + (shapeInfo.Pitch)*(j + shapeInfo.Height / 2)] & Mask; + + UINT AndMask32 = (AndMask) ? 0xFFFFFFFF : 0xFF000000; + UINT XorMask32 = (XorMask) ? 0x00FFFFFF : 0x00000000; + + pDesktopBits32[jDP*dwDestWidth + iDP] = (pDesktopBits32[jDP*dwDestWidth + iDP] & AndMask32) ^ XorMask32; + } + } + } + else + { + UINT* pShapeBuffer32 = (UINT*)pShapeBuffer; + for(int j = rcPointer.top, jDP = rcDesktopPointer.top; + j DXGIManager::GetOutputDuplication() +{ + vector outputs; + switch(m_CaptureSource) + { + case CSMonitor1: + { + // Return the one with IsPrimary + for(vector::iterator iter = m_vOutputs.begin(); + iter != m_vOutputs.end(); + iter++) + { + DXGIOutputDuplication& out = *iter; + if(out.IsPrimary()) + { + outputs.push_back(out); + break; + } + } + } + break; + + case CSMonitor2: + { + // Return the first with !IsPrimary + for(vector::iterator iter = m_vOutputs.begin(); + iter != m_vOutputs.end(); + iter++) + { + DXGIOutputDuplication& out = *iter; + if(!out.IsPrimary()) + { + outputs.push_back(out); + break; + } + } + } + break; + + case CSDesktop: + { + // Return all outputs + for(vector::iterator iter = m_vOutputs.begin(); + iter != m_vOutputs.end(); + iter++) + { + DXGIOutputDuplication& out = *iter; + outputs.push_back(out); + } + } + break; + } + return outputs; +} + +BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) +{ + int *Count = (int*)dwData; + (*Count)++; + return TRUE; +} + +int DXGIManager::GetMonitorCount() +{ + int Count = 0; + if (EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)&Count)) + return Count; + return -1; +} diff --git a/vendor/DXGICapture/DXGIManager.h b/vendor/DXGICapture/DXGIManager.h new file mode 100644 index 00000000..792cfed4 --- /dev/null +++ b/vendor/DXGICapture/DXGIManager.h @@ -0,0 +1,89 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +class DXGIPointerInfo; + +enum CaptureSource +{ + CSUndefined, + CSMonitor1, + CSMonitor2, + CSDesktop +}; + +class DXGIPointerInfo +{ +public: + DXGIPointerInfo(BYTE* pPointerShape, UINT uiPointerShapeBufSize, DXGI_OUTDUPL_FRAME_INFO fi, DXGI_OUTDUPL_POINTER_SHAPE_INFO psi); + ~DXGIPointerInfo(); + BYTE* GetBuffer(); + UINT GetBufferSize(); + DXGI_OUTDUPL_FRAME_INFO& GetFrameInfo(); + DXGI_OUTDUPL_POINTER_SHAPE_INFO& GetShapeInfo(); + +private: + BYTE* m_pPointerShape; + UINT m_uiPointerShapeBufSize; + DXGI_OUTDUPL_POINTER_SHAPE_INFO m_PSI; + DXGI_OUTDUPL_FRAME_INFO m_FI; +}; + +class DXGIOutputDuplication +{ +public: + DXGIOutputDuplication(IDXGIAdapter1* pAdapter, + ID3D11Device* pD3DDevice, + ID3D11DeviceContext* pD3DDeviceContext, + IDXGIOutput1* pDXGIOutput1, + IDXGIOutputDuplication* pDXGIOutputDuplication); + + HRESULT GetDesc(DXGI_OUTPUT_DESC& desc); + HRESULT AcquireNextFrame(IDXGISurface1** pD3D11Texture2D, DXGIPointerInfo*& pDXGIPointer); + HRESULT ReleaseFrame(); + + bool IsPrimary(); + +private: + CComPtr m_Adapter; + CComPtr m_D3DDevice; + CComPtr m_D3DDeviceContext; + CComPtr m_DXGIOutput1; + CComPtr m_DXGIOutputDuplication; +}; + +class DXGIManager +{ +public: + DXGIManager(); + ~DXGIManager(); + HRESULT SetCaptureSource(CaptureSource type); + CaptureSource GetCaptureSource(); + + HRESULT GetOutputRect(RECT& rc); + HRESULT GetOutputBits(BYTE* pBits, RECT& rcDest); +private: + HRESULT Init(); + int GetMonitorCount(); + vector GetOutputDuplication(); + void DrawMousePointer(BYTE* pDesktopBits, RECT rcDesktop, RECT rcDest); +private: + CComPtr m_spDXGIFactory1; + vector m_vOutputs; + bool m_bInitialized; + CaptureSource m_CaptureSource; + RECT m_rcCurrentOutput; + BYTE* m_pBuf; + + CComPtr m_spWICFactory; + ULONG_PTR m_gdiplusToken; + DXGIPointerInfo* m_pDXGIPointer; +}; \ No newline at end of file diff --git a/vendor/DXGICapture/README b/vendor/DXGICapture/README new file mode 100644 index 00000000..ef0b5d36 --- /dev/null +++ b/vendor/DXGICapture/README @@ -0,0 +1,5 @@ +code in this directory is temporary and requires a re-write, it is horrible and +to be deprecated ASAP. + +The reason for keeping a local copy is because I have fixed some stupid bugs +and implemented proper alpha blending for the cursor. diff --git a/vendor/DXGICaptureSample b/vendor/DXGICaptureSample deleted file mode 160000 index 9e304f24..00000000 --- a/vendor/DXGICaptureSample +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9e304f24b5b11bbd1c61a9c1c528693cd2843c5d