2017-10-31 16:53:06 +00:00
|
|
|
/*
|
|
|
|
KVMGFX Client - A KVM Client for VGA Passthrough
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
|
2017-10-31 12:22:55 +00:00
|
|
|
#include "Service.h"
|
|
|
|
#include "IVSHMEM.h"
|
2017-10-31 12:21:05 +00:00
|
|
|
|
2017-10-31 13:51:53 +00:00
|
|
|
#include "common\debug.h"
|
|
|
|
#include "common\KVMGFXHeader.h"
|
2017-10-31 12:21:05 +00:00
|
|
|
|
|
|
|
#include "CaptureFactory.h"
|
|
|
|
|
|
|
|
Service * Service::m_instance = NULL;
|
|
|
|
|
|
|
|
Service::Service() :
|
|
|
|
m_initialized(false),
|
|
|
|
m_readyEvent(INVALID_HANDLE_VALUE),
|
|
|
|
m_capture(NULL),
|
2017-11-15 06:28:17 +00:00
|
|
|
m_memory(NULL),
|
|
|
|
m_frameIndex(0)
|
2017-10-31 12:21:05 +00:00
|
|
|
{
|
|
|
|
m_ivshmem = IVSHMEM::Get();
|
|
|
|
}
|
|
|
|
|
|
|
|
Service::~Service()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Service::Initialize()
|
|
|
|
{
|
|
|
|
if (m_initialized)
|
|
|
|
DeInitialize();
|
|
|
|
|
2017-10-31 13:51:53 +00:00
|
|
|
m_capture = CaptureFactory::GetCaptureDevice();
|
2017-11-02 06:55:25 +00:00
|
|
|
if (!m_capture)
|
2017-10-31 13:51:53 +00:00
|
|
|
{
|
|
|
|
DEBUG_ERROR("Failed to initialize capture interface");
|
|
|
|
DeInitialize();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-10-31 12:21:05 +00:00
|
|
|
if (!m_ivshmem->Initialize())
|
|
|
|
{
|
|
|
|
DEBUG_ERROR("IVSHMEM failed to initalize");
|
|
|
|
DeInitialize();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_ivshmem->GetSize() < sizeof(KVMGFXHeader))
|
|
|
|
{
|
|
|
|
DEBUG_ERROR("Shared memory is not large enough for the KVMGFXHeader");
|
|
|
|
DeInitialize();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-11-15 06:28:17 +00:00
|
|
|
m_memory = static_cast<uint8_t*>(m_ivshmem->GetMemory());
|
2017-10-31 12:21:05 +00:00
|
|
|
if (!m_memory)
|
|
|
|
{
|
|
|
|
DEBUG_ERROR("Failed to get IVSHMEM memory");
|
|
|
|
DeInitialize();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_readyEvent = m_ivshmem->CreateVectorEvent(0);
|
|
|
|
if (m_readyEvent == INVALID_HANDLE_VALUE)
|
|
|
|
{
|
|
|
|
DEBUG_ERROR("Failed to get event for vector 0");
|
|
|
|
DeInitialize();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-11-15 06:28:17 +00:00
|
|
|
KVMGFXHeader * header = reinterpret_cast<KVMGFXHeader*>(m_memory);
|
2017-10-31 14:46:47 +00:00
|
|
|
|
|
|
|
// we save this as it might actually be valid
|
|
|
|
UINT16 hostID = header->hostID;
|
|
|
|
|
2017-10-31 12:21:05 +00:00
|
|
|
ZeroMemory(header, sizeof(KVMGFXHeader));
|
|
|
|
memcpy(header->magic, KVMGFX_HEADER_MAGIC, sizeof(KVMGFX_HEADER_MAGIC));
|
|
|
|
|
|
|
|
header->version = 2;
|
|
|
|
header->guestID = m_ivshmem->GetPeerID();
|
2017-10-31 14:46:47 +00:00
|
|
|
header->hostID = hostID;
|
2017-10-31 12:21:05 +00:00
|
|
|
|
|
|
|
m_initialized = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Service::DeInitialize()
|
|
|
|
{
|
2017-10-31 13:51:53 +00:00
|
|
|
if (m_readyEvent != INVALID_HANDLE_VALUE)
|
|
|
|
CloseHandle(m_readyEvent);
|
|
|
|
|
|
|
|
m_memory = NULL;
|
|
|
|
m_ivshmem->DeInitialize();
|
|
|
|
|
2017-10-31 12:21:05 +00:00
|
|
|
if (m_capture)
|
|
|
|
{
|
|
|
|
m_capture->DeInitialize();
|
|
|
|
m_capture = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_initialized = false;
|
|
|
|
}
|
|
|
|
|
2017-10-31 16:23:46 +00:00
|
|
|
bool Service::Process()
|
2017-10-31 12:21:05 +00:00
|
|
|
{
|
|
|
|
if (!m_initialized)
|
|
|
|
return false;
|
|
|
|
|
2017-11-15 06:28:17 +00:00
|
|
|
KVMGFXHeader * header = reinterpret_cast<KVMGFXHeader *>(m_memory);
|
|
|
|
const uint64_t dataOffset = sizeof(KVMGFXHeader) + m_frameIndex * m_capture->GetMaxFrameSize();
|
|
|
|
uint8_t * data = m_memory + dataOffset;
|
|
|
|
const size_t available = m_ivshmem->GetSize() - sizeof(KVMGFXHeader);
|
|
|
|
|
|
|
|
if (dataOffset + m_capture->GetMaxFrameSize() > available)
|
2017-10-31 12:21:05 +00:00
|
|
|
{
|
2017-11-15 06:28:17 +00:00
|
|
|
DEBUG_ERROR("Frame can exceed buffer size!");
|
2017-10-31 12:21:05 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// setup the header
|
|
|
|
header->frameType = m_capture->GetFrameType();
|
|
|
|
header->compType = m_capture->GetFrameCompression();
|
2017-10-31 14:46:47 +00:00
|
|
|
header->dataLen = 0;
|
|
|
|
|
|
|
|
FrameInfo frame;
|
|
|
|
frame.buffer = data;
|
|
|
|
frame.bufferSize = m_ivshmem->GetSize() - sizeof(KVMGFXHeader);
|
2017-10-31 12:21:05 +00:00
|
|
|
|
|
|
|
// capture a frame of data
|
2017-10-31 14:46:47 +00:00
|
|
|
if (!m_capture->GrabFrame(frame))
|
2017-10-31 12:21:05 +00:00
|
|
|
{
|
2017-10-31 14:46:47 +00:00
|
|
|
header->dataLen = 0;
|
2017-10-31 12:21:05 +00:00
|
|
|
DEBUG_ERROR("Capture failed");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-11-15 06:28:17 +00:00
|
|
|
// wait for the host to notify that is it is ready to proceed
|
|
|
|
ResetEvent(m_readyEvent);
|
|
|
|
bool eventDone = false;
|
|
|
|
while (!eventDone)
|
|
|
|
{
|
|
|
|
switch (WaitForSingleObject(m_readyEvent, 1000))
|
|
|
|
{
|
|
|
|
case WAIT_ABANDONED:
|
|
|
|
DEBUG_ERROR("Wait abandoned");
|
|
|
|
return false;
|
|
|
|
|
|
|
|
case WAIT_OBJECT_0:
|
|
|
|
eventDone = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
// if we timed out we just continue to ring until we get an answer or we are stopped
|
|
|
|
case WAIT_TIMEOUT:
|
|
|
|
continue;
|
|
|
|
|
|
|
|
case WAIT_FAILED:
|
|
|
|
DEBUG_ERROR("Wait failed");
|
|
|
|
return false;
|
|
|
|
|
|
|
|
default:
|
|
|
|
DEBUG_ERROR("Unknown error");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-31 14:46:47 +00:00
|
|
|
// copy the frame details into the header
|
|
|
|
header->width = frame.width;
|
|
|
|
header->height = frame.height;
|
|
|
|
header->stride = frame.stride;
|
2017-11-15 06:28:17 +00:00
|
|
|
header->dataPos = dataOffset;
|
2017-10-31 14:46:47 +00:00
|
|
|
header->dataLen = frame.outSize;
|
|
|
|
|
2017-10-31 12:21:05 +00:00
|
|
|
// tell the host where the cursor is
|
|
|
|
POINT cursorPos;
|
|
|
|
GetCursorPos(&cursorPos);
|
|
|
|
header->mouseX = cursorPos.x;
|
|
|
|
header->mouseY = cursorPos.y;
|
|
|
|
|
2017-11-15 06:28:17 +00:00
|
|
|
if (!m_ivshmem->RingDoorbell(header->hostID, 0))
|
2017-10-31 12:21:05 +00:00
|
|
|
{
|
2017-11-15 06:28:17 +00:00
|
|
|
DEBUG_ERROR("Failed to ring doorbell");
|
|
|
|
return false;
|
2017-10-31 12:21:05 +00:00
|
|
|
}
|
2017-10-31 16:53:06 +00:00
|
|
|
|
2017-11-15 06:28:17 +00:00
|
|
|
if (++m_frameIndex == 2)
|
|
|
|
m_frameIndex = 0;
|
|
|
|
|
2017-10-31 12:21:05 +00:00
|
|
|
return true;
|
2017-10-31 16:53:06 +00:00
|
|
|
}
|