diff --git a/common/debug.h b/common/debug.h index 4a42dc11..e1050f56 100644 --- a/common/debug.h +++ b/common/debug.h @@ -18,11 +18,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include -#ifdef DEBUG - #define DEBUG_PRINT(type, fmt, ...) do {fprintf(stderr, type " %20s:%-5u | %-24s | " fmt "\n", __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__);} while (0) -#else - #define DEBUG_PRINT(type, fmt, ...) do {} while(0) -#endif +#define DEBUG_PRINT(type, fmt, ...) do {fprintf(stderr, type " %20s:%-5u | %-24s | " fmt "\n", __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__);} while (0) #define DEBUG_INFO(fmt, ...) DEBUG_PRINT("[I]", fmt, ##__VA_ARGS__) #define DEBUG_WARN(fmt, ...) DEBUG_PRINT("[W]", fmt, ##__VA_ARGS__) diff --git a/host/Capture/DXGI.cpp b/host/Capture/DXGI.cpp index 864c812e..16476a88 100644 --- a/host/Capture/DXGI.cpp +++ b/host/Capture/DXGI.cpp @@ -22,6 +22,7 @@ using namespace Capture; #include "common\debug.h" DXGI::DXGI() : + m_options(NULL), m_initialized(false), m_dxgiFactory(NULL), m_device(NULL), @@ -37,11 +38,12 @@ DXGI::~DXGI() } -bool DXGI::Initialize() +bool DXGI::Initialize(CaptureOptions * options) { if (m_initialized) DeInitialize(); + m_options = options; HRESULT status; status = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void **)(&m_dxgiFactory)); @@ -208,7 +210,7 @@ bool DXGI::ReInitialize() */ Sleep(200); - return Initialize(); + return Initialize(m_options); } FrameType DXGI::GetFrameType() diff --git a/host/Capture/DXGI.h b/host/Capture/DXGI.h index b6549491..d44e64df 100644 --- a/host/Capture/DXGI.h +++ b/host/Capture/DXGI.h @@ -33,7 +33,10 @@ namespace Capture public: DXGI(); ~DXGI(); - bool Initialize(); + + const char * GetName() { return "DXGI"; } + + bool Initialize(CaptureOptions * options); void DeInitialize(); enum FrameType GetFrameType(); enum FrameComp GetFrameCompression(); @@ -41,6 +44,8 @@ namespace Capture bool GrabFrame(struct FrameInfo & frame); private: + CaptureOptions * m_options; + bool ReInitialize(); bool m_initialized; diff --git a/host/Capture/NvFBC.cpp b/host/Capture/NvFBC.cpp index b5296870..56d86aaa 100644 --- a/host/Capture/NvFBC.cpp +++ b/host/Capture/NvFBC.cpp @@ -31,6 +31,8 @@ using namespace Capture; #endif NvFBC::NvFBC() : + m_options(NULL), + m_optNoCrop(false), m_initialized(false), m_hDLL(NULL), m_nvFBC(NULL) @@ -41,11 +43,18 @@ NvFBC::~NvFBC() { } -bool NvFBC::Initialize() +bool NvFBC::Initialize(CaptureOptions * options) { if (m_initialized) DeInitialize(); + m_options = options; + m_optNoCrop = false; + for (CaptureOptions::const_iterator it = options->begin(); it != options->end(); ++it) + { + if (_strcmpi(*it, "nocrop") == 0) { m_optNoCrop = true; continue; } + } + std::string nvfbc = Util::GetSystemRoot() + "\\" + NVFBC_LIBRARY_NAME; m_hDLL = LoadLibraryA(nvfbc.c_str()); if (!m_hDLL) @@ -221,15 +230,30 @@ bool NvFBC::GrabFrame(struct FrameInfo & frame) NVFBCRESULT status = m_nvFBC->NvFBCToSysGrabFrame(&m_grabFrameParams); if (status == NVFBC_SUCCESS) { - const unsigned int realHeight = min(m_grabInfo.dwHeight, desktop.bottom - desktop.top ); - const unsigned int realWidth = min(m_grabInfo.dwWidth , desktop.right - desktop.left); - const unsigned int dataWidth = realWidth * 3; - const unsigned int dataOffset = - (((m_grabInfo.dwHeight - realHeight) >> 1) * m_grabInfo.dwBufferWidth + - ((m_grabInfo.dwWidth - realWidth ) >> 1)) * 3; + unsigned int dataWidth; + unsigned int dataOffset; + + if (m_optNoCrop) + { + dataWidth = m_grabInfo.dwWidth * 3; + dataOffset = 0; + + frame.width = m_grabInfo.dwWidth; + frame.height = m_grabInfo.dwHeight; + } + else + { + const unsigned int realHeight = min(m_grabInfo.dwHeight, (unsigned int)(desktop.bottom - desktop.top)); + const unsigned int realWidth = min(m_grabInfo.dwWidth , (unsigned int)(desktop.right - desktop.left)); + dataWidth = realWidth * 3; + dataOffset = + (((m_grabInfo.dwHeight - realHeight) >> 1) * m_grabInfo.dwBufferWidth + + ((m_grabInfo.dwWidth - realWidth ) >> 1)) * 3; + + frame.width = realWidth; + frame.height = realHeight; + } - frame.width = realWidth; - frame.height = realHeight; frame.stride = frame.width; frame.outSize = frame.width * frame.height * 3; @@ -251,7 +275,7 @@ bool NvFBC::GrabFrame(struct FrameInfo & frame) { DEBUG_WARN("Session was invalidated, attempting to restart"); DeInitialize(); - if (!Initialize()) + if (!Initialize(m_options)) { DEBUG_ERROR("Failed to re-iniaialize"); return false; diff --git a/host/Capture/NvFBC.h b/host/Capture/NvFBC.h index 6b554e92..2425533a 100644 --- a/host/Capture/NvFBC.h +++ b/host/Capture/NvFBC.h @@ -33,7 +33,9 @@ namespace Capture NvFBC(); ~NvFBC(); - bool Initialize(); + const char * GetName() { return "NvFBC"; } + + bool Initialize(CaptureOptions * options); void DeInitialize(); enum FrameType GetFrameType(); enum FrameComp GetFrameCompression(); @@ -41,6 +43,9 @@ namespace Capture bool GrabFrame(struct FrameInfo & frame); private: + CaptureOptions * m_options; + bool m_optNoCrop; + bool m_initialized; HMODULE m_hDLL; diff --git a/host/CaptureFactory.h b/host/CaptureFactory.h index cd0286ff..91fe5319 100644 --- a/host/CaptureFactory.h +++ b/host/CaptureFactory.h @@ -20,6 +20,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA #define W32_LEAN_AND_MEAN #include +#include #include "common\debug.h" #include "ICapture.h" @@ -29,29 +30,61 @@ Place, Suite 330, Boston, MA 02111-1307 USA class CaptureFactory { public: - static ICapture * GetCaptureDevice() + typedef std::vector DeviceList; + + static DeviceList & GetDevices() { - ICapture *dev; + static DeviceList devices; + if (!devices.empty()) + return devices; - dev = new Capture::NvFBC(); - if (dev->Initialize()) - { - DEBUG_INFO("Using NvFBC"); - return dev; - } - dev->DeInitialize(); - delete dev; - - dev = new Capture::DXGI(); - if (dev->Initialize()) - { - DEBUG_INFO("Using DXGI"); - return dev; - } - dev->DeInitialize(); - delete dev; + devices.push_back(new Capture::NvFBC()); + devices.push_back(new Capture::DXGI ()); - DEBUG_ERROR("Failed to initialize a compatible capture device"); + return devices; + } + + static ICapture * GetDevice(const char * name, CaptureOptions * options) + { + DeviceList devices = GetDevices(); + for (DeviceList::const_iterator it = devices.begin(); it != devices.end(); ++it) + { + ICapture * device = *it; + if (_strcmpi(name, device->GetName()) != 0) + continue; + + if (device->Initialize(options)) + { + DEBUG_INFO("Using %s", device->GetName()); + return device; + } + + device->DeInitialize(); + DEBUG_ERROR("Failed to initialize %s", device->GetName()); + return NULL; + } + + DEBUG_ERROR("No such device: %s", name); + return NULL; + } + + static ICapture * DetectDevice(CaptureOptions * options) + { + DeviceList devices = GetDevices(); + for (DeviceList::const_iterator it = devices.begin(); it != devices.end(); ++it) + { + ICapture * device = *it; + + DEBUG_INFO("Trying %s", device->GetName()); + if (device->Initialize(options)) + { + DEBUG_INFO("Using %s", device->GetName()); + return device; + } + device->DeInitialize(); + } + + DEBUG_ERROR("Failed to initialize a capture device"); return NULL; } }; \ No newline at end of file diff --git a/host/ICapture.h b/host/ICapture.h index e2633bf4..603a19dc 100644 --- a/host/ICapture.h +++ b/host/ICapture.h @@ -19,6 +19,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA #pragma once #include "common/KVMGFXHeader.h" +#include struct FrameInfo { @@ -30,10 +31,14 @@ struct FrameInfo size_t outSize; }; +typedef std::vector CaptureOptions; + __interface ICapture { public: - bool Initialize(); + const char * GetName(); + + bool Initialize(CaptureOptions * options); void DeInitialize(); enum FrameType GetFrameType(); enum FrameComp GetFrameCompression(); diff --git a/host/Service.cpp b/host/Service.cpp index 575b84bc..a56225db 100644 --- a/host/Service.cpp +++ b/host/Service.cpp @@ -40,19 +40,12 @@ Service::~Service() { } -bool Service::Initialize() +bool Service::Initialize(ICapture * captureDevice) { if (m_initialized) DeInitialize(); - m_capture = CaptureFactory::GetCaptureDevice(); - if (!m_capture) - { - DEBUG_ERROR("Failed to initialize capture interface"); - DeInitialize(); - return false; - } - + m_capture = captureDevice; if (!m_ivshmem->Initialize()) { DEBUG_ERROR("IVSHMEM failed to initalize"); diff --git a/host/Service.h b/host/Service.h index 8f7644e9..5be61b66 100644 --- a/host/Service.h +++ b/host/Service.h @@ -35,7 +35,7 @@ public: return m_instance; } - bool Initialize(); + bool Initialize(ICapture * captureDevice); void DeInitialize(); bool Process(); diff --git a/host/kvm-ivshmem-host.vcxproj b/host/kvm-ivshmem-host.vcxproj index 21b70dce..1c803b4a 100644 --- a/host/kvm-ivshmem-host.vcxproj +++ b/host/kvm-ivshmem-host.vcxproj @@ -160,6 +160,7 @@ + diff --git a/host/kvm-ivshmem-host.vcxproj.filters b/host/kvm-ivshmem-host.vcxproj.filters index 7574fa53..9e43533f 100644 --- a/host/kvm-ivshmem-host.vcxproj.filters +++ b/host/kvm-ivshmem-host.vcxproj.filters @@ -39,6 +39,9 @@ Source Files\Capture + + Source Files + diff --git a/host/main.cpp b/host/main.cpp index ebd52e23..b5d75427 100644 --- a/host/main.cpp +++ b/host/main.cpp @@ -17,70 +17,244 @@ Place, Suite 330, Boston, MA 02111-1307 USA */ #include -#include "common\debug.h" +#include +#include "common\debug.h" +#include "vendor\getopt\getopt.h" + +#include "CaptureFactory.h" #include "Service.h" -#ifdef DEBUG #include #include #include -#endif + +int parseArgs(struct StartupArgs & args); +int run(struct StartupArgs & args); + +void doHelp(); +void doLicense(); + +bool consoleActive = false; +void setupConsole(); + +struct StartupArgs +{ + bool foreground; + const char * captureDevice; + CaptureOptions captureOptions; +}; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdParam, int iCmdShow) { -#ifdef DEBUG + struct StartupArgs args; + ZeroMemory(&args, sizeof(struct StartupArgs)); + int ret = parseArgs(args); + if (ret == 0) { - HANDLE _handle; - int _conout; - FILE * fp; - - AllocConsole(); - - CONSOLE_SCREEN_BUFFER_INFO conInfo; - GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &conInfo); - conInfo.dwSize.Y = 500; - SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), conInfo.dwSize); - - _handle = GetStdHandle(STD_INPUT_HANDLE); - _conout = _open_osfhandle((intptr_t)_handle, _O_TEXT); - fp = _fdopen(_conout, "r"); - freopen_s(&fp, "CONIN$", "r", stdin); - - _handle = GetStdHandle(STD_OUTPUT_HANDLE); - _conout = _open_osfhandle((intptr_t)_handle, _O_TEXT); - fp = _fdopen(_conout, "w"); - freopen_s(&fp, "CONOUT$", "w", stdout); - - _handle = GetStdHandle(STD_ERROR_HANDLE); - _conout = _open_osfhandle((intptr_t)_handle, _O_TEXT); - fp = _fdopen(_conout, "w"); - freopen_s(&fp, "CONOUT$", "w", stderr); - - std::ios::sync_with_stdio(); - std::wcout.clear(); - std::cout.clear(); - std::wcerr.clear(); - std::cerr.clear(); - std::wcin.clear(); - std::cin.clear(); + ret = run(args); + if (ret != 0) + { + if (!args.foreground) + { + setupConsole(); + fprintf(stderr, "An error occurred, re-run in forground mode (-f) for more information\n"); + } + } } -#endif - Service *svc = svc->Get(); - if (!svc->Initialize()) + if (consoleActive) { - DEBUG_ERROR("Failed to initialize service"); + fprintf(stderr, "\nPress enter to terminate..."); + fflush(stderr); + getc(stdin); + } + + return ret; +} + +int run(struct StartupArgs & args) +{ + if (args.foreground) + setupConsole(); + + ICapture * captureDevice; + if (args.captureDevice == NULL) + captureDevice = CaptureFactory::DetectDevice(&args.captureOptions); + else + { + captureDevice = CaptureFactory::GetDevice(args.captureDevice, &args.captureOptions); + if (!captureDevice) + { + setupConsole(); + fprintf(stderr, "Failed to configure requested capture device\n"); + return -1; + } + } + + if (!captureDevice) + { + setupConsole(); + fprintf(stderr, "Unable to configure a capture device\n"); return -1; } + Service *svc = svc->Get(); + if (!svc->Initialize(captureDevice)) + return -1; + while (true) if (!svc->Process()) break; svc->DeInitialize(); -#ifdef DEBUG - getc(stdin); -#endif return 0; +} + +int parseArgs(struct StartupArgs & args) +{ + int c; + while((c = getopt(__argc, __argv, "hc:o:fl")) != -1) + { + switch (c) + { + case '?': + case 'h': + doHelp(); + return -1; + + case 'c': + { + const CaptureFactory::DeviceList deviceList = CaptureFactory::GetDevices(); + + bool found = false; + if (strcmp(optarg, "?") != 0) + { + for (CaptureFactory::DeviceList::const_iterator it = deviceList.begin(); it != deviceList.end(); ++it) + { + if (_strcmpi(optarg, (*it)->GetName()) == 0) + { + args.captureDevice = (*it)->GetName(); + found = true; + break; + } + } + + if (!found) + { + setupConsole(); + fprintf(stderr, "Invalid capture device: %s\n\n", optarg); + } + } + + if (!found) + { + setupConsole(); + fprintf(stderr, "Available Capture Devices:\n\n"); + for (CaptureFactory::DeviceList::const_iterator it = deviceList.begin(); it != deviceList.end(); ++it) + fprintf(stderr, " %s\n", (*it)->GetName()); + return -1; + } + break; + } + + case 'o': + { + args.captureOptions.push_back(optarg); + break; + } + + case 'f': + args.foreground = true; + break; + + case 'l': + doLicense(); + return -1; + } + } + + return 0; +} + +void doHelp() +{ + setupConsole(); + const char *app = PathFindFileNameA(__argv[0]); + fprintf(stderr, + "Usage: %s [OPTION]...\n" + "Example: %s -c ?\n" + "\n" + " -h Print out this help\n" + " -c Specify the capture device to use or ? to list availble (device is probed if not specified)\n" + " -o Option to pass to the capture device, may be specified multiple times for extra options\n" + " -f Foreground mode\n" + " -l License information\n", + app, + app + ); +} + +void doLicense() +{ + setupConsole(); + fprintf(stderr, + "KVMGFX Client - A KVM Client for VGA Passthrough\n" + "Copyright(C) 2017 Geoffrey McRae \n" + "\n" + "This program is free software; you can redistribute it and / or modify it under\n" + "the terms of the GNU General Public License as published by the Free Software\n" + "Foundation; either version 2 of the License, or (at your option) any later\n" + "version.\n" + "\n" + "This program is distributed in the hope that it will be useful, but WITHOUT ANY\n" + "WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A\n" + "PARTICULAR PURPOSE.See the GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License along with\n" + "this program; if not, write to the Free Software Foundation, Inc., 59 Temple\n" + "Place, Suite 330, Boston, MA 02111 - 1307 USA\n" + ); +} + +void setupConsole() +{ + if (consoleActive) + return; + + HANDLE _handle; + int _conout; + FILE * fp; + + AllocConsole(); + + CONSOLE_SCREEN_BUFFER_INFO conInfo; + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &conInfo); + conInfo.dwSize.Y = 500; + SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), conInfo.dwSize); + + _handle = GetStdHandle(STD_INPUT_HANDLE); + _conout = _open_osfhandle((intptr_t)_handle, _O_TEXT); + fp = _fdopen(_conout, "r"); + freopen_s(&fp, "CONIN$", "r", stdin); + + _handle = GetStdHandle(STD_OUTPUT_HANDLE); + _conout = _open_osfhandle((intptr_t)_handle, _O_TEXT); + fp = _fdopen(_conout, "w"); + freopen_s(&fp, "CONOUT$", "w", stdout); + + _handle = GetStdHandle(STD_ERROR_HANDLE); + _conout = _open_osfhandle((intptr_t)_handle, _O_TEXT); + fp = _fdopen(_conout, "w"); + freopen_s(&fp, "CONOUT$", "w", stderr); + + std::ios::sync_with_stdio(); + std::wcout.clear(); + std::cout.clear(); + std::wcerr.clear(); + std::cerr.clear(); + std::wcin.clear(); + std::cin.clear(); + + consoleActive = true; } \ No newline at end of file diff --git a/vendor/getopt/README.txt b/vendor/getopt/README.txt new file mode 100644 index 00000000..6c6a9d45 --- /dev/null +++ b/vendor/getopt/README.txt @@ -0,0 +1 @@ +This source comes from: https://gist.github.com/superwills/5815344 \ No newline at end of file diff --git a/vendor/getopt/getopt.c b/vendor/getopt/getopt.c new file mode 100644 index 00000000..6b3c735a --- /dev/null +++ b/vendor/getopt/getopt.c @@ -0,0 +1,106 @@ +#include "getopt.h" + +/* +* Copyright (c) 1987, 1993, 1994 +* The Regents of the University of California. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* 3. All advertising materials mentioning features or use of this software +* must display the following acknowledgement: +* This product includes software developed by the University of +* California, Berkeley and its contributors. +* 4. Neither the name of the University nor the names of its contributors +* may be used to endorse or promote products derived from this software +* without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +* SUCH DAMAGE. +*/ + +#include +#include + +int opterr = 1, /* if error message should be printed */ +optind = 1, /* index into parent argv vector */ +optopt, /* character checked for validity */ +optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ + +#define BADCH (int)'?' +#define BADARG (int)':' +#define EMSG "" + + /* + * getopt -- + * Parse argc/argv argument vector. + */ +int getopt(int nargc, char * const nargv[], const char *ostr) +{ + static char *place = EMSG; /* option letter processing */ + const char *oli; /* option letter list index */ + + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc || *(place = nargv[optind]) != '-') { + place = EMSG; + return (-1); + } + if (place[1] && *++place == '-') { /* found "--" */ + ++optind; + place = EMSG; + return (-1); + } + } /* option letter okay? */ + if ((optopt = (int)*place++) == (int)':' || + !(oli = strchr(ostr, optopt))) { + /* + * if the user didn't specify '-' as an option, + * assume it means -1. + */ + if (optopt == (int)'-') + return (-1); + if (!*place) + ++optind; + if (opterr && *ostr != ':') + (void)printf("illegal option -- %c\n", optopt); + return (BADCH); + } + if (*++oli != ':') { /* don't need argument */ + optarg = NULL; + if (!*place) + ++optind; + } + else { /* need an argument */ + if (*place) /* no white space */ + optarg = place; + else if (nargc <= ++optind) { /* no arg */ + place = EMSG; + if (*ostr == ':') + return (BADARG); + if (opterr) + (void)printf("option requires an argument -- %c\n", optopt); + return (BADCH); + } + else /* white space */ + optarg = nargv[optind]; + place = EMSG; + ++optind; + } + return (optopt); /* dump back option letter */ +} diff --git a/vendor/getopt/getopt.h b/vendor/getopt/getopt.h new file mode 100644 index 00000000..fd89c323 --- /dev/null +++ b/vendor/getopt/getopt.h @@ -0,0 +1,20 @@ +#ifndef GETOPT_H +#define GETOPT_H + +#ifdef __cplusplus +extern "C" { +#endif + + extern int opterr; /* if error message should be printed */ + extern int optind; /* index into parent argv vector */ + extern int optopt; /* character checked for validity */ + extern int optreset; /* reset getopt */ + extern char *optarg; /* argument associated with option */ + + int getopt(int nargc, char * const nargv[], const char *ostr); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file