2017-11-02 06:55:25 +00:00
|
|
|
/*
|
2017-12-03 15:31:16 +00:00
|
|
|
Looking Glass - KVM FrameRelay (KVMFR) Client
|
2017-11-02 06:55:25 +00:00
|
|
|
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it under
|
|
|
|
the terms of the GNU General Public License as published by the Free Software
|
|
|
|
Foundation; either version 2 of the License, or (at your option) any later
|
|
|
|
version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
|
|
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License along with
|
|
|
|
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
|
|
|
Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "DXGI.h"
|
|
|
|
using namespace Capture;
|
|
|
|
|
2017-12-05 09:39:54 +00:00
|
|
|
#include "Util.h"
|
2017-11-02 11:37:19 +00:00
|
|
|
#include "common\debug.h"
|
2017-11-17 04:24:01 +00:00
|
|
|
#include "common\memcpySSE.h"
|
2017-11-02 11:37:19 +00:00
|
|
|
|
2017-11-02 06:55:25 +00:00
|
|
|
DXGI::DXGI() :
|
2017-11-16 09:53:22 +00:00
|
|
|
m_options(NULL),
|
2017-11-02 11:37:19 +00:00
|
|
|
m_initialized(false),
|
2017-11-03 11:20:48 +00:00
|
|
|
m_dxgiFactory(NULL),
|
|
|
|
m_device(NULL),
|
|
|
|
m_deviceContext(NULL),
|
|
|
|
m_dup(NULL),
|
2017-11-03 13:14:30 +00:00
|
|
|
m_texture(NULL),
|
|
|
|
m_pointer(NULL)
|
2017-11-02 06:55:25 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
DXGI::~DXGI()
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2017-11-16 09:53:22 +00:00
|
|
|
bool DXGI::Initialize(CaptureOptions * options)
|
2017-11-02 06:55:25 +00:00
|
|
|
{
|
|
|
|
if (m_initialized)
|
|
|
|
DeInitialize();
|
|
|
|
|
2017-11-16 09:53:22 +00:00
|
|
|
m_options = options;
|
2017-11-03 11:20:48 +00:00
|
|
|
HRESULT status;
|
|
|
|
|
|
|
|
status = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void **)(&m_dxgiFactory));
|
|
|
|
if (FAILED(status))
|
2017-11-02 11:37:19 +00:00
|
|
|
{
|
2017-11-03 11:20:48 +00:00
|
|
|
DEBUG_ERROR("Failed to create DXGIFactory: %08x", status);
|
2017-11-02 11:37:19 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-11-03 11:20:48 +00:00
|
|
|
bool done = false;
|
|
|
|
CComPtr<IDXGIAdapter1> adapter;
|
|
|
|
for (int i = 0; m_dxgiFactory->EnumAdapters1(i, &adapter) != DXGI_ERROR_NOT_FOUND; i++)
|
|
|
|
{
|
|
|
|
CComPtr<IDXGIOutput> output;
|
|
|
|
for (int i = 0; adapter->EnumOutputs(i, &output) != DXGI_ERROR_NOT_FOUND; i++)
|
|
|
|
{
|
|
|
|
DXGI_OUTPUT_DESC outputDesc;
|
|
|
|
output->GetDesc(&outputDesc);
|
|
|
|
if (!outputDesc.AttachedToDesktop)
|
|
|
|
{
|
|
|
|
output.Release();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_output = output;
|
|
|
|
if (!m_output)
|
|
|
|
{
|
|
|
|
output.Release();
|
|
|
|
adapter.Release();
|
|
|
|
DEBUG_ERROR("Failed to get IDXGIOutput1");
|
|
|
|
DeInitialize();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_width = outputDesc.DesktopCoordinates.right - outputDesc.DesktopCoordinates.left;
|
|
|
|
m_height = outputDesc.DesktopCoordinates.bottom - outputDesc.DesktopCoordinates.top;
|
|
|
|
|
|
|
|
output.Release();
|
|
|
|
done = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (done)
|
|
|
|
break;
|
|
|
|
|
|
|
|
adapter.Release();
|
|
|
|
}
|
2017-11-02 11:37:19 +00:00
|
|
|
|
2017-11-03 11:20:48 +00:00
|
|
|
if (!done)
|
|
|
|
{
|
|
|
|
DEBUG_ERROR("Failed to locate a valid output device");
|
|
|
|
DeInitialize();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const D3D_FEATURE_LEVEL featureLevels[] = {
|
|
|
|
D3D_FEATURE_LEVEL_10_1,
|
|
|
|
D3D_FEATURE_LEVEL_10_0,
|
|
|
|
D3D_FEATURE_LEVEL_9_3,
|
|
|
|
D3D_FEATURE_LEVEL_9_2,
|
|
|
|
D3D_FEATURE_LEVEL_9_1
|
|
|
|
};
|
|
|
|
|
2017-11-26 05:10:59 +00:00
|
|
|
#if DEBUG
|
|
|
|
#define CREATE_FLAGS (D3D11_CREATE_DEVICE_DEBUG)
|
|
|
|
#else
|
|
|
|
#define CREATE_FLAGS (0)
|
|
|
|
#endif
|
|
|
|
|
2017-11-03 11:20:48 +00:00
|
|
|
status = D3D11CreateDevice(
|
|
|
|
adapter,
|
|
|
|
D3D_DRIVER_TYPE_UNKNOWN,
|
|
|
|
NULL,
|
2017-11-26 05:10:59 +00:00
|
|
|
CREATE_FLAGS,
|
2017-11-03 11:20:48 +00:00
|
|
|
featureLevels, ARRAYSIZE(featureLevels),
|
|
|
|
D3D11_SDK_VERSION,
|
|
|
|
&m_device,
|
|
|
|
&m_featureLevel,
|
|
|
|
&m_deviceContext
|
|
|
|
);
|
2017-11-26 05:10:59 +00:00
|
|
|
|
|
|
|
#undef CREATE_FLAGS
|
|
|
|
|
2017-11-03 11:20:48 +00:00
|
|
|
adapter.Release();
|
|
|
|
|
2017-11-03 17:02:55 +00:00
|
|
|
if (FAILED(status))
|
2017-11-03 11:20:48 +00:00
|
|
|
{
|
|
|
|
DEBUG_ERROR("Failed to create D3D11 device");
|
|
|
|
DeInitialize();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-11-03 17:00:00 +00:00
|
|
|
// we try this twice just incase we still get an error
|
|
|
|
// on re-initialization
|
|
|
|
for(int i = 0; i < 2; ++i)
|
2017-11-03 11:20:48 +00:00
|
|
|
{
|
2017-11-03 17:00:00 +00:00
|
|
|
status = m_output->DuplicateOutput(m_device, &m_dup);
|
|
|
|
if (SUCCEEDED(status))
|
|
|
|
break;
|
|
|
|
Sleep(200);
|
2017-11-03 11:20:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (FAILED(status))
|
|
|
|
{
|
|
|
|
DEBUG_ERROR("DuplicateOutput Failed: %08x", status);
|
|
|
|
DeInitialize();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
D3D11_TEXTURE2D_DESC texDesc;
|
|
|
|
ZeroMemory(&texDesc, sizeof(texDesc));
|
|
|
|
texDesc.Width = m_width;
|
|
|
|
texDesc.Height = m_height;
|
|
|
|
texDesc.MipLevels = 1;
|
|
|
|
texDesc.ArraySize = 1;
|
|
|
|
texDesc.SampleDesc.Count = 1;
|
|
|
|
texDesc.SampleDesc.Quality = 0;
|
|
|
|
texDesc.Usage = D3D11_USAGE_STAGING;
|
|
|
|
texDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
|
|
|
texDesc.BindFlags = 0;
|
|
|
|
texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
|
|
|
texDesc.MiscFlags = 0;
|
|
|
|
|
|
|
|
status = m_device->CreateTexture2D(&texDesc, NULL, &m_texture);
|
|
|
|
if (FAILED(status))
|
|
|
|
{
|
|
|
|
DEBUG_ERROR("Failed to create texture: %08x", status);
|
|
|
|
DeInitialize();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-11-02 06:55:25 +00:00
|
|
|
m_initialized = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DXGI::DeInitialize()
|
|
|
|
{
|
2017-11-03 13:14:30 +00:00
|
|
|
if (m_pointer)
|
|
|
|
{
|
|
|
|
delete[] m_pointer;
|
|
|
|
m_pointer = NULL;
|
2017-11-03 17:00:00 +00:00
|
|
|
m_pointerBufSize = 0;
|
2017-11-03 13:14:30 +00:00
|
|
|
}
|
|
|
|
|
2017-11-03 11:20:48 +00:00
|
|
|
if (m_texture)
|
|
|
|
m_texture.Release();
|
|
|
|
|
|
|
|
if (m_dup)
|
|
|
|
m_dup.Release();
|
|
|
|
|
|
|
|
if (m_output)
|
|
|
|
m_output.Release();
|
|
|
|
|
|
|
|
if (m_deviceContext)
|
|
|
|
m_deviceContext.Release();
|
|
|
|
|
|
|
|
if (m_device)
|
|
|
|
m_device.Release();
|
|
|
|
|
|
|
|
if (m_dxgiFactory)
|
|
|
|
m_dxgiFactory.Release();
|
2017-11-02 11:37:19 +00:00
|
|
|
|
2017-11-02 06:55:25 +00:00
|
|
|
m_initialized = false;
|
|
|
|
}
|
|
|
|
|
2017-11-03 17:00:00 +00:00
|
|
|
bool DXGI::ReInitialize()
|
|
|
|
{
|
|
|
|
DeInitialize();
|
|
|
|
|
|
|
|
/*
|
|
|
|
DXGI needs some time when mode switches occur, failing to do so causes
|
|
|
|
failure to start and exceptions internal to DXGI
|
|
|
|
*/
|
|
|
|
Sleep(200);
|
|
|
|
|
2017-11-16 09:53:22 +00:00
|
|
|
return Initialize(m_options);
|
2017-11-03 17:00:00 +00:00
|
|
|
}
|
|
|
|
|
2017-11-02 06:55:25 +00:00
|
|
|
FrameType DXGI::GetFrameType()
|
|
|
|
{
|
|
|
|
if (!m_initialized)
|
|
|
|
return FRAME_TYPE_INVALID;
|
|
|
|
|
2017-11-17 04:24:01 +00:00
|
|
|
return FRAME_TYPE_ARGB;
|
2017-11-02 06:55:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t DXGI::GetMaxFrameSize()
|
|
|
|
{
|
2017-11-03 11:20:48 +00:00
|
|
|
if (!m_initialized)
|
2017-11-02 11:37:19 +00:00
|
|
|
return 0;
|
|
|
|
|
2017-11-17 04:24:01 +00:00
|
|
|
return (m_width * m_height * 4);
|
2017-11-02 06:55:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool DXGI::GrabFrame(FrameInfo & frame)
|
|
|
|
{
|
2017-11-03 17:00:00 +00:00
|
|
|
if (!m_initialized)
|
|
|
|
return false;
|
|
|
|
|
2017-11-03 11:20:48 +00:00
|
|
|
DXGI_OUTDUPL_FRAME_INFO frameInfo;
|
|
|
|
CComPtr<IDXGIResource> res;
|
2017-11-02 11:37:19 +00:00
|
|
|
|
2017-11-03 11:20:48 +00:00
|
|
|
HRESULT status;
|
2017-11-02 11:37:19 +00:00
|
|
|
for(int i = 0; i < 2; ++i)
|
|
|
|
{
|
2017-11-03 11:20:48 +00:00
|
|
|
status = m_dup->AcquireNextFrame(INFINITE, &frameInfo, &res);
|
|
|
|
if (SUCCEEDED(status))
|
2017-11-02 11:37:19 +00:00
|
|
|
break;
|
2017-11-03 17:00:00 +00:00
|
|
|
|
|
|
|
switch (status)
|
|
|
|
{
|
|
|
|
case DXGI_ERROR_ACCESS_LOST: // desktop switch, mode change or switch DWM on or off
|
|
|
|
case WAIT_ABANDONED: // this can happen also during desktop switches, not documented by MS though
|
2017-11-03 11:20:48 +00:00
|
|
|
{
|
2017-11-03 19:11:24 +00:00
|
|
|
// see if we can open the desktop, if not the secure desktop
|
|
|
|
// is active so just wait for it instead of aborting out
|
|
|
|
HDESK desktop = NULL;
|
|
|
|
while(!desktop)
|
|
|
|
{
|
|
|
|
desktop = OpenInputDesktop(0, TRUE, GENERIC_READ);
|
|
|
|
if (desktop)
|
|
|
|
break;
|
|
|
|
Sleep(100);
|
|
|
|
}
|
|
|
|
CloseDesktop(desktop);
|
|
|
|
|
2017-11-03 17:00:00 +00:00
|
|
|
if (!ReInitialize())
|
|
|
|
{
|
2017-11-03 19:11:24 +00:00
|
|
|
DEBUG_ERROR("Failed to ReInitialize after lost access to desktop");
|
2017-11-03 17:00:00 +00:00
|
|
|
return false;
|
|
|
|
}
|
2017-11-04 00:15:06 +00:00
|
|
|
continue;
|
2017-11-03 11:20:48 +00:00
|
|
|
}
|
|
|
|
|
2017-11-03 17:00:00 +00:00
|
|
|
default:
|
|
|
|
// unknown failure
|
|
|
|
DEBUG_INFO("AcquireNextFrame failed: %08x", status);
|
|
|
|
return false;
|
|
|
|
}
|
2017-11-03 11:20:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// retry count exceeded
|
|
|
|
if (FAILED(status))
|
|
|
|
{
|
2017-11-03 17:00:00 +00:00
|
|
|
m_dup->ReleaseFrame();
|
2017-11-03 11:20:48 +00:00
|
|
|
DEBUG_ERROR("Failed to acquire next frame");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
CComQIPtr<ID3D11Texture2D> src = res;
|
|
|
|
if (!src)
|
|
|
|
{
|
2017-11-03 17:00:00 +00:00
|
|
|
m_dup->ReleaseFrame();
|
2017-11-03 11:20:48 +00:00
|
|
|
DEBUG_ERROR("Failed to get src ID3D11Texture2D");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
D3D11_TEXTURE2D_DESC desc;
|
|
|
|
src->GetDesc(&desc);
|
|
|
|
|
|
|
|
m_deviceContext->CopyResource(m_texture, src);
|
2017-11-03 13:14:30 +00:00
|
|
|
|
|
|
|
// if the pointer shape has changed
|
|
|
|
if (frameInfo.PointerShapeBufferSize > 0)
|
|
|
|
{
|
|
|
|
if (m_pointerBufSize < frameInfo.PointerShapeBufferSize)
|
|
|
|
{
|
|
|
|
if (m_pointer)
|
|
|
|
delete[] m_pointer;
|
|
|
|
m_pointer = new BYTE[frameInfo.PointerShapeBufferSize];
|
|
|
|
m_pointerBufSize = frameInfo.PointerShapeBufferSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = m_dup->GetFramePointerShape(m_pointerBufSize, m_pointer, &m_pointerSize, &m_shapeInfo);
|
|
|
|
if (!SUCCEEDED(status))
|
|
|
|
{
|
2017-11-03 17:00:00 +00:00
|
|
|
m_dup->ReleaseFrame();
|
2017-11-03 13:14:30 +00:00
|
|
|
DEBUG_ERROR("Failed to get the new pointer shape: %08x", status);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-03 11:20:48 +00:00
|
|
|
m_dup->ReleaseFrame();
|
|
|
|
res.Release();
|
|
|
|
src.Release();
|
|
|
|
|
|
|
|
CComQIPtr<IDXGISurface1> surface = m_texture;
|
|
|
|
if (!surface)
|
|
|
|
{
|
|
|
|
DEBUG_ERROR("Failed to get IDXGISurface1");
|
|
|
|
return false;
|
2017-11-02 11:37:19 +00:00
|
|
|
}
|
|
|
|
|
2017-11-03 11:20:48 +00:00
|
|
|
DXGI_MAPPED_RECT rect;
|
|
|
|
status = surface->Map(&rect, DXGI_MAP_READ);
|
|
|
|
if (FAILED(status))
|
|
|
|
{
|
|
|
|
DEBUG_ERROR("Failed to map surface: %08x", status);
|
2017-11-02 11:37:19 +00:00
|
|
|
return false;
|
2017-11-03 11:20:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
m_width = desc.Width;
|
|
|
|
m_height = desc.Height;
|
2017-11-17 04:24:01 +00:00
|
|
|
const int pitch = m_width * 4;
|
2017-11-02 11:37:19 +00:00
|
|
|
|
2017-11-03 11:20:48 +00:00
|
|
|
frame.width = desc.Width;
|
|
|
|
frame.height = desc.Height;
|
2017-11-16 11:43:29 +00:00
|
|
|
frame.stride = desc.Width;
|
|
|
|
frame.outSize = min(frame.bufferSize, m_height * pitch);
|
2017-11-03 13:14:30 +00:00
|
|
|
|
|
|
|
// if we have a mouse update
|
|
|
|
if (frameInfo.LastMouseUpdateTime.QuadPart)
|
|
|
|
{
|
|
|
|
m_pointerVisible = frameInfo.PointerPosition.Visible;
|
|
|
|
m_pointerPos = frameInfo.PointerPosition.Position;
|
2017-12-05 09:39:54 +00:00
|
|
|
|
|
|
|
frame.hasMousePos = true;
|
|
|
|
frame.mouseX = m_pointerPos.x;
|
|
|
|
frame.mouseY = m_pointerPos.y;
|
2017-11-03 13:14:30 +00:00
|
|
|
}
|
|
|
|
|
2017-12-05 09:39:54 +00:00
|
|
|
memcpySSE(frame.buffer, rect.pBits, frame.outSize);
|
|
|
|
status = surface->Unmap();
|
|
|
|
|
2017-11-03 13:14:30 +00:00
|
|
|
// if the pointer is to be drawn
|
|
|
|
if (m_pointerVisible)
|
|
|
|
{
|
2017-12-05 09:39:54 +00:00
|
|
|
enum CursorType type;
|
2017-11-03 13:14:30 +00:00
|
|
|
switch (m_shapeInfo.Type)
|
|
|
|
{
|
2017-12-05 09:39:54 +00:00
|
|
|
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR : type = CURSOR_TYPE_COLOR ; break;
|
|
|
|
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR: type = CURSOR_TYPE_MASKED_COLOR; break;
|
|
|
|
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME : type = CURSOR_TYPE_MONOCHROME ; break;
|
|
|
|
default:
|
|
|
|
DEBUG_ERROR("Invalid cursor type");
|
|
|
|
return false;
|
2017-11-03 13:14:30 +00:00
|
|
|
}
|
|
|
|
|
2017-12-05 09:39:54 +00:00
|
|
|
POINT cursorPos;
|
|
|
|
POINT cursorRect;
|
|
|
|
cursorPos.x = m_pointerPos.x - m_shapeInfo.HotSpot.x;
|
|
|
|
cursorPos.y = m_pointerPos.y - m_shapeInfo.HotSpot.y;
|
|
|
|
cursorRect.x = m_shapeInfo.Width;
|
|
|
|
cursorRect.y = m_shapeInfo.Height;
|
|
|
|
|
|
|
|
Util::DrawCursor(
|
|
|
|
type,
|
|
|
|
m_pointer,
|
|
|
|
cursorRect,
|
|
|
|
m_shapeInfo.Pitch,
|
|
|
|
cursorPos,
|
|
|
|
frame
|
|
|
|
);
|
|
|
|
}
|
2017-11-16 11:43:29 +00:00
|
|
|
|
2017-11-03 11:20:48 +00:00
|
|
|
if (FAILED(status))
|
|
|
|
{
|
|
|
|
DEBUG_ERROR("Failed to unmap surface: %08x", status);
|
|
|
|
return false;
|
|
|
|
}
|
2017-11-02 11:37:19 +00:00
|
|
|
|
|
|
|
return true;
|
2017-11-02 06:55:25 +00:00
|
|
|
}
|