mirror of
https://github.com/gnif/LookingGlass.git
synced 2025-11-17 07:28:44 +00:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3784e9c06f | ||
|
|
24709ef186 | ||
|
|
db59e67ddb | ||
|
|
1815387bbb | ||
|
|
6595374a2c | ||
|
|
9d8f3a79b0 | ||
|
|
bf85358715 | ||
|
|
b77a34b8b6 | ||
|
|
287b983d27 | ||
|
|
7c5b2b5c1c | ||
|
|
758b7af754 | ||
|
|
b89a8fee04 | ||
|
|
2bb8b0227c | ||
|
|
ae4156d041 | ||
|
|
fe337cf510 | ||
|
|
7bfed41523 | ||
|
|
9bb66b7bd6 | ||
|
|
f7420317f1 | ||
|
|
c1379a45d2 | ||
|
|
9c03327701 | ||
|
|
0d8b2449cf | ||
|
|
d1bd5b3115 | ||
|
|
e03621a622 |
@@ -30,6 +30,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/select.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
@@ -544,4 +545,4 @@ bool ivshmem_kick_irq(uint16_t clientID, uint16_t vector)
|
||||
|
||||
DEBUG_ERROR("failed to send kick");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ typedef struct LG_RendererParams
|
||||
TTF_Font * font;
|
||||
bool showFPS;
|
||||
bool resample;
|
||||
bool vsync;
|
||||
}
|
||||
LG_RendererParams;
|
||||
|
||||
@@ -97,4 +98,4 @@ typedef struct LG_Renderer
|
||||
LG_RendererOnFrameEvent on_frame_event;
|
||||
LG_RendererRender render;
|
||||
}
|
||||
LG_Renderer;
|
||||
LG_Renderer;
|
||||
180
client/main.c
180
client/main.c
@@ -50,6 +50,7 @@ struct AppState
|
||||
SDL_Point srcSize;
|
||||
LG_RendererRect dstRect;
|
||||
SDL_Point cursor;
|
||||
bool cursorVisible;
|
||||
bool haveCursorPos;
|
||||
float scaleX, scaleY;
|
||||
|
||||
@@ -63,6 +64,7 @@ struct AppState
|
||||
|
||||
struct AppParams
|
||||
{
|
||||
bool vsync;
|
||||
bool autoResize;
|
||||
bool allowResize;
|
||||
bool keepAspect;
|
||||
@@ -84,6 +86,7 @@ struct AppParams
|
||||
struct AppState state;
|
||||
struct AppParams params =
|
||||
{
|
||||
.vsync = true,
|
||||
.autoResize = false,
|
||||
.allowResize = true,
|
||||
.keepAspect = true,
|
||||
@@ -136,8 +139,11 @@ inline void updatePositionInfo()
|
||||
state.dstRect.h = h;
|
||||
}
|
||||
|
||||
state.scaleX = (float)state.srcSize.y / (float)state.dstRect.h;
|
||||
state.scaleY = (float)state.srcSize.x / (float)state.dstRect.w;
|
||||
if (state.started)
|
||||
{
|
||||
state.scaleX = (float)state.srcSize.y / (float)state.dstRect.h;
|
||||
state.scaleY = (float)state.srcSize.x / (float)state.dstRect.w;
|
||||
}
|
||||
|
||||
if (state.lgr)
|
||||
state.lgr->on_resize(state.lgrData, w, h, state.dstRect);
|
||||
@@ -147,6 +153,12 @@ int renderThread(void * unused)
|
||||
{
|
||||
bool error = false;
|
||||
struct KVMFRHeader header;
|
||||
|
||||
LG_RendererCursor cursorType = LG_CURSOR_COLOR;
|
||||
struct KVMFRFrame cursor = {};
|
||||
size_t cursorDataSize = 0;
|
||||
uint8_t * cursorData = NULL;
|
||||
|
||||
volatile uint32_t * updateCount = &state.shm->updateCount;
|
||||
|
||||
memset(&header, 0, sizeof(struct KVMFRHeader));
|
||||
@@ -191,12 +203,14 @@ int renderThread(void * unused)
|
||||
{
|
||||
// sainty check of the frame format
|
||||
if (
|
||||
header.frame.type >= FRAME_TYPE_MAX ||
|
||||
header.frame.width == 0 ||
|
||||
header.frame.height == 0 ||
|
||||
header.frame.stride == 0 ||
|
||||
header.frame.dataPos == 0 ||
|
||||
header.frame.dataPos > state.shmSize
|
||||
header.detail.frame.type >= FRAME_TYPE_MAX ||
|
||||
header.detail.frame.width == 0 ||
|
||||
header.detail.frame.height == 0 ||
|
||||
header.detail.frame.stride == 0 ||
|
||||
header.detail.frame.pitch == 0 ||
|
||||
header.detail.frame.dataPos == 0 ||
|
||||
header.detail.frame.dataPos > state.shmSize ||
|
||||
header.detail.frame.pitch < header.detail.frame.width
|
||||
){
|
||||
usleep(1000);
|
||||
continue;
|
||||
@@ -204,19 +218,18 @@ int renderThread(void * unused)
|
||||
|
||||
// setup the renderer format with the frame format details
|
||||
LG_RendererFormat lgrFormat;
|
||||
lgrFormat.width = header.frame.width;
|
||||
lgrFormat.height = header.frame.height;
|
||||
lgrFormat.stride = header.frame.stride;
|
||||
lgrFormat.width = header.detail.frame.width;
|
||||
lgrFormat.height = header.detail.frame.height;
|
||||
lgrFormat.stride = header.detail.frame.stride;
|
||||
lgrFormat.pitch = header.detail.frame.pitch;
|
||||
|
||||
switch(header.frame.type)
|
||||
switch(header.detail.frame.type)
|
||||
{
|
||||
case FRAME_TYPE_ARGB:
|
||||
lgrFormat.pitch = header.frame.stride * 4;
|
||||
lgrFormat.bpp = 32;
|
||||
break;
|
||||
|
||||
case FRAME_TYPE_RGB:
|
||||
lgrFormat.pitch = header.frame.stride * 3;
|
||||
lgrFormat.bpp = 24;
|
||||
break;
|
||||
|
||||
@@ -231,7 +244,7 @@ int renderThread(void * unused)
|
||||
|
||||
// check the header's dataPos is sane
|
||||
const size_t dataSize = lgrFormat.height * lgrFormat.pitch;
|
||||
if (header.frame.dataPos + dataSize > state.shmSize)
|
||||
if (header.detail.frame.dataPos + dataSize > state.shmSize)
|
||||
{
|
||||
DEBUG_ERROR("The guest sent an invalid dataPos");
|
||||
break;
|
||||
@@ -250,14 +263,49 @@ int renderThread(void * unused)
|
||||
break;
|
||||
}
|
||||
|
||||
state.srcSize.x = header.frame.width;
|
||||
state.srcSize.y = header.frame.height;
|
||||
state.srcSize.x = header.detail.frame.width;
|
||||
state.srcSize.y = header.detail.frame.height;
|
||||
if (params.autoResize)
|
||||
SDL_SetWindowSize(state.window, header.frame.width, header.frame.height);
|
||||
SDL_SetWindowSize(state.window, header.detail.frame.width, header.detail.frame.height);
|
||||
|
||||
state.started = true;
|
||||
updatePositionInfo();
|
||||
|
||||
// if we have saved shape info, send it now
|
||||
if (cursorData)
|
||||
{
|
||||
if (!state.lgr->on_mouse_shape(
|
||||
state.lgrData,
|
||||
cursorType,
|
||||
cursor.width,
|
||||
cursor.height,
|
||||
cursor.pitch,
|
||||
cursorData
|
||||
))
|
||||
{
|
||||
DEBUG_ERROR("Failed to update mouse shape");
|
||||
break;
|
||||
}
|
||||
|
||||
free(cursorData);
|
||||
cursorData = NULL;
|
||||
cursorDataSize = 0;
|
||||
}
|
||||
|
||||
// if we have a cursor position, send it now
|
||||
if (state.haveCursorPos)
|
||||
{
|
||||
state.lgr->on_mouse_event
|
||||
(
|
||||
state.lgrData,
|
||||
state.cursorVisible,
|
||||
state.cursor.x,
|
||||
state.cursor.y
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const uint8_t * data = (const uint8_t *)state.shm + header.frame.dataPos;
|
||||
const uint8_t * data = (const uint8_t *)state.shm + header.detail.frame.dataPos;
|
||||
if (!state.lgr->on_frame_event(state.lgrData, data))
|
||||
{
|
||||
DEBUG_ERROR("Failed to render the frame");
|
||||
@@ -268,45 +316,64 @@ int renderThread(void * unused)
|
||||
// if we have cursor data
|
||||
if (header.flags & KVMFR_HEADER_FLAG_CURSOR)
|
||||
{
|
||||
if (header.cursor.flags & KVMFR_CURSOR_FLAG_POS)
|
||||
if (header.detail.cursor.flags & KVMFR_CURSOR_FLAG_POS)
|
||||
{
|
||||
state.cursor.x = header.cursor.x;
|
||||
state.cursor.y = header.cursor.y;
|
||||
state.cursor.x = header.detail.cursor.x;
|
||||
state.cursor.y = header.detail.cursor.y;
|
||||
state.cursorVisible = header.detail.cursor.flags & KVMFR_CURSOR_FLAG_VISIBLE;
|
||||
state.haveCursorPos = true;
|
||||
}
|
||||
|
||||
if (header.cursor.flags & KVMFR_CURSOR_FLAG_SHAPE)
|
||||
if (header.detail.cursor.flags & KVMFR_CURSOR_FLAG_SHAPE)
|
||||
{
|
||||
LG_RendererCursor c = LG_CURSOR_COLOR;
|
||||
switch(header.cursor.type)
|
||||
{
|
||||
case CURSOR_TYPE_COLOR : c = LG_CURSOR_COLOR ; break;
|
||||
case CURSOR_TYPE_MONOCHROME : c = LG_CURSOR_MONOCHROME ; break;
|
||||
case CURSOR_TYPE_MASKED_COLOR: c = LG_CURSOR_MASKED_COLOR; break;
|
||||
default:
|
||||
DEBUG_ERROR("Invalid cursor type");
|
||||
break;
|
||||
}
|
||||
|
||||
if (state.lgr)
|
||||
{
|
||||
bool bad = false;
|
||||
switch(header.detail.cursor.type)
|
||||
{
|
||||
case CURSOR_TYPE_COLOR : cursorType = LG_CURSOR_COLOR ; break;
|
||||
case CURSOR_TYPE_MONOCHROME : cursorType = LG_CURSOR_MONOCHROME ; break;
|
||||
case CURSOR_TYPE_MASKED_COLOR: cursorType = LG_CURSOR_MASKED_COLOR; break;
|
||||
default:
|
||||
DEBUG_ERROR("Invalid cursor type");
|
||||
bad = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (bad)
|
||||
break;
|
||||
|
||||
// check the data position is sane
|
||||
const uint64_t dataSize = header.cursor.h * header.cursor.pitch;
|
||||
if (header.cursor.dataPos + dataSize > state.shmSize)
|
||||
const uint64_t dataSize = header.detail.frame.height * header.detail.frame.pitch;
|
||||
if (header.detail.frame.dataPos + dataSize > state.shmSize)
|
||||
{
|
||||
DEBUG_ERROR("The guest sent an invalid mouse dataPos");
|
||||
break;
|
||||
}
|
||||
|
||||
const uint8_t * data = (const uint8_t *)state.shm + header.cursor.dataPos;
|
||||
const uint8_t * data = (const uint8_t *)state.shm + header.detail.frame.dataPos;
|
||||
if (!state.started)
|
||||
{
|
||||
// save off the cursor data so we can tell the renderer when it's ready
|
||||
if (cursorDataSize < dataSize)
|
||||
{
|
||||
if (cursorData) free(cursorData);
|
||||
cursorData = (uint8_t *)malloc(dataSize);
|
||||
cursorDataSize = dataSize;
|
||||
}
|
||||
memcpy(&cursor, &header.detail.frame, sizeof(struct KVMFRFrame));
|
||||
memcpy(cursorData, data, dataSize);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!state.lgr->on_mouse_shape(
|
||||
state.lgrData,
|
||||
c,
|
||||
header.cursor.w,
|
||||
header.cursor.h,
|
||||
header.cursor.pitch,
|
||||
data
|
||||
))
|
||||
cursorType,
|
||||
header.detail.frame.width,
|
||||
header.detail.frame.height,
|
||||
header.detail.frame.pitch,
|
||||
data)
|
||||
)
|
||||
{
|
||||
DEBUG_ERROR("Failed to update mouse shape");
|
||||
break;
|
||||
@@ -314,21 +381,24 @@ int renderThread(void * unused)
|
||||
}
|
||||
}
|
||||
|
||||
if (state.lgr)
|
||||
if (state.lgr && state.started)
|
||||
{
|
||||
state.lgr->on_mouse_event(
|
||||
state.lgrData,
|
||||
(header.cursor.flags & KVMFR_CURSOR_FLAG_VISIBLE) != 0,
|
||||
state.cursorVisible,
|
||||
state.cursor.x,
|
||||
state.cursor.y
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (state.lgr)
|
||||
if (state.started && state.lgr)
|
||||
state.lgr->render(state.lgrData);
|
||||
}
|
||||
|
||||
if (cursorData)
|
||||
free(cursorData);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -631,6 +701,7 @@ int run()
|
||||
lgrParams.font = state.font;
|
||||
lgrParams.resample = params.useMipmap;
|
||||
lgrParams.showFPS = params.showFPS;
|
||||
lgrParams.vsync = params.vsync;
|
||||
Uint32 sdlFlags;
|
||||
|
||||
// probe for a a suitable renderer
|
||||
@@ -676,6 +747,11 @@ int run()
|
||||
)
|
||||
);
|
||||
|
||||
if (params.fullscreen)
|
||||
{
|
||||
SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
|
||||
}
|
||||
|
||||
// set the compositor hint to bypass for low latency
|
||||
SDL_SysWMinfo wminfo;
|
||||
SDL_VERSION(&wminfo.version);
|
||||
@@ -743,8 +819,7 @@ int run()
|
||||
DEBUG_ERROR("Failed to map memory");
|
||||
break;
|
||||
}
|
||||
state.shmSize = ivshmem_get_map_size();
|
||||
state.shm->hostID = ivshmem_get_id();
|
||||
state.shmSize = ivshmem_get_map_size();
|
||||
|
||||
if (params.useSpice)
|
||||
{
|
||||
@@ -841,13 +916,14 @@ void doHelp(char * app)
|
||||
" -M Don't hide the host cursor\n"
|
||||
"\n"
|
||||
" -m Disable mipmapping\n"
|
||||
" -v Disable VSync\n"
|
||||
" -v Disable VSYNC\n"
|
||||
" -k Enable FPS display\n"
|
||||
"\n"
|
||||
" -a Auto resize the window to the guest\n"
|
||||
" -n Don't allow the window to be manually resized\n"
|
||||
" -r Don't maintain the aspect ratio\n"
|
||||
" -d Borderless mode\n"
|
||||
" -F Borderless fullscreen mode\n"
|
||||
" -x XPOS Initial window X position [current: %s]\n"
|
||||
" -y YPOS Initial window Y position [current: %s]\n"
|
||||
" -w WIDTH Initial window width [current: %u]\n"
|
||||
@@ -894,7 +970,7 @@ void doLicense()
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
int c;
|
||||
while((c = getopt(argc, argv, "hf:sc:p:jMmkanrdFx:y:w:b:l")) != -1)
|
||||
while((c = getopt(argc, argv, "hf:sc:p:jMmvkanrdFx:y:w:b:l")) != -1)
|
||||
switch(c)
|
||||
{
|
||||
case '?':
|
||||
@@ -931,6 +1007,10 @@ int main(int argc, char * argv[])
|
||||
params.useMipmap = false;
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
params.vsync = false;
|
||||
break;
|
||||
|
||||
case 'k':
|
||||
params.showFPS = true;
|
||||
break;
|
||||
|
||||
@@ -47,6 +47,7 @@ struct LGR_OpenGL
|
||||
{
|
||||
LG_RendererParams params;
|
||||
bool configured;
|
||||
SDL_Window * sdlWindow;
|
||||
SDL_GLContext glContext;
|
||||
bool doneInfo;
|
||||
|
||||
@@ -68,7 +69,6 @@ struct LGR_OpenGL
|
||||
int fpsList;
|
||||
int mouseList;
|
||||
LG_RendererRect destRect;
|
||||
bool mipmap;
|
||||
|
||||
bool hasTextures;
|
||||
GLuint textures[TEXTURE_COUNT];
|
||||
@@ -81,6 +81,7 @@ struct LGR_OpenGL
|
||||
SDL_Rect fpsRect;
|
||||
|
||||
bool mouseUpdate;
|
||||
bool newShape;
|
||||
uint64_t lastMouseDraw;
|
||||
LG_RendererCursor mouseType;
|
||||
bool mouseVisible;
|
||||
@@ -131,7 +132,7 @@ bool lgr_opengl_initialize(void ** opaque, const LG_RendererParams params, Uint3
|
||||
}
|
||||
|
||||
*sdlFlags = SDL_WINDOW_OPENGL;
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 0);
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -147,6 +148,7 @@ bool lgr_opengl_configure(void * opaque, SDL_Window *window, const LG_RendererFo
|
||||
return false;
|
||||
}
|
||||
|
||||
this->sdlWindow = window;
|
||||
this->glContext = SDL_GL_CreateContext(window);
|
||||
if (!this->glContext)
|
||||
{
|
||||
@@ -168,7 +170,7 @@ bool lgr_opengl_configure(void * opaque, SDL_Window *window, const LG_RendererFo
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_GL_SetSwapInterval(0);
|
||||
SDL_GL_SetSwapInterval(this->params.vsync ? 1 : 0);
|
||||
|
||||
// check if the GPU supports GL_ARB_buffer_storage first
|
||||
// there is no advantage to this renderer if it is not present.
|
||||
@@ -362,7 +364,7 @@ void lgr_opengl_on_resize(void * opaque, const int width, const int height, cons
|
||||
if (!this || !this->configured)
|
||||
return;
|
||||
|
||||
this->window.x = width;
|
||||
this->window.x = width;
|
||||
this->window.y = height;
|
||||
memcpy(&this->destRect, &destRect, sizeof(LG_RendererRect));
|
||||
|
||||
@@ -491,6 +493,7 @@ bool lgr_opengl_on_mouse_shape(void * opaque, const LG_RendererCursor cursor, co
|
||||
}
|
||||
|
||||
this->mouseUpdate = true;
|
||||
this->newShape = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -630,15 +633,17 @@ bool lgr_opengl_on_frame_event(void * opaque, const uint8_t * data)
|
||||
(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);
|
||||
|
||||
@@ -700,15 +705,27 @@ bool lgr_opengl_render(void * opaque)
|
||||
if (!this->mouseUpdate)
|
||||
return true;
|
||||
|
||||
// don't update the mouse too fast
|
||||
const uint64_t delta = nanotime() - this->lastMouseDraw;
|
||||
if (delta < 1e7)
|
||||
return true;
|
||||
}
|
||||
if (!this->newShape)
|
||||
{
|
||||
// don't update the mouse too fast
|
||||
const uint64_t delta = nanotime() - this->lastMouseDraw;
|
||||
if (delta < 5e6)
|
||||
return true;
|
||||
}
|
||||
this->newShape = false;
|
||||
|
||||
// wait for vsync
|
||||
unsigned int count;
|
||||
glXWaitVideoSyncSGI(1, 0, &count);
|
||||
glDrawBuffer(GL_FRONT);
|
||||
glCallList(this->texList + this->texIndex);
|
||||
lgr_opengl_draw_mouse(this);
|
||||
if (this->fpsTexture)
|
||||
glCallList(this->fpsList);
|
||||
glDrawBuffer(GL_BACK);
|
||||
glFlush();
|
||||
|
||||
this->mouseUpdate = false;
|
||||
this->lastMouseDraw = nanotime();
|
||||
return true;
|
||||
}
|
||||
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
@@ -718,7 +735,15 @@ bool lgr_opengl_render(void * opaque)
|
||||
lgr_opengl_draw_mouse(this);
|
||||
if (this->fpsTexture)
|
||||
glCallList(this->fpsList);
|
||||
glFlush();
|
||||
|
||||
unsigned int before, after;
|
||||
glXGetVideoSyncSGI(&before);
|
||||
SDL_GL_SwapWindow(this->sdlWindow);
|
||||
|
||||
// wait for the swap to happen to ensure we dont buffer frames
|
||||
glXGetVideoSyncSGI(&after);
|
||||
if (before == after)
|
||||
glXWaitVideoSyncSGI(1, 0, &before);
|
||||
|
||||
++this->frameCount;
|
||||
const uint64_t t = nanotime();
|
||||
|
||||
@@ -21,7 +21,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include <stdint.h>
|
||||
|
||||
#define KVMFR_HEADER_MAGIC "[[KVMFR]]"
|
||||
#define KVMFR_HEADER_VERSION 3
|
||||
#define KVMFR_HEADER_VERSION 4
|
||||
|
||||
typedef enum FrameType
|
||||
{
|
||||
@@ -49,9 +49,6 @@ typedef struct KVMFRCursor
|
||||
uint8_t flags; // KVMFR_CURSOR_FLAGS
|
||||
int16_t x, y; // cursor x & y position
|
||||
CursorType type; // shape buffer data type
|
||||
uint8_t w, h; // shape width and height
|
||||
uint8_t pitch; // shape row length in bytes
|
||||
uint64_t dataPos; // offset to the cursor data
|
||||
}
|
||||
KVMFRCursor;
|
||||
|
||||
@@ -60,7 +57,8 @@ typedef struct KVMFRFrame
|
||||
FrameType type; // the frame data type
|
||||
uint32_t width; // the width
|
||||
uint32_t height; // the height
|
||||
uint32_t stride; // the row stride
|
||||
uint32_t stride; // the row stride (zero if cursor data)
|
||||
uint32_t pitch; // the row pitch (stride in bytes)
|
||||
uint64_t dataPos; // offset to the frame
|
||||
}
|
||||
KVMFRFrame;
|
||||
@@ -70,16 +68,19 @@ KVMFRFrame;
|
||||
#define KVMFR_HEADER_FLAG_RESTART 4 // restart signal from client
|
||||
#define KVMFR_HEADER_FLAG_READY 8 // ready signal from client
|
||||
|
||||
typedef struct KVMFRDetail
|
||||
{
|
||||
KVMFRFrame frame; // the frame information
|
||||
KVMFRCursor cursor; // the cursor information
|
||||
}
|
||||
KVMFRDetail;
|
||||
|
||||
typedef struct KVMFRHeader
|
||||
{
|
||||
char magic[sizeof(KVMFR_HEADER_MAGIC)];
|
||||
uint32_t version; // version of this structure
|
||||
uint16_t hostID; // the host ivshmem client id
|
||||
uint16_t guestID; // the guest ivshmem client id
|
||||
uint32_t updateCount; // updated each change
|
||||
uint8_t flags; // KVMFR_HEADER_FLAGS
|
||||
|
||||
KVMFRFrame frame; // the frame information
|
||||
KVMFRCursor cursor; // the cursor information
|
||||
KVMFRDetail detail; // details
|
||||
}
|
||||
KVMFRHeader;
|
||||
KVMFRHeader;
|
||||
|
||||
@@ -31,6 +31,7 @@ DXGI::DXGI() :
|
||||
m_deviceContext(),
|
||||
m_dup(),
|
||||
m_texture(),
|
||||
m_surface(),
|
||||
m_pointer(NULL)
|
||||
{
|
||||
}
|
||||
@@ -190,6 +191,15 @@ void DXGI::DeInitialize()
|
||||
m_pointerBufSize = 0;
|
||||
}
|
||||
|
||||
if (m_surfaceMapped)
|
||||
{
|
||||
m_surface->Unmap();
|
||||
m_surfaceMapped = false;
|
||||
}
|
||||
|
||||
if (m_surface)
|
||||
m_surface.Release();
|
||||
|
||||
if (m_texture)
|
||||
m_texture.Release();
|
||||
|
||||
@@ -241,7 +251,23 @@ GrabStatus DXGI::GrabFrame(FrameInfo & frame)
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
status = m_dup->AcquireNextFrame(INFINITE, &frameInfo, &res);
|
||||
status = m_dup->AcquireNextFrame(1000, &frameInfo, &res);
|
||||
if (status == DXGI_ERROR_WAIT_TIMEOUT)
|
||||
{
|
||||
if (!m_surfaceMapped)
|
||||
break;
|
||||
|
||||
// send the last frame again if we timeout to prevent the client stalling on restart
|
||||
frame.width = m_desc.Width;
|
||||
frame.height = m_desc.Height;
|
||||
frame.pitch = m_rect.Pitch;
|
||||
frame.stride = m_rect.Pitch / 4;
|
||||
|
||||
unsigned int size = m_height * m_rect.Pitch;
|
||||
memcpySSE(frame.buffer, m_rect.pBits, size < frame.bufferSize ? size : frame.bufferSize);
|
||||
return GRAB_STATUS_OK;
|
||||
}
|
||||
|
||||
if (!SUCCEEDED(status))
|
||||
break;
|
||||
|
||||
@@ -321,32 +347,34 @@ GrabStatus DXGI::GrabFrame(FrameInfo & frame)
|
||||
if (SUCCEEDED(status))
|
||||
break;
|
||||
|
||||
HDESK desktop = NULL;
|
||||
switch (status)
|
||||
{
|
||||
case DXGI_ERROR_ACCESS_LOST: // desktop switch, mode change or switch DWM on or off
|
||||
return GRAB_STATUS_REINIT;
|
||||
// desktop switch, mode change, switch DWM on or off or Secure Desktop
|
||||
case DXGI_ERROR_ACCESS_LOST:
|
||||
// see if we can open the desktop, if so request a reinit
|
||||
// if not the secure desktop is active so just wait for it
|
||||
// instead of aborting out
|
||||
desktop = OpenInputDesktop(0, TRUE, GENERIC_READ);
|
||||
if (desktop)
|
||||
{
|
||||
// open suceeded, not on the secure desktop, return to reinit
|
||||
CloseDesktop(desktop);
|
||||
return GRAB_STATUS_REINIT;
|
||||
}
|
||||
// fall through
|
||||
|
||||
case WAIT_ABANDONED: // this can happen also during desktop switches, not documented by MS though
|
||||
{
|
||||
// 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)
|
||||
// this can happen during desktop switches also, not documented by MS though
|
||||
case WAIT_ABANDONED:
|
||||
do
|
||||
{
|
||||
desktop = OpenInputDesktop(0, TRUE, GENERIC_READ);
|
||||
if (desktop)
|
||||
break;
|
||||
Sleep(100);
|
||||
}
|
||||
} while (!desktop);
|
||||
CloseDesktop(desktop);
|
||||
|
||||
if (!ReInitialize())
|
||||
{
|
||||
DEBUG_ERROR("Failed to ReInitialize after lost access to desktop");
|
||||
return GRAB_STATUS_ERROR;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
return GRAB_STATUS_REINIT;
|
||||
|
||||
default:
|
||||
// unknown failure
|
||||
@@ -371,8 +399,7 @@ GrabStatus DXGI::GrabFrame(FrameInfo & frame)
|
||||
return GRAB_STATUS_ERROR;
|
||||
}
|
||||
|
||||
D3D11_TEXTURE2D_DESC desc;
|
||||
src->GetDesc(&desc);
|
||||
src->GetDesc(&m_desc);
|
||||
|
||||
m_deviceContext->CopyResource(m_texture, src);
|
||||
|
||||
@@ -380,38 +407,43 @@ GrabStatus DXGI::GrabFrame(FrameInfo & frame)
|
||||
res.Release();
|
||||
src.Release();
|
||||
|
||||
IDXGISurface1Ptr surface(m_texture);
|
||||
if (!surface)
|
||||
if (m_surfaceMapped)
|
||||
{
|
||||
status = m_surface->Unmap();
|
||||
if (FAILED(status))
|
||||
{
|
||||
DEBUG_ERROR("Failed to unmap surface: %08x", (int)status);
|
||||
return GRAB_STATUS_ERROR;
|
||||
}
|
||||
m_surfaceMapped = false;
|
||||
m_surface.Release();
|
||||
}
|
||||
|
||||
m_surface = m_texture;
|
||||
if (!m_surface)
|
||||
{
|
||||
DEBUG_ERROR("Failed to get IDXGISurface1");
|
||||
return GRAB_STATUS_ERROR;
|
||||
}
|
||||
|
||||
DXGI_MAPPED_RECT rect;
|
||||
status = surface->Map(&rect, DXGI_MAP_READ);
|
||||
status = m_surface->Map(&m_rect, DXGI_MAP_READ);
|
||||
if (FAILED(status))
|
||||
{
|
||||
DEBUG_ERROR("Failed to map surface: %08x", (int)status);
|
||||
return GRAB_STATUS_ERROR;
|
||||
}
|
||||
m_surfaceMapped = true;
|
||||
|
||||
m_width = desc.Width;
|
||||
m_height = desc.Height;
|
||||
m_width = m_desc.Width;
|
||||
m_height = m_desc.Height;
|
||||
|
||||
frame.width = desc.Width;
|
||||
frame.height = desc.Height;
|
||||
frame.stride = rect.Pitch / 4;
|
||||
frame.width = m_desc.Width;
|
||||
frame.height = m_desc.Height;
|
||||
frame.pitch = m_rect.Pitch;
|
||||
frame.stride = m_rect.Pitch / 4;
|
||||
|
||||
unsigned int size = m_height * rect.Pitch;
|
||||
memcpySSE(frame.buffer, rect.pBits, size < frame.bufferSize ? size : frame.bufferSize);
|
||||
|
||||
status = surface->Unmap();
|
||||
|
||||
if (FAILED(status))
|
||||
{
|
||||
DEBUG_ERROR("Failed to unmap surface: %08x", (int)status);
|
||||
return GRAB_STATUS_ERROR;
|
||||
}
|
||||
unsigned int size = m_height * m_rect.Pitch;
|
||||
memcpySSE(frame.buffer, m_rect.pBits, size < frame.bufferSize ? size : frame.bufferSize);
|
||||
|
||||
return GRAB_STATUS_OK;
|
||||
}
|
||||
@@ -80,6 +80,10 @@ namespace Capture
|
||||
IDXGIOutput1Ptr m_output;
|
||||
IDXGIOutputDuplicationPtr m_dup;
|
||||
ID3D11Texture2DPtr m_texture;
|
||||
IDXGISurface1Ptr m_surface;
|
||||
D3D11_TEXTURE2D_DESC m_desc;
|
||||
DXGI_MAPPED_RECT m_rect;
|
||||
bool m_surfaceMapped;
|
||||
BYTE * m_pointer;
|
||||
UINT m_pointerBufSize;
|
||||
UINT m_pointerSize;
|
||||
|
||||
@@ -289,6 +289,7 @@ enum GrabStatus NvFBC::GrabFrame(struct FrameInfo & frame)
|
||||
}
|
||||
|
||||
frame.stride = frame.width;
|
||||
frame.pitch = dataWidth;
|
||||
uint8_t *src = (uint8_t *)m_frameBuffer + dataOffset;
|
||||
uint8_t *dst = (uint8_t *)frame.buffer;
|
||||
for(unsigned int y = 0; y < frame.height; ++y, dst += dataWidth, src += m_grabInfo.dwBufferWidth * 4)
|
||||
|
||||
@@ -42,6 +42,7 @@ struct FrameInfo
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
unsigned int stride;
|
||||
unsigned int pitch;
|
||||
void * buffer;
|
||||
size_t bufferSize;
|
||||
|
||||
|
||||
@@ -40,12 +40,13 @@ Service::Service() :
|
||||
m_memory(NULL),
|
||||
m_timer(NULL),
|
||||
m_capture(NULL),
|
||||
m_header(NULL),
|
||||
m_shmHeader(NULL),
|
||||
m_frameIndex(0),
|
||||
m_cursorDataSize(0),
|
||||
m_cursorData(NULL),
|
||||
m_shapePending(false)
|
||||
{
|
||||
m_consoleSessionID = WTSGetActiveConsoleSessionId();
|
||||
m_ivshmem = IVSHMEM::Get();
|
||||
}
|
||||
|
||||
@@ -92,15 +93,13 @@ bool Service::Initialize(ICapture * captureDevice)
|
||||
}
|
||||
|
||||
// update everything except for the hostID
|
||||
memcpy(m_header->magic, KVMFR_HEADER_MAGIC, sizeof(KVMFR_HEADER_MAGIC));
|
||||
m_header->version = KVMFR_HEADER_VERSION;
|
||||
m_header->guestID = m_ivshmem->GetPeerID();
|
||||
m_header->updateCount = 0;
|
||||
memcpy(m_shmHeader->magic, KVMFR_HEADER_MAGIC, sizeof(KVMFR_HEADER_MAGIC));
|
||||
m_shmHeader->version = KVMFR_HEADER_VERSION;
|
||||
m_shmHeader->updateCount = 0;
|
||||
|
||||
// clear but retain the restart flag if it was set by the client
|
||||
INTERLOCKED_AND8((char *)&m_header->flags, KVMFR_HEADER_FLAG_RESTART);
|
||||
ZeroMemory(&m_header->frame , sizeof(KVMFRFrame ));
|
||||
ZeroMemory(&m_header->cursor, sizeof(KVMFRCursor));
|
||||
INTERLOCKED_AND8((char *)&m_shmHeader->flags, KVMFR_HEADER_FLAG_RESTART);
|
||||
ZeroMemory(&m_shmHeader->detail, sizeof(KVMFRDetail));
|
||||
|
||||
m_initialized = true;
|
||||
return true;
|
||||
@@ -108,7 +107,7 @@ bool Service::Initialize(ICapture * captureDevice)
|
||||
|
||||
bool Service::InitPointers()
|
||||
{
|
||||
m_header = reinterpret_cast<KVMFRHeader *>(m_memory);
|
||||
m_shmHeader = reinterpret_cast<KVMFRHeader *>(m_memory);
|
||||
m_frame[0] = (uint8_t *)(((uintptr_t)m_memory + sizeof(KVMFRHeader *) + 0x7F) & ~0x7F);
|
||||
m_frameSize = ((m_ivshmem->GetSize() - (m_frame[0] - m_memory)) & ~0x7F) >> 1;
|
||||
m_frame[1] = m_frame[0] + m_frameSize;
|
||||
@@ -142,7 +141,7 @@ void Service::DeInitialize()
|
||||
m_cursorData = NULL;
|
||||
}
|
||||
|
||||
m_header = NULL;
|
||||
m_shmHeader = NULL;
|
||||
m_frame[0] = NULL;
|
||||
m_frame[1] = NULL;
|
||||
m_dataOffset[0] = 0;
|
||||
@@ -171,7 +170,7 @@ bool Service::Process()
|
||||
frame.buffer = m_frame[m_frameIndex];
|
||||
frame.bufferSize = m_frameSize;
|
||||
|
||||
volatile uint8_t *flags = &m_header->flags;
|
||||
volatile uint8_t *flags = &m_shmHeader->flags;
|
||||
|
||||
// wait for the host to notify that is it is ready to proceed
|
||||
while (true)
|
||||
@@ -181,7 +180,7 @@ bool Service::Process()
|
||||
// check if the client has flagged a restart
|
||||
if (f & KVMFR_HEADER_FLAG_RESTART)
|
||||
{
|
||||
m_header->updateCount = 0;
|
||||
m_shmHeader->updateCount = 0;
|
||||
INTERLOCKED_AND8((volatile char *)flags, ~(KVMFR_HEADER_FLAG_RESTART));
|
||||
restart = true;
|
||||
break;
|
||||
@@ -233,6 +232,13 @@ bool Service::Process()
|
||||
|
||||
case GRAB_STATUS_REINIT:
|
||||
DEBUG_INFO("ReInitialize Requested");
|
||||
if(WTSGetActiveConsoleSessionId() != m_consoleSessionID)
|
||||
{
|
||||
DEBUG_INFO("User switch detected, waiting to regain control");
|
||||
while (WTSGetActiveConsoleSessionId() != m_consoleSessionID)
|
||||
Sleep(100);
|
||||
}
|
||||
|
||||
if (!m_capture->ReInitialize() || !InitPointers())
|
||||
{
|
||||
DEBUG_ERROR("ReInitialize Failed");
|
||||
@@ -252,17 +258,18 @@ bool Service::Process()
|
||||
}
|
||||
|
||||
uint8_t updateFlags = 0;
|
||||
m_header->cursor.flags = 0;
|
||||
m_detail.cursor.flags = 0;
|
||||
|
||||
if (!cursorOnly)
|
||||
{
|
||||
// signal a frame update
|
||||
updateFlags |= KVMFR_HEADER_FLAG_FRAME;
|
||||
m_header->frame.type = m_capture->GetFrameType();
|
||||
m_header->frame.width = frame.width;
|
||||
m_header->frame.height = frame.height;
|
||||
m_header->frame.stride = frame.stride;
|
||||
m_header->frame.dataPos = m_dataOffset[m_frameIndex];
|
||||
m_detail.frame.type = m_capture->GetFrameType();
|
||||
m_detail.frame.width = frame.width;
|
||||
m_detail.frame.height = frame.height;
|
||||
m_detail.frame.stride = frame.stride;
|
||||
m_detail.frame.pitch = frame.pitch;
|
||||
m_detail.frame.dataPos = m_dataOffset[m_frameIndex];
|
||||
if (++m_frameIndex == 2)
|
||||
m_frameIndex = 0;
|
||||
}
|
||||
@@ -280,11 +287,11 @@ bool Service::Process()
|
||||
|
||||
// tell the host where the cursor is
|
||||
updateFlags |= KVMFR_HEADER_FLAG_CURSOR;
|
||||
m_header->cursor.flags |= KVMFR_CURSOR_FLAG_POS;
|
||||
m_detail.cursor.flags |= KVMFR_CURSOR_FLAG_POS;
|
||||
if (m_cursor.visible)
|
||||
m_header->cursor.flags |= KVMFR_CURSOR_FLAG_VISIBLE;
|
||||
m_header->cursor.x = m_cursor.x;
|
||||
m_header->cursor.y = m_cursor.y;
|
||||
m_detail.cursor.flags |= KVMFR_CURSOR_FLAG_VISIBLE;
|
||||
m_detail.cursor.x = m_cursor.x;
|
||||
m_detail.cursor.y = m_cursor.y;
|
||||
}
|
||||
|
||||
if (frame.cursor.hasShape || m_shapePending || (m_cursor.hasShape && restart))
|
||||
@@ -323,15 +330,17 @@ bool Service::Process()
|
||||
{
|
||||
// give the host the new cursor shape
|
||||
updateFlags |= KVMFR_HEADER_FLAG_CURSOR;
|
||||
m_header->cursor.flags |= KVMFR_CURSOR_FLAG_SHAPE;
|
||||
m_detail.cursor.flags |= KVMFR_CURSOR_FLAG_SHAPE;
|
||||
if (m_cursor.visible)
|
||||
m_header->cursor.flags |= KVMFR_CURSOR_FLAG_VISIBLE;
|
||||
m_detail.cursor.flags |= KVMFR_CURSOR_FLAG_VISIBLE;
|
||||
|
||||
m_header->cursor.type = m_cursor.type;
|
||||
m_header->cursor.w = m_cursor.w;
|
||||
m_header->cursor.h = m_cursor.h;
|
||||
m_header->cursor.pitch = m_cursor.pitch;
|
||||
m_header->cursor.dataPos = m_dataOffset[m_frameIndex];
|
||||
// shapes are sent as frame data
|
||||
m_detail.cursor.type = m_cursor.type;
|
||||
m_detail.frame.width = m_cursor.w;
|
||||
m_detail.frame.height = m_cursor.h;
|
||||
m_detail.frame.stride = 0;
|
||||
m_detail.frame.pitch = m_cursor.pitch;
|
||||
m_detail.frame.dataPos = m_dataOffset[m_frameIndex];
|
||||
memcpy(m_frame[m_frameIndex], m_cursorData, m_cursor.dataSize);
|
||||
m_shapePending = false;
|
||||
|
||||
@@ -344,8 +353,11 @@ bool Service::Process()
|
||||
INTERLOCKED_AND8((volatile char *)flags, KVMFR_HEADER_FLAG_RESTART);
|
||||
INTERLOCKED_OR8 ((volatile char *)flags, updateFlags);
|
||||
|
||||
// increment the update count to resume the host
|
||||
++m_header->updateCount;
|
||||
// update the shared details only
|
||||
memcpy(&m_shmHeader->detail, &m_detail, sizeof(KVMFRDetail));
|
||||
|
||||
// increment the update count so the guest stops waiting
|
||||
++m_shmHeader->updateCount;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -49,12 +49,14 @@ private:
|
||||
~Service();
|
||||
|
||||
bool m_initialized;
|
||||
DWORD m_consoleSessionID;
|
||||
uint8_t * m_memory;
|
||||
IVSHMEM * m_ivshmem;
|
||||
HANDLE m_timer;
|
||||
ICapture * m_capture;
|
||||
|
||||
KVMFRHeader * m_header;
|
||||
KVMFRDetail m_detail;
|
||||
KVMFRHeader * m_shmHeader;
|
||||
uint8_t * m_frame[2];
|
||||
size_t m_frameSize;
|
||||
uint64_t m_dataOffset[2];
|
||||
|
||||
Reference in New Issue
Block a user