mirror of
https://github.com/gnif/LookingGlass.git
synced 2025-11-17 15:38:45 +00:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ead48195db | ||
|
|
c0b67e7cb2 | ||
|
|
2ae9b479f6 | ||
|
|
3fa9f371b4 | ||
|
|
8ec4abc544 | ||
|
|
7280f305e0 | ||
|
|
7b64d35cd1 | ||
|
|
8c2709a3f4 | ||
|
|
d55f0bf841 | ||
|
|
dd0930d265 | ||
|
|
04f7800df4 | ||
|
|
d8a80a1cfc | ||
|
|
9d29b1195d | ||
|
|
2374b1a9fb | ||
|
|
163f612efa | ||
|
|
a7180a5609 | ||
|
|
81f4a7fade | ||
|
|
7986350cb8 | ||
|
|
e379f70784 |
6
README.md
Normal file
6
README.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# LookingGlass
|
||||
An extremely low latency KVMFR (KVM FrameRelay) implementation for guests with VGA PCI Passthrough.
|
||||
|
||||
* Project Website: https://looking-glass.hostfission.com
|
||||
* Support Forum: https://forum.level1techs.com/t/looking-glass-guides-help-and-support/122387
|
||||
* Windows Builds of the host application: https://looking-glass.hostfission.com/downloads
|
||||
@@ -27,6 +27,8 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#define IS_LG_RENDERER_VALID(x) \
|
||||
((x)->get_name && \
|
||||
(x)->initialize && \
|
||||
(x)->configure && \
|
||||
(x)->deconfigure && \
|
||||
(x)->deinitialize && \
|
||||
(x)->is_compatible && \
|
||||
(x)->on_resize && \
|
||||
@@ -36,11 +38,9 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
typedef struct LG_RendererParams
|
||||
{
|
||||
SDL_Window * window;
|
||||
TTF_Font * font;
|
||||
bool showFPS;
|
||||
int width;
|
||||
int height;
|
||||
TTF_Font * font;
|
||||
bool showFPS;
|
||||
bool resample;
|
||||
}
|
||||
LG_RendererParams;
|
||||
|
||||
@@ -72,19 +72,23 @@ typedef enum LG_RendererCursor
|
||||
LG_RendererCursor;
|
||||
|
||||
typedef const char * (* LG_RendererGetName )();
|
||||
typedef bool (* LG_RendererInitialize )(void ** opaque, const LG_RendererParams params, const LG_RendererFormat format);
|
||||
typedef bool (* LG_RendererInitialize )(void ** opaque, const LG_RendererParams params, Uint32 * sdlFlags);
|
||||
typedef bool (* LG_RendererConfigure )(void * opaque, SDL_Window *window, const LG_RendererFormat format);
|
||||
typedef void (* LG_RendererDeConfigure )(void * opaque);
|
||||
typedef void (* LG_RendererDeInitialize )(void * opaque);
|
||||
typedef bool (* LG_RendererIsCompatible )(void * opaque, const LG_RendererFormat format);
|
||||
typedef void (* LG_RendererOnResize )(void * opaque, const int width, const int height, const LG_RendererRect destRect);
|
||||
typedef bool (* LG_RendererOnMouseShape )(void * opaque, const LG_RendererCursor cursor, const int width, const int height, const int pitch, const uint8_t * data);
|
||||
typedef bool (* LG_RendererOnMouseEvent )(void * opaque, const bool visible , const int x, const int y);
|
||||
typedef bool (* LG_RendererOnFrameEvent )(void * opaque, const uint8_t * data, bool resample);
|
||||
typedef bool (* LG_RendererOnFrameEvent )(void * opaque, const uint8_t * data);
|
||||
typedef bool (* LG_RendererRender )(void * opaque);
|
||||
|
||||
typedef struct LG_Renderer
|
||||
{
|
||||
LG_RendererGetName get_name;
|
||||
LG_RendererInitialize initialize;
|
||||
LG_RendererConfigure configure;
|
||||
LG_RendererDeConfigure deconfigure;
|
||||
LG_RendererDeInitialize deinitialize;
|
||||
LG_RendererIsCompatible is_compatible;
|
||||
LG_RendererOnResize on_resize;
|
||||
|
||||
170
client/main.c
170
client/main.c
@@ -166,11 +166,7 @@ int renderThread(void * unused)
|
||||
// we must take a copy of the header, both to let the guest advance and to
|
||||
// prevent the contained arguments being abused to overflow buffers
|
||||
memcpy(&header, state.shm, sizeof(struct KVMFRHeader));
|
||||
if (!ivshmem_kick_irq(header.guestID, 0))
|
||||
{
|
||||
usleep(1000);
|
||||
continue;
|
||||
}
|
||||
__sync_or_and_fetch(&state.shm->flags, KVMFR_HEADER_FLAG_READY);
|
||||
|
||||
// check the header's magic and version are valid
|
||||
if (
|
||||
@@ -232,66 +228,17 @@ int renderThread(void * unused)
|
||||
break;
|
||||
}
|
||||
|
||||
// check if we have a compatible renderer
|
||||
if (!state.lgr || !state.lgr->is_compatible(state.lgrData, lgrFormat))
|
||||
// check if the renderer needs reconfiguration
|
||||
if (!state.lgr->is_compatible(state.lgrData, lgrFormat))
|
||||
{
|
||||
int width, height;
|
||||
SDL_GetWindowSize(state.window, &width, &height);
|
||||
|
||||
LG_RendererParams lgrParams;
|
||||
lgrParams.window = state.window;
|
||||
lgrParams.font = state.font;
|
||||
lgrParams.showFPS = params.showFPS;
|
||||
lgrParams.width = width;
|
||||
lgrParams.height = height;
|
||||
|
||||
DEBUG_INFO("Data Format: w=%u, h=%u, s=%u, p=%u, bpp=%u",
|
||||
lgrFormat.width, lgrFormat.height, lgrFormat.stride, lgrFormat.pitch, lgrFormat.bpp);
|
||||
|
||||
// first try to reinitialize any existing renderer
|
||||
if (state.lgr)
|
||||
state.lgr->deconfigure(state.lgrData);
|
||||
if (!state.lgr->configure(state.lgrData, state.window, lgrFormat))
|
||||
{
|
||||
state.lgr->deinitialize(state.lgrData);
|
||||
if (state.lgr->initialize(&state.lgrData, lgrParams, lgrFormat))
|
||||
{
|
||||
DEBUG_INFO("Reinitialized %s", state.lgr->get_name());
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_ERROR("Failed to reinitialize %s, trying other renderers", state.lgr->get_name());
|
||||
state.lgr->deinitialize(state.lgrData);
|
||||
state.lgr = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!state.lgr)
|
||||
{
|
||||
// probe for a a suitable renderer
|
||||
for(const LG_Renderer **r = &LG_Renderers[0]; *r; ++r)
|
||||
{
|
||||
if (!IS_LG_RENDERER_VALID(*r))
|
||||
{
|
||||
DEBUG_ERROR("FIXME: Renderer %d is invalid, skipping", (int)(r - &LG_Renderers[0]));
|
||||
continue;
|
||||
}
|
||||
|
||||
state.lgrData = NULL;
|
||||
if (!(*r)->initialize(&state.lgrData, lgrParams, lgrFormat))
|
||||
{
|
||||
(*r)->deinitialize(state.lgrData);
|
||||
continue;
|
||||
}
|
||||
|
||||
state.lgr = *r;
|
||||
DEBUG_INFO("Initialized %s", (*r)->get_name());
|
||||
break;
|
||||
}
|
||||
|
||||
if (!state.lgr)
|
||||
{
|
||||
DEBUG_INFO("Unable to find a suitable renderer");
|
||||
return -1;
|
||||
}
|
||||
DEBUG_ERROR("Failed to reconfigure %s", state.lgr->get_name());
|
||||
break;
|
||||
}
|
||||
|
||||
state.srcSize.x = header.frame.width;
|
||||
@@ -302,7 +249,7 @@ int renderThread(void * unused)
|
||||
}
|
||||
|
||||
const uint8_t * data = (const uint8_t *)state.shm + header.frame.dataPos;
|
||||
if (!state.lgr->on_frame_event(state.lgrData, data, params.useMipmap))
|
||||
if (!state.lgr->on_frame_event(state.lgrData, data))
|
||||
{
|
||||
DEBUG_ERROR("Failed to render the frame");
|
||||
break;
|
||||
@@ -364,9 +311,6 @@ int renderThread(void * unused)
|
||||
state.lgr->render(state.lgrData);
|
||||
}
|
||||
|
||||
if (state.lgr)
|
||||
state.lgr->deinitialize(state.lgrData);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -434,10 +378,27 @@ int eventThread(void * arg)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (event.type == SDL_QUIT)
|
||||
switch(event.type)
|
||||
{
|
||||
case SDL_QUIT:
|
||||
state.running = false;
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT:
|
||||
{
|
||||
switch(event.window.event)
|
||||
{
|
||||
case SDL_WINDOWEVENT_ENTER:
|
||||
realignGuest = true;
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||
updatePositionInfo();
|
||||
realignGuest = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!params.useSpice)
|
||||
@@ -586,22 +547,6 @@ int eventThread(void * arg)
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT:
|
||||
{
|
||||
switch(event.window.event)
|
||||
{
|
||||
case SDL_WINDOWEVENT_ENTER:
|
||||
realignGuest = true;
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||
updatePositionInfo();
|
||||
realignGuest = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -664,6 +609,40 @@ int run()
|
||||
FcPatternDestroy(pat);
|
||||
}
|
||||
|
||||
LG_RendererParams lgrParams;
|
||||
lgrParams.font = state.font;
|
||||
lgrParams.resample = params.useMipmap;
|
||||
lgrParams.showFPS = params.showFPS;
|
||||
Uint32 sdlFlags;
|
||||
|
||||
// probe for a a suitable renderer
|
||||
for(const LG_Renderer **r = &LG_Renderers[0]; *r; ++r)
|
||||
{
|
||||
if (!IS_LG_RENDERER_VALID(*r))
|
||||
{
|
||||
DEBUG_ERROR("FIXME: Renderer %d is invalid, skipping", (int)(r - &LG_Renderers[0]));
|
||||
continue;
|
||||
}
|
||||
|
||||
state.lgrData = NULL;
|
||||
sdlFlags = 0;
|
||||
if (!(*r)->initialize(&state.lgrData, lgrParams, &sdlFlags))
|
||||
{
|
||||
(*r)->deinitialize(state.lgrData);
|
||||
continue;
|
||||
}
|
||||
|
||||
state.lgr = *r;
|
||||
DEBUG_INFO("Initialized %s", (*r)->get_name());
|
||||
break;
|
||||
}
|
||||
|
||||
if (!state.lgr)
|
||||
{
|
||||
DEBUG_INFO("Unable to find a suitable renderer");
|
||||
return -1;
|
||||
}
|
||||
|
||||
state.window = SDL_CreateWindow(
|
||||
"Looking Glass (Client)",
|
||||
params.center ? SDL_WINDOWPOS_CENTERED : params.x,
|
||||
@@ -671,10 +650,10 @@ int run()
|
||||
params.w,
|
||||
params.h,
|
||||
(
|
||||
SDL_WINDOW_SHOWN |
|
||||
SDL_WINDOW_OPENGL |
|
||||
SDL_WINDOW_SHOWN |
|
||||
(params.allowResize ? SDL_WINDOW_RESIZABLE : 0) |
|
||||
(params.borderless ? SDL_WINDOW_BORDERLESS : 0)
|
||||
(params.borderless ? SDL_WINDOW_BORDERLESS : 0) |
|
||||
sdlFlags
|
||||
)
|
||||
);
|
||||
|
||||
@@ -748,15 +727,6 @@ int run()
|
||||
state.shmSize = ivshmem_get_map_size();
|
||||
state.shm->hostID = ivshmem_get_id();
|
||||
|
||||
// flag the host that we are starting up this is important so that
|
||||
// the host wakes up if it is waiting on an interrupt, the host will
|
||||
// also send us the current mouse shape since we won't know it yet
|
||||
DEBUG_INFO("Waiting for host to signal it's ready...");
|
||||
__sync_or_and_fetch(&state.shm->flags, KVMFR_HEADER_FLAG_RESTART);
|
||||
while(state.running && (state.shm->flags & KVMFR_HEADER_FLAG_RESTART))
|
||||
usleep(1000);
|
||||
DEBUG_INFO("Host ready, starting session");
|
||||
|
||||
if (params.useSpice)
|
||||
{
|
||||
if (!spice_connect(params.spiceHost, params.spicePort, ""))
|
||||
@@ -786,9 +756,16 @@ int run()
|
||||
break;
|
||||
}
|
||||
|
||||
while(state.running)
|
||||
renderThread(NULL);
|
||||
// flag the host that we are starting up this is important so that
|
||||
// the host wakes up if it is waiting on an interrupt, the host will
|
||||
// also send us the current mouse shape since we won't know it yet
|
||||
DEBUG_INFO("Waiting for host to signal it's ready...");
|
||||
__sync_or_and_fetch(&state.shm->flags, KVMFR_HEADER_FLAG_RESTART);
|
||||
while(state.running && (state.shm->flags & KVMFR_HEADER_FLAG_RESTART))
|
||||
usleep(1000);
|
||||
DEBUG_INFO("Host ready, starting session");
|
||||
|
||||
renderThread(NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -806,6 +783,9 @@ int run()
|
||||
if (t_spice)
|
||||
SDL_WaitThread(t_spice, NULL);
|
||||
|
||||
if (state.lgr)
|
||||
state.lgr->deinitialize(state.lgrData);
|
||||
|
||||
if (state.window)
|
||||
SDL_DestroyWindow(state.window);
|
||||
|
||||
|
||||
@@ -1,9 +1,28 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
https://looking-glass.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 "lg-renderer.h"
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <SDL_ttf.h>
|
||||
#include <SDL2/SDL_ttf.h>
|
||||
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include <GL/gl.h>
|
||||
@@ -14,11 +33,12 @@
|
||||
#include "memcpySSE.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define VBO_BUFFERS 2
|
||||
#define BUFFER_COUNT 1
|
||||
|
||||
#define FPS_TEXTURE (VBO_BUFFERS )
|
||||
#define MOUSE_TEXTURE (VBO_BUFFERS+1)
|
||||
#define TEXTURE_COUNT (VBO_BUFFERS+2)
|
||||
#define FRAME_TEXTURE 0
|
||||
#define FPS_TEXTURE 1
|
||||
#define MOUSE_TEXTURE 2
|
||||
#define TEXTURE_COUNT 3
|
||||
|
||||
static PFNGLXGETVIDEOSYNCSGIPROC glXGetVideoSyncSGI = NULL;
|
||||
static PFNGLXWAITVIDEOSYNCSGIPROC glXWaitVideoSyncSGI = NULL;
|
||||
@@ -26,8 +46,11 @@ static PFNGLXWAITVIDEOSYNCSGIPROC glXWaitVideoSyncSGI = NULL;
|
||||
struct LGR_OpenGL
|
||||
{
|
||||
LG_RendererParams params;
|
||||
bool initialized;
|
||||
bool configured;
|
||||
SDL_GLContext glContext;
|
||||
bool doneInfo;
|
||||
|
||||
SDL_Point window;
|
||||
bool resizeWindow;
|
||||
bool frameUpdate;
|
||||
|
||||
@@ -38,13 +61,14 @@ struct LGR_OpenGL
|
||||
|
||||
uint64_t drawStart;
|
||||
bool hasBuffers;
|
||||
GLuint vboID[VBO_BUFFERS];
|
||||
uint8_t * texPixels[VBO_BUFFERS];
|
||||
GLuint vboID[1];
|
||||
uint8_t * texPixels[BUFFER_COUNT];
|
||||
int texIndex;
|
||||
int texList;
|
||||
int fpsList;
|
||||
int mouseList;
|
||||
LG_RendererRect destRect;
|
||||
bool mipmap;
|
||||
|
||||
bool hasTextures;
|
||||
GLuint textures[TEXTURE_COUNT];
|
||||
@@ -56,9 +80,9 @@ struct LGR_OpenGL
|
||||
uint64_t frameCount;
|
||||
SDL_Rect fpsRect;
|
||||
|
||||
bool mouseUpdate;
|
||||
uint64_t lastMouseDraw;
|
||||
LG_RendererCursor mouseType;
|
||||
bool mouseRepair;
|
||||
SDL_Rect mouseRepairPos;
|
||||
bool mouseVisible;
|
||||
SDL_Rect mousePos;
|
||||
};
|
||||
@@ -79,7 +103,7 @@ const char * lgr_opengl_get_name()
|
||||
return "OpenGL";
|
||||
}
|
||||
|
||||
bool lgr_opengl_initialize(void ** opaque, const LG_RendererParams params, const LG_RendererFormat format)
|
||||
bool lgr_opengl_initialize(void ** opaque, const LG_RendererParams params, Uint32 * sdlFlags)
|
||||
{
|
||||
// create our local storage
|
||||
*opaque = malloc(sizeof(struct LGR_OpenGL));
|
||||
@@ -89,26 +113,10 @@ bool lgr_opengl_initialize(void ** opaque, const LG_RendererParams params, const
|
||||
return false;
|
||||
}
|
||||
memset(*opaque, 0, sizeof(struct LGR_OpenGL));
|
||||
|
||||
struct LGR_OpenGL * this = (struct LGR_OpenGL *)*opaque;
|
||||
memcpy(&this->params, ¶ms, sizeof(LG_RendererParams));
|
||||
|
||||
this->glContext = SDL_GL_CreateContext(params.window);
|
||||
if (!this->glContext)
|
||||
{
|
||||
DEBUG_ERROR("Failed to create the OpenGL context");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SDL_GL_MakeCurrent(params.window, this->glContext) != 0)
|
||||
{
|
||||
DEBUG_ERROR("Failed to make the GL context current");
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_INFO("Vendor : %s", glGetString(GL_VENDOR ));
|
||||
DEBUG_INFO("Renderer: %s", glGetString(GL_RENDERER));
|
||||
DEBUG_INFO("Version : %s", glGetString(GL_VERSION ));
|
||||
|
||||
if (!glXGetVideoSyncSGI)
|
||||
{
|
||||
glXGetVideoSyncSGI = (PFNGLXGETVIDEOSYNCSGIPROC )glXGetProcAddress((const GLubyte *)"glXGetVideoSyncSGI" );
|
||||
@@ -122,6 +130,44 @@ bool lgr_opengl_initialize(void ** opaque, const LG_RendererParams params, const
|
||||
}
|
||||
}
|
||||
|
||||
*sdlFlags = SDL_WINDOW_OPENGL;
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool lgr_opengl_configure(void * opaque, SDL_Window *window, const LG_RendererFormat format)
|
||||
{
|
||||
struct LGR_OpenGL * this = (struct LGR_OpenGL *)opaque;
|
||||
if (!this)
|
||||
return false;
|
||||
|
||||
if (this->configured)
|
||||
{
|
||||
DEBUG_ERROR("Renderer already configured, call deconfigure first");
|
||||
return false;
|
||||
}
|
||||
|
||||
this->glContext = SDL_GL_CreateContext(window);
|
||||
if (!this->glContext)
|
||||
{
|
||||
DEBUG_ERROR("Failed to create the OpenGL context");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this->doneInfo)
|
||||
{
|
||||
DEBUG_INFO("Vendor : %s", glGetString(GL_VENDOR ));
|
||||
DEBUG_INFO("Renderer: %s", glGetString(GL_RENDERER));
|
||||
DEBUG_INFO("Version : %s", glGetString(GL_VERSION ));
|
||||
this->doneInfo = true;
|
||||
}
|
||||
|
||||
if (SDL_GL_MakeCurrent(window, this->glContext) != 0)
|
||||
{
|
||||
DEBUG_ERROR("Failed to make the GL context current");
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_GL_SetSwapInterval(0);
|
||||
|
||||
// check if the GPU supports GL_ARB_buffer_storage first
|
||||
@@ -155,40 +201,44 @@ bool lgr_opengl_initialize(void ** opaque, const LG_RendererParams params, const
|
||||
this->texSize = format.height * format.pitch;
|
||||
|
||||
// generate lists for drawing
|
||||
this->texList = glGenLists(2);
|
||||
this->texList = glGenLists(BUFFER_COUNT);
|
||||
this->fpsList = glGenLists(1);
|
||||
this->mouseList = glGenLists(1);
|
||||
|
||||
// generate the pixel unpack buffers
|
||||
glGenBuffers(VBO_BUFFERS, this->vboID);
|
||||
glGenBuffers(1, this->vboID);
|
||||
if (lgr_opengl_check_error("glGenBuffers"))
|
||||
return false;
|
||||
this->hasBuffers = true;
|
||||
|
||||
// persistant bind the buffers
|
||||
for (int i = 0; i < VBO_BUFFERS; ++i)
|
||||
{
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->vboID[i]);
|
||||
if (lgr_opengl_check_error("glBindBuffer"))
|
||||
return false;
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->vboID[0]);
|
||||
if (lgr_opengl_check_error("glBindBuffer"))
|
||||
return false;
|
||||
|
||||
glBufferStorage(GL_PIXEL_UNPACK_BUFFER, this->texSize, 0, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT);
|
||||
if (lgr_opengl_check_error("glBufferStorage"))
|
||||
return false;
|
||||
glBufferStorage(
|
||||
GL_PIXEL_UNPACK_BUFFER,
|
||||
this->texSize * BUFFER_COUNT,
|
||||
NULL,
|
||||
GL_MAP_WRITE_BIT |
|
||||
GL_MAP_PERSISTENT_BIT
|
||||
);
|
||||
if (lgr_opengl_check_error("glBufferStorage"))
|
||||
return false;
|
||||
|
||||
this->texPixels[i] = glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, this->texSize,
|
||||
GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_FLUSH_EXPLICIT_BIT);
|
||||
if (lgr_opengl_check_error("glMapBufferRange"))
|
||||
return false;
|
||||
this->texPixels[0] = glMapBufferRange(
|
||||
GL_PIXEL_UNPACK_BUFFER,
|
||||
0,
|
||||
this->texSize * BUFFER_COUNT,
|
||||
GL_MAP_WRITE_BIT |
|
||||
GL_MAP_PERSISTENT_BIT |
|
||||
GL_MAP_FLUSH_EXPLICIT_BIT
|
||||
);
|
||||
|
||||
if (!this->texPixels[i])
|
||||
{
|
||||
DEBUG_ERROR("Failed to map the buffer range");
|
||||
return false;
|
||||
}
|
||||
if (lgr_opengl_check_error("glMapBufferRange"))
|
||||
return false;
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
for(int i = 1; i < BUFFER_COUNT; ++i)
|
||||
this->texPixels[i] = this->texPixels[i-1] + this->texSize;
|
||||
|
||||
// create the textures
|
||||
glGenTextures(TEXTURE_COUNT, this->textures);
|
||||
@@ -196,41 +246,51 @@ bool lgr_opengl_initialize(void ** opaque, const LG_RendererParams params, const
|
||||
return false;
|
||||
this->hasTextures = true;
|
||||
|
||||
// bind the textures to the unpack buffers
|
||||
for (int i = 0; i < VBO_BUFFERS; ++i)
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, this->textures[i]);
|
||||
if (lgr_opengl_check_error("glBindTexture"))
|
||||
return false;
|
||||
// create the frame texture
|
||||
glBindTexture(GL_TEXTURE_2D, this->textures[FRAME_TEXTURE]);
|
||||
if (lgr_opengl_check_error("glBindTexture"))
|
||||
return false;
|
||||
|
||||
glTexImage2D(
|
||||
GL_TEXTURE_2D,
|
||||
0,
|
||||
this->intFormat,
|
||||
format.width, format.height,
|
||||
0,
|
||||
this->vboFormat,
|
||||
GL_UNSIGNED_BYTE,
|
||||
(void*)0
|
||||
);
|
||||
glTexImage2D(
|
||||
GL_TEXTURE_2D,
|
||||
0,
|
||||
this->intFormat,
|
||||
format.width,
|
||||
format.height * BUFFER_COUNT,
|
||||
0,
|
||||
this->vboFormat,
|
||||
GL_UNSIGNED_BYTE,
|
||||
(void*)0
|
||||
);
|
||||
if (lgr_opengl_check_error("glTexImage2D"))
|
||||
return false;
|
||||
|
||||
// configure the texture
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
|
||||
for (int i = 0; i < BUFFER_COUNT; ++i)
|
||||
{
|
||||
const float ts = (1.0f / BUFFER_COUNT) * i;
|
||||
const float te = (1.0f / BUFFER_COUNT) + ts;
|
||||
|
||||
// create the display lists
|
||||
glNewList(this->texList + i, GL_COMPILE);
|
||||
glBindTexture(GL_TEXTURE_2D, this->textures[i]);
|
||||
glBindTexture(GL_TEXTURE_2D, this->textures[FRAME_TEXTURE]);
|
||||
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
glBegin(GL_TRIANGLE_STRIP);
|
||||
glTexCoord2f(0.0f, 0.0f); glVertex2i(0 , 0 );
|
||||
glTexCoord2f(1.0f, 0.0f); glVertex2i(format.width, 0 );
|
||||
glTexCoord2f(0.0f, 1.0f); glVertex2i(0 , format.height);
|
||||
glTexCoord2f(1.0f, 1.0f); glVertex2i(format.width, format.height);
|
||||
glTexCoord2f(0.0f, ts); glVertex2i(0 , 0 );
|
||||
glTexCoord2f(1.0f, ts); glVertex2i(format.width, 0 );
|
||||
glTexCoord2f(0.0f, te); glVertex2i(0 , format.height);
|
||||
glTexCoord2f(1.0f, te); glVertex2i(format.width, format.height);
|
||||
glEnd();
|
||||
glEndList();
|
||||
|
||||
if (lgr_opengl_check_error("glTexImage2D"))
|
||||
return false;
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glEnable(GL_COLOR_MATERIAL);
|
||||
@@ -244,24 +304,45 @@ bool lgr_opengl_initialize(void ** opaque, const LG_RendererParams params, const
|
||||
|
||||
// copy the format into the local storage
|
||||
memcpy(&this->format, &format, sizeof(LG_RendererFormat));
|
||||
this->initialized = true;
|
||||
this->configured = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void lgr_opengl_deconfigure(void * opaque)
|
||||
{
|
||||
struct LGR_OpenGL * this = (struct LGR_OpenGL *)opaque;
|
||||
if (!this || !this->configured)
|
||||
return;
|
||||
|
||||
if (this->hasTextures)
|
||||
{
|
||||
glDeleteTextures(TEXTURE_COUNT, this->textures);
|
||||
this->hasTextures = false;
|
||||
}
|
||||
|
||||
if (this->hasBuffers)
|
||||
{
|
||||
glDeleteBuffers(1, this->vboID);
|
||||
this->hasBuffers = false;
|
||||
}
|
||||
|
||||
if (this->glContext)
|
||||
{
|
||||
SDL_GL_DeleteContext(this->glContext);
|
||||
this->glContext = NULL;
|
||||
}
|
||||
|
||||
this->configured = false;
|
||||
}
|
||||
|
||||
void lgr_opengl_deinitialize(void * opaque)
|
||||
{
|
||||
struct LGR_OpenGL * this = (struct LGR_OpenGL *)opaque;
|
||||
if (!this)
|
||||
return;
|
||||
|
||||
if (this->hasTextures)
|
||||
glDeleteTextures(VBO_BUFFERS, this->textures);
|
||||
|
||||
if (this->hasBuffers)
|
||||
glDeleteBuffers(VBO_BUFFERS, this->vboID);
|
||||
|
||||
if (this->glContext)
|
||||
SDL_GL_DeleteContext(this->glContext);
|
||||
if (this->configured)
|
||||
lgr_opengl_deconfigure(opaque);
|
||||
|
||||
free(this);
|
||||
}
|
||||
@@ -269,7 +350,7 @@ void lgr_opengl_deinitialize(void * opaque)
|
||||
bool lgr_opengl_is_compatible(void * opaque, const LG_RendererFormat format)
|
||||
{
|
||||
const struct LGR_OpenGL * this = (struct LGR_OpenGL *)opaque;
|
||||
if (!this || !this->initialized)
|
||||
if (!this || !this->configured)
|
||||
return false;
|
||||
|
||||
return (memcmp(&this->format, &format, sizeof(LG_RendererFormat)) == 0);
|
||||
@@ -278,11 +359,11 @@ bool lgr_opengl_is_compatible(void * opaque, const LG_RendererFormat format)
|
||||
void lgr_opengl_on_resize(void * opaque, const int width, const int height, const LG_RendererRect destRect)
|
||||
{
|
||||
struct LGR_OpenGL * this = (struct LGR_OpenGL *)opaque;
|
||||
if (!this || !this->initialized)
|
||||
if (!this || !this->configured)
|
||||
return;
|
||||
|
||||
this->params.width = width;
|
||||
this->params.height = height;
|
||||
this->window.x = width;
|
||||
this->window.y = height;
|
||||
memcpy(&this->destRect, &destRect, sizeof(LG_RendererRect));
|
||||
|
||||
this->resizeWindow = true;
|
||||
@@ -291,7 +372,7 @@ void lgr_opengl_on_resize(void * opaque, const int width, const int height, cons
|
||||
bool lgr_opengl_on_mouse_shape(void * opaque, const LG_RendererCursor cursor, const int width, const int height, const int pitch, const uint8_t * data)
|
||||
{
|
||||
struct LGR_OpenGL * this = (struct LGR_OpenGL *)opaque;
|
||||
if (!this || !this->initialized)
|
||||
if (!this || !this->configured)
|
||||
return false;
|
||||
|
||||
this->mouseType = cursor;
|
||||
@@ -409,13 +490,14 @@ bool lgr_opengl_on_mouse_shape(void * opaque, const LG_RendererCursor cursor, co
|
||||
}
|
||||
}
|
||||
|
||||
this->mouseUpdate = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool lgr_opengl_on_mouse_event(void * opaque, const bool visible, const int x, const int y)
|
||||
{
|
||||
struct LGR_OpenGL * this = (struct LGR_OpenGL *)opaque;
|
||||
if (!this || !this->initialized)
|
||||
if (!this || !this->configured)
|
||||
return false;
|
||||
|
||||
if (this->mousePos.x == x && this->mousePos.y == y && this->mouseVisible == visible)
|
||||
@@ -424,18 +506,24 @@ bool lgr_opengl_on_mouse_event(void * opaque, const bool visible, const int x, c
|
||||
this->mouseVisible = visible;
|
||||
this->mousePos.x = x;
|
||||
this->mousePos.y = y;
|
||||
|
||||
this->mouseUpdate = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool lgr_opengl_on_frame_event(void * opaque, const uint8_t * data, bool resample)
|
||||
bool lgr_opengl_on_frame_event(void * opaque, const uint8_t * data)
|
||||
{
|
||||
struct LGR_OpenGL * this = (struct LGR_OpenGL *)opaque;
|
||||
if (!this || !this->initialized)
|
||||
if (!this)
|
||||
{
|
||||
DEBUG_ERROR("Invalid opaque pointer");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (++this->texIndex == VBO_BUFFERS)
|
||||
this->texIndex = 0;
|
||||
if (!this->configured)
|
||||
{
|
||||
DEBUG_ERROR("Not configured");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this->params.showFPS && this->renderTime > 1e9)
|
||||
{
|
||||
@@ -450,7 +538,7 @@ bool lgr_opengl_on_frame_event(void * opaque, const uint8_t * data, bool resampl
|
||||
return false;
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D , this->textures[VBO_BUFFERS]);
|
||||
glBindTexture(GL_TEXTURE_2D , this->textures[FPS_TEXTURE]);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT , 4 );
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, textSurface->w );
|
||||
glTexImage2D(
|
||||
@@ -495,7 +583,7 @@ bool lgr_opengl_on_frame_event(void * opaque, const uint8_t * data, bool resampl
|
||||
glEnd();
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, this->textures[VBO_BUFFERS]);
|
||||
glBindTexture(GL_TEXTURE_2D, this->textures[FPS_TEXTURE]);
|
||||
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
glBegin(GL_TRIANGLE_STRIP);
|
||||
glTexCoord2f(0.0f , 0.0f); glVertex2i(this->fpsRect.x , this->fpsRect.y );
|
||||
@@ -507,70 +595,67 @@ bool lgr_opengl_on_frame_event(void * opaque, const uint8_t * data, bool resampl
|
||||
glEndList();
|
||||
}
|
||||
|
||||
// bind the texture and update it
|
||||
glBindTexture(GL_TEXTURE_2D , this->textures[FRAME_TEXTURE]);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->vboID[0] );
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT , 4 );
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH , this->format.stride );
|
||||
|
||||
// copy the buffer to the texture
|
||||
memcpySSE(this->texPixels[this->texIndex], data, this->texSize);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->vboID[this->texIndex]);
|
||||
glFlushMappedBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, this->texSize);
|
||||
|
||||
// bind the texture and update it
|
||||
glBindTexture(GL_TEXTURE_2D , this->textures[this->texIndex]);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT , 4 );
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH , this->format.width );
|
||||
glFlushMappedBufferRange(
|
||||
GL_PIXEL_UNPACK_BUFFER,
|
||||
this->texSize * this->texIndex,
|
||||
this->texSize
|
||||
);
|
||||
|
||||
// update the texture
|
||||
glTexSubImage2D(
|
||||
GL_TEXTURE_2D,
|
||||
0,
|
||||
0, 0,
|
||||
0,
|
||||
this->texIndex * this->format.height,
|
||||
this->format.width ,
|
||||
this->format.height,
|
||||
this->vboFormat,
|
||||
GL_UNSIGNED_BYTE,
|
||||
(void*)0
|
||||
(void*)(this->texIndex * this->texSize)
|
||||
);
|
||||
|
||||
const bool mipmap = resample && (
|
||||
(this->format.width > this->destRect.w) ||
|
||||
(this->format.height > this->destRect.h));
|
||||
lgr_opengl_check_error("glTexSubImage2D");
|
||||
|
||||
// unbind the buffer
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
|
||||
// configure the texture
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
const bool mipmap = this->params.resample && (
|
||||
(this->format.width > this->destRect.w) ||
|
||||
(this->format.height > this->destRect.h));
|
||||
|
||||
if (this->mipmap != mipmap)
|
||||
{
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
|
||||
mipmap ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
|
||||
this->mipmap = mipmap;
|
||||
}
|
||||
|
||||
if (mipmap)
|
||||
{
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
}
|
||||
else
|
||||
{
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
if (++this->texIndex == BUFFER_COUNT)
|
||||
this->texIndex = 0;
|
||||
|
||||
this->frameUpdate = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void lgr_opengl_draw_mouse(struct LGR_OpenGL * this)
|
||||
static inline void lgr_opengl_draw_mouse(struct LGR_OpenGL * this)
|
||||
{
|
||||
if (this->mouseRepair)
|
||||
{
|
||||
glCallList(this->texList + this->texIndex);
|
||||
this->mouseRepair = false;
|
||||
}
|
||||
|
||||
if (!this->mouseVisible)
|
||||
return;
|
||||
|
||||
memcpy(&this->mouseRepairPos, &this->mousePos, sizeof(SDL_Rect));
|
||||
this->mouseRepair = true;
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(this->mouseRepairPos.x, this->mouseRepairPos.y, 0.0f);
|
||||
glTranslatef(this->mousePos.x, this->mousePos.y, 0.0f);
|
||||
glCallList(this->mouseList);
|
||||
glPopMatrix();
|
||||
}
|
||||
@@ -578,16 +663,16 @@ void lgr_opengl_draw_mouse(struct LGR_OpenGL * this)
|
||||
bool lgr_opengl_render(void * opaque)
|
||||
{
|
||||
struct LGR_OpenGL * this = (struct LGR_OpenGL *)opaque;
|
||||
if (!this || !this->initialized)
|
||||
if (!this || !this->configured)
|
||||
return false;
|
||||
|
||||
if (this->resizeWindow)
|
||||
{
|
||||
// setup the projection matrix
|
||||
glViewport(0, 0, this->params.width, this->params.height);
|
||||
glViewport(0, 0, this->window.x, this->window.y);
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
gluOrtho2D(0, this->params.width, this->params.height, 0);
|
||||
gluOrtho2D(0, this->window.x, this->window.y, 0);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
@@ -607,39 +692,42 @@ bool lgr_opengl_render(void * opaque)
|
||||
);
|
||||
|
||||
this->resizeWindow = false;
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
if (this->frameUpdate)
|
||||
|
||||
if (!this->frameUpdate)
|
||||
{
|
||||
glCallList(this->texList + this->texIndex);
|
||||
this->mouseRepair = false;
|
||||
if (!this->mouseUpdate)
|
||||
return true;
|
||||
|
||||
// don't update the mouse too fast
|
||||
const uint64_t delta = nanotime() - this->lastMouseDraw;
|
||||
if (delta < 1e7)
|
||||
return true;
|
||||
}
|
||||
|
||||
// wait for vsync
|
||||
unsigned int count;
|
||||
glXWaitVideoSyncSGI(1, 0, &count);
|
||||
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
|
||||
glCallList(this->texList + this->texIndex);
|
||||
lgr_opengl_draw_mouse(this);
|
||||
if (this->fpsTexture)
|
||||
glCallList(this->fpsList);
|
||||
|
||||
GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
glWaitSync(sync, 0, 1000);
|
||||
glDeleteSync(sync);
|
||||
|
||||
unsigned int count;
|
||||
glXGetVideoSyncSGI(&count);
|
||||
if (count == this->gpuFrameCount)
|
||||
glXWaitVideoSyncSGI(1, 0, &count);
|
||||
|
||||
SDL_GL_SwapWindow(this->params.window);
|
||||
glXGetVideoSyncSGI(&this->gpuFrameCount);
|
||||
glFlush();
|
||||
|
||||
++this->frameCount;
|
||||
const uint64_t t = nanotime();
|
||||
this->renderTime += t - this->lastFrameTime;
|
||||
this->lastFrameTime = t;
|
||||
|
||||
this->frameUpdate = false;
|
||||
this->frameUpdate = false;
|
||||
this->mouseUpdate = false;
|
||||
this->lastMouseDraw = t;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -647,6 +735,8 @@ const LG_Renderer LGR_OpenGL =
|
||||
{
|
||||
.get_name = lgr_opengl_get_name,
|
||||
.initialize = lgr_opengl_initialize,
|
||||
.configure = lgr_opengl_configure,
|
||||
.deconfigure = lgr_opengl_deconfigure,
|
||||
.deinitialize = lgr_opengl_deinitialize,
|
||||
.is_compatible = lgr_opengl_is_compatible,
|
||||
.on_resize = lgr_opengl_on_resize,
|
||||
|
||||
@@ -22,14 +22,14 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include <time.h>
|
||||
#include <stdint.h>
|
||||
|
||||
inline uint64_t microtime()
|
||||
static inline uint64_t microtime()
|
||||
{
|
||||
struct timespec time;
|
||||
clock_gettime(CLOCK_MONOTONIC_RAW, &time);
|
||||
return ((uint64_t)time.tv_sec * 1000000) + (time.tv_nsec / 1000);
|
||||
}
|
||||
|
||||
inline uint64_t nanotime()
|
||||
static inline uint64_t nanotime()
|
||||
{
|
||||
struct timespec time;
|
||||
clock_gettime(CLOCK_MONOTONIC_RAW, &time);
|
||||
|
||||
@@ -69,6 +69,7 @@ KVMFRFrame;
|
||||
#define KVMFR_HEADER_FLAG_FRAME 1 // frame update available
|
||||
#define KVMFR_HEADER_FLAG_CURSOR 2 // cursor update available
|
||||
#define KVMFR_HEADER_FLAG_RESTART 4 // restart signal from client
|
||||
#define KVMFR_HEADER_FLAG_READY 8 // ready signal from client
|
||||
|
||||
typedef struct KVMFRHeader
|
||||
{
|
||||
|
||||
@@ -397,13 +397,13 @@ GrabStatus DXGI::GrabFrame(FrameInfo & frame)
|
||||
|
||||
m_width = desc.Width;
|
||||
m_height = desc.Height;
|
||||
const int pitch = m_width * 4;
|
||||
|
||||
frame.width = desc.Width;
|
||||
frame.height = desc.Height;
|
||||
frame.stride = desc.Width;
|
||||
frame.stride = rect.Pitch / 4;
|
||||
|
||||
memcpySSE(frame.buffer, rect.pBits, min(frame.bufferSize, m_height * rect.Pitch));
|
||||
|
||||
memcpySSE(frame.buffer, rect.pBits, min(frame.bufferSize, m_height * pitch));
|
||||
status = surface->Unmap();
|
||||
|
||||
if (FAILED(status))
|
||||
|
||||
@@ -30,7 +30,7 @@ Service * Service::m_instance = NULL;
|
||||
Service::Service() :
|
||||
m_initialized(false),
|
||||
m_memory(NULL),
|
||||
m_readyEvent(INVALID_HANDLE_VALUE),
|
||||
m_timer(NULL),
|
||||
m_capture(NULL),
|
||||
m_header(NULL),
|
||||
m_frameIndex(0)
|
||||
@@ -73,11 +73,10 @@ bool Service::Initialize(ICapture * captureDevice)
|
||||
if (!InitPointers())
|
||||
return false;
|
||||
|
||||
m_readyEvent = m_ivshmem->CreateVectorEvent(0);
|
||||
if (m_readyEvent == INVALID_HANDLE_VALUE)
|
||||
m_timer = CreateWaitableTimer(NULL, TRUE, NULL);
|
||||
if (!m_timer)
|
||||
{
|
||||
DEBUG_ERROR("Failed to get event for vector 0");
|
||||
DeInitialize();
|
||||
DEBUG_ERROR("Failed to create waitable timer");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -117,10 +116,10 @@ bool Service::InitPointers()
|
||||
|
||||
void Service::DeInitialize()
|
||||
{
|
||||
if (m_readyEvent != INVALID_HANDLE_VALUE)
|
||||
if (m_timer)
|
||||
{
|
||||
CloseHandle(m_readyEvent);
|
||||
m_readyEvent = INVALID_HANDLE_VALUE;
|
||||
CloseHandle(m_timer);
|
||||
m_timer = NULL;
|
||||
}
|
||||
|
||||
m_header = NULL;
|
||||
@@ -152,41 +151,38 @@ bool Service::Process()
|
||||
frame.buffer = m_frame[m_frameIndex];
|
||||
frame.bufferSize = m_frameSize;
|
||||
|
||||
volatile uint8_t *flags = &m_header->flags;
|
||||
|
||||
// wait for the host to notify that is it is ready to proceed
|
||||
bool eventDone = false;
|
||||
while (!eventDone)
|
||||
while (true)
|
||||
{
|
||||
const uint8_t f = *flags;
|
||||
|
||||
// check if the client has flagged a restart
|
||||
if (m_header->flags & KVMFR_HEADER_FLAG_RESTART)
|
||||
if (f & KVMFR_HEADER_FLAG_RESTART)
|
||||
{
|
||||
InterlockedAnd8((char *)&m_header->flags, ~(KVMFR_HEADER_FLAG_RESTART));
|
||||
InterlockedAnd8((volatile char *)flags, ~(KVMFR_HEADER_FLAG_RESTART));
|
||||
restart = true;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (WaitForSingleObject(m_readyEvent, 200))
|
||||
// check if the client has flagged it's ready
|
||||
if (f & KVMFR_HEADER_FLAG_READY)
|
||||
{
|
||||
case WAIT_ABANDONED:
|
||||
DEBUG_ERROR("Wait abandoned");
|
||||
return false;
|
||||
|
||||
case WAIT_OBJECT_0:
|
||||
eventDone = true;
|
||||
InterlockedAnd8((volatile char *)flags, ~(KVMFR_HEADER_FLAG_READY));
|
||||
break;
|
||||
}
|
||||
|
||||
case WAIT_TIMEOUT:
|
||||
continue;
|
||||
|
||||
case WAIT_FAILED:
|
||||
DEBUG_ERROR("Wait failed");
|
||||
return false;
|
||||
|
||||
default:
|
||||
DEBUG_ERROR("Unknown error");
|
||||
// wait for 100ns before polling again
|
||||
LARGE_INTEGER timeout;
|
||||
timeout.QuadPart = -100;
|
||||
if (!SetWaitableTimer(m_timer, &timeout, 0, NULL, NULL, FALSE))
|
||||
{
|
||||
DEBUG_ERROR("Failed to set waitable timer");
|
||||
return false;
|
||||
}
|
||||
WaitForSingleObject(m_timer, INFINITE);
|
||||
}
|
||||
ResetEvent(m_readyEvent);
|
||||
|
||||
bool ok = false;
|
||||
bool cursorOnly = false;
|
||||
@@ -228,13 +224,13 @@ bool Service::Process()
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t flags = 0;
|
||||
uint8_t updateFlags = 0;
|
||||
m_header->cursor.flags = 0;
|
||||
|
||||
if (!cursorOnly)
|
||||
{
|
||||
// signal a frame update
|
||||
flags |= KVMFR_HEADER_FLAG_FRAME;
|
||||
updateFlags |= KVMFR_HEADER_FLAG_FRAME;
|
||||
m_header->frame.type = m_capture->GetFrameType();
|
||||
m_header->frame.width = frame.width;
|
||||
m_header->frame.height = frame.height;
|
||||
@@ -247,7 +243,7 @@ bool Service::Process()
|
||||
if (frame.cursor.hasPos)
|
||||
{
|
||||
// tell the host where the cursor is
|
||||
flags |= KVMFR_HEADER_FLAG_CURSOR;
|
||||
updateFlags |= KVMFR_HEADER_FLAG_CURSOR;
|
||||
m_header->cursor.flags |= KVMFR_CURSOR_FLAG_POS;
|
||||
if (frame.cursor.visible)
|
||||
m_header->cursor.flags |= KVMFR_CURSOR_FLAG_VISIBLE;
|
||||
@@ -263,7 +259,7 @@ bool Service::Process()
|
||||
if (frame.cursor.hasShape)
|
||||
{
|
||||
// give the host the new cursor shape
|
||||
flags |= KVMFR_HEADER_FLAG_CURSOR;
|
||||
updateFlags |= KVMFR_HEADER_FLAG_CURSOR;
|
||||
m_header->cursor.flags |= KVMFR_CURSOR_FLAG_SHAPE;
|
||||
m_header->cursor.type = frame.cursor.type;
|
||||
m_header->cursor.w = frame.cursor.w;
|
||||
@@ -287,15 +283,15 @@ bool Service::Process()
|
||||
// if we already have a shape and the client restarted send it to them
|
||||
if (restart && m_haveShape)
|
||||
{
|
||||
flags |= KVMFR_HEADER_FLAG_CURSOR;
|
||||
updateFlags |= KVMFR_HEADER_FLAG_CURSOR;
|
||||
m_cursor.flags |= KVMFR_CURSOR_FLAG_SHAPE;
|
||||
memcpy(&m_header->cursor, &m_cursor, sizeof(KVMFRCursor));
|
||||
}
|
||||
}
|
||||
|
||||
// update the flags
|
||||
InterlockedAnd8((char *)&m_header->flags, KVMFR_HEADER_FLAG_RESTART);
|
||||
InterlockedOr8 ((char *)&m_header->flags, flags);
|
||||
InterlockedAnd8((volatile char *)flags, KVMFR_HEADER_FLAG_RESTART);
|
||||
InterlockedOr8 ((volatile char *)flags, updateFlags);
|
||||
|
||||
// increment the update count to resume the host
|
||||
++m_header->updateCount;
|
||||
|
||||
@@ -51,7 +51,7 @@ private:
|
||||
bool m_initialized;
|
||||
uint8_t * m_memory;
|
||||
IVSHMEM * m_ivshmem;
|
||||
HANDLE m_readyEvent;
|
||||
HANDLE m_timer;
|
||||
ICapture * m_capture;
|
||||
|
||||
KVMFRHeader * m_header;
|
||||
|
||||
Reference in New Issue
Block a user