mirror of
https://github.com/gnif/LookingGlass.git
synced 2025-11-17 15:38:45 +00:00
Compare commits
44 Commits
Release/B1
...
porthole
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce5c36705e | ||
|
|
7aee4eed11 | ||
|
|
416c4bbb48 | ||
|
|
76007092d5 | ||
|
|
453b8e6a4d | ||
|
|
968b313993 | ||
|
|
4dde82c646 | ||
|
|
4c424cdbdf | ||
|
|
963e7f8393 | ||
|
|
f93c918aa5 | ||
|
|
d5f409b02e | ||
|
|
75cea21cfc | ||
|
|
df2a3b6151 | ||
|
|
fad4d18973 | ||
|
|
f4ad730cc4 | ||
|
|
67ddb70932 | ||
|
|
27c3a93d15 | ||
|
|
df9798c819 | ||
|
|
1dfa0ed218 | ||
|
|
01f5238a9d | ||
|
|
c382a5acb1 | ||
|
|
5e3a46beb9 | ||
|
|
6ed4e23b80 | ||
|
|
0851ae6f14 | ||
|
|
caebddce4d | ||
|
|
01da541815 | ||
|
|
9d6bb57eff | ||
|
|
438548c427 | ||
|
|
0e7e918e2c | ||
|
|
7d6e061ade | ||
|
|
66891aa536 | ||
|
|
1d7a2ccf82 | ||
|
|
e1bfb1234b | ||
|
|
9377fdfc37 | ||
|
|
5f1d17ba1f | ||
|
|
4c0ca1c8e7 | ||
|
|
8ef1aee35c | ||
|
|
4168cc8d78 | ||
|
|
bca54ab1f6 | ||
|
|
6d2c464436 | ||
|
|
e93bd7a3bf | ||
|
|
da94075e7b | ||
|
|
69522495de | ||
|
|
fce88fc72c |
3
arbiter/.gitignore
vendored
Normal file
3
arbiter/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
bin/
|
||||
build/
|
||||
*.swp
|
||||
62
arbiter/CMakeLists.txt
Normal file
62
arbiter/CMakeLists.txt
Normal file
@@ -0,0 +1,62 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(looking-glass-arbiter C)
|
||||
|
||||
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/")
|
||||
|
||||
include(GNUInstallDirs)
|
||||
include(CheckCCompilerFlag)
|
||||
include(FeatureSummary)
|
||||
|
||||
option(OPTIMIZE_FOR_NATIVE "Build with -march=native" ON)
|
||||
if(OPTIMIZE_FOR_NATIVE)
|
||||
CHECK_C_COMPILER_FLAG("-march=native" COMPILER_SUPPORTS_MARCH_NATIVE)
|
||||
if(COMPILER_SUPPORTS_MARCH_NATIVE)
|
||||
add_compile_options("-march=native")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_compile_options(
|
||||
"-Wall"
|
||||
"-Werror"
|
||||
"-Wfatal-errors"
|
||||
"-ffast-math"
|
||||
"-fdata-sections"
|
||||
"-ffunction-sections"
|
||||
"$<$<CONFIG:DEBUG>:-O0;-g3;-ggdb>"
|
||||
)
|
||||
|
||||
set(EXE_FLAGS "-Wl,--gc-sections")
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
|
||||
execute_process(
|
||||
COMMAND cat ../VERSION
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE BUILD_VERSION
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
add_definitions(-D BUILD_VERSION='"${BUILD_VERSION}"')
|
||||
get_filename_component(PROJECT_TOP "${PROJECT_SOURCE_DIR}/.." ABSOLUTE)
|
||||
|
||||
include_directories(
|
||||
${PROJECT_SOURCE_DIR}/include
|
||||
${CMAKE_BINARY_DIR}/include
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
src/main.c
|
||||
)
|
||||
|
||||
add_subdirectory("${PROJECT_TOP}/common" "${CMAKE_BINARY_DIR}/common")
|
||||
add_subdirectory("${PROJECT_TOP}/porthole" "${CMAKE_BINARY_DIR}/porthole")
|
||||
|
||||
add_executable(looking-glass-arbiter ${SOURCES})
|
||||
target_link_libraries(looking-glass-arbiter
|
||||
${EXE_FLAGS}
|
||||
lg_common
|
||||
porthole
|
||||
)
|
||||
|
||||
install(PROGRAMS ${CMAKE_BINARY_DIR}/looking-glass-arbiter DESTINATION bin/ COMPONENT binary)
|
||||
|
||||
feature_summary(WHAT ENABLED_FEATURES DISABLED_FEATURES)
|
||||
55
arbiter/src/main.c
Normal file
55
arbiter/src/main.c
Normal file
@@ -0,0 +1,55 @@
|
||||
#include "common/debug.h"
|
||||
#include "common/option.h"
|
||||
#include "porthole/client.h"
|
||||
|
||||
static struct Option options[] =
|
||||
{
|
||||
// app options
|
||||
{
|
||||
.module = "host",
|
||||
.name = "socket",
|
||||
.description = "The porthole host socket",
|
||||
.type = OPTION_TYPE_STRING,
|
||||
.value.x_string = "/var/tmp/porthole",
|
||||
},
|
||||
{0}
|
||||
};
|
||||
|
||||
static void map_event(uint32_t type, PortholeMap * map)
|
||||
{
|
||||
DEBUG_INFO("map_event: %u, %u, %u", type, map->id, map->size);
|
||||
}
|
||||
|
||||
static void unmap_event(uint32_t id)
|
||||
{
|
||||
DEBUG_INFO("unmap_event: %u", id);
|
||||
}
|
||||
|
||||
static void discon_event()
|
||||
{
|
||||
DEBUG_INFO("discon_event");
|
||||
}
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
option_register(options);
|
||||
if (!option_parse(argc, argv))
|
||||
return -1;
|
||||
|
||||
if (!option_validate())
|
||||
return -1;
|
||||
|
||||
PortholeClient phc;
|
||||
if (!porthole_client_open(
|
||||
&phc,
|
||||
option_get_string("host", "socket"),
|
||||
map_event,
|
||||
unmap_event,
|
||||
discon_event))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
porthole_client_close(&phc);
|
||||
return 0;
|
||||
}
|
||||
@@ -21,6 +21,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "common/framebuffer.h"
|
||||
|
||||
typedef enum CaptureResult
|
||||
{
|
||||
@@ -55,7 +56,6 @@ typedef struct CaptureFrame
|
||||
unsigned int pitch;
|
||||
unsigned int stride;
|
||||
CaptureFormat format;
|
||||
void * data;
|
||||
}
|
||||
CaptureFrame;
|
||||
|
||||
@@ -84,7 +84,8 @@ typedef struct CaptureInterface
|
||||
unsigned int (*getMaxFrameSize)();
|
||||
|
||||
CaptureResult (*capture )();
|
||||
CaptureResult (*getFrame )(CaptureFrame * frame );
|
||||
CaptureResult (*waitFrame )(CaptureFrame * frame );
|
||||
CaptureResult (*getFrame )(FrameBuffer frame );
|
||||
CaptureResult (*getPointer)(CapturePointer * pointer);
|
||||
}
|
||||
CaptureInterface;
|
||||
@@ -766,7 +766,7 @@ static CaptureResult dxgi_capture()
|
||||
return CAPTURE_RESULT_OK;
|
||||
}
|
||||
|
||||
static CaptureResult dxgi_getFrame(CaptureFrame * frame)
|
||||
static CaptureResult dxgi_waitFrame(CaptureFrame * frame)
|
||||
{
|
||||
assert(this);
|
||||
assert(this->initialized);
|
||||
@@ -778,7 +778,6 @@ static CaptureResult dxgi_getFrame(CaptureFrame * frame)
|
||||
if (this->stop)
|
||||
return CAPTURE_RESULT_REINIT;
|
||||
|
||||
// only reset the event if we used the texture
|
||||
os_resetEvent(tex->mapped);
|
||||
|
||||
frame->width = this->width;
|
||||
@@ -787,7 +786,16 @@ static CaptureResult dxgi_getFrame(CaptureFrame * frame)
|
||||
frame->stride = this->stride;
|
||||
frame->format = this->format;
|
||||
|
||||
memcpy(frame->data, tex->map.pData, this->pitch * this->height);
|
||||
return CAPTURE_RESULT_OK;
|
||||
}
|
||||
|
||||
static CaptureResult dxgi_getFrame(FrameBuffer frame)
|
||||
{
|
||||
assert(this);
|
||||
assert(this->initialized);
|
||||
|
||||
Texture * tex = &this->texture[this->texRIndex];
|
||||
framebuffer_write(frame, tex->map.pData, this->pitch * this->height);
|
||||
os_signalEvent(tex->free);
|
||||
|
||||
if (++this->texRIndex == this->maxTextures)
|
||||
@@ -867,6 +875,7 @@ struct CaptureInterface Capture_DXGI =
|
||||
.free = dxgi_free,
|
||||
.getMaxFrameSize = dxgi_getMaxFrameSize,
|
||||
.capture = dxgi_capture,
|
||||
.waitFrame = dxgi_waitFrame,
|
||||
.getFrame = dxgi_getFrame,
|
||||
.getPointer = dxgi_getPointer
|
||||
};
|
||||
@@ -23,6 +23,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include "windows/debug.h"
|
||||
#include "windows/mousehook.h"
|
||||
#include "common/option.h"
|
||||
#include "common/framebuffer.h"
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <windows.h>
|
||||
@@ -236,7 +237,7 @@ static CaptureResult nvfbc_capture()
|
||||
return CAPTURE_RESULT_OK;
|
||||
}
|
||||
|
||||
static CaptureResult nvfbc_getFrame(CaptureFrame * frame)
|
||||
static CaptureResult nvfbc_waitFrame(CaptureFrame * frame)
|
||||
{
|
||||
if (!os_waitEvent(this->frameEvent, 1000))
|
||||
return CAPTURE_RESULT_TIMEOUT;
|
||||
@@ -266,7 +267,16 @@ static CaptureResult nvfbc_getFrame(CaptureFrame * frame)
|
||||
#endif
|
||||
|
||||
frame->format = this->grabInfo.bIsHDR ? CAPTURE_FMT_RGBA10 : CAPTURE_FMT_BGRA;
|
||||
memcpy(frame->data, this->frameBuffer, frame->pitch * frame->height);
|
||||
return CAPTURE_RESULT_OK;
|
||||
}
|
||||
|
||||
static CaptureResult nvfbc_getFrame(FrameBuffer frame)
|
||||
{
|
||||
framebuffer_write(
|
||||
frame,
|
||||
this->frameBuffer,
|
||||
this->grabInfo.dwHeight * this->grabInfo.dwBufferWidth * 4
|
||||
);
|
||||
return CAPTURE_RESULT_OK;
|
||||
}
|
||||
|
||||
@@ -310,6 +320,7 @@ struct CaptureInterface Capture_NVFBC =
|
||||
.free = nvfbc_free,
|
||||
.getMaxFrameSize = nvfbc_getMaxFrameSize,
|
||||
.capture = nvfbc_capture,
|
||||
.waitFrame = nvfbc_waitFrame,
|
||||
.getFrame = nvfbc_getFrame,
|
||||
.getPointer = nvfbc_getPointer
|
||||
};
|
||||
@@ -368,11 +368,16 @@ bool os_shmemMmap(void **ptr)
|
||||
return true;
|
||||
}
|
||||
|
||||
IVSHMEM_MMAP_CONFIG config =
|
||||
{
|
||||
.cacheMode = IVSHMEM_CACHE_WRITECOMBINED
|
||||
};
|
||||
|
||||
memset(&app.shmemMap, 0, sizeof(IVSHMEM_MMAP));
|
||||
if (!DeviceIoControl(
|
||||
app.shmemHandle,
|
||||
IOCTL_IVSHMEM_REQUEST_MMAP,
|
||||
NULL, 0,
|
||||
&config, sizeof(IVSHMEM_MMAP_CONFIG),
|
||||
&app.shmemMap, sizeof(IVSHMEM_MMAP),
|
||||
NULL, NULL))
|
||||
{
|
||||
|
||||
@@ -49,7 +49,7 @@ struct app
|
||||
|
||||
uint8_t * frames;
|
||||
unsigned int frameSize;
|
||||
uint8_t * frame[MAX_FRAMES];
|
||||
FrameBuffer frame[MAX_FRAMES];
|
||||
unsigned int frameOffset[MAX_FRAMES];
|
||||
|
||||
bool running;
|
||||
@@ -168,9 +168,7 @@ static int frameThread(void * opaque)
|
||||
|
||||
while(app.running)
|
||||
{
|
||||
frame.data = app.frame[frameIndex];
|
||||
|
||||
switch(app.iface->getFrame(&frame))
|
||||
switch(app.iface->waitFrame(&frame))
|
||||
{
|
||||
case CAPTURE_RESULT_OK:
|
||||
break;
|
||||
@@ -226,7 +224,9 @@ static int frameThread(void * opaque)
|
||||
fi->dataPos = app.frameOffset[frameIndex];
|
||||
frameValid = true;
|
||||
|
||||
framebuffer_prepare(app.frame[frameIndex]);
|
||||
INTERLOCKED_OR8(&fi->flags, KVMFR_FRAME_FLAG_UPDATE);
|
||||
app.iface->getFrame(app.frame[frameIndex]);
|
||||
|
||||
if (++frameIndex == MAX_FRAMES)
|
||||
frameIndex = 0;
|
||||
@@ -363,14 +363,14 @@ int app_main(int argc, char * argv[])
|
||||
app.frames = (uint8_t *)ALIGN_UP(app.pointerData + app.pointerDataSize);
|
||||
app.frameSize = ALIGN_DN((shmemSize - (app.frames - shmemMap)) / MAX_FRAMES);
|
||||
|
||||
DEBUG_INFO("Max Cursor Size : %u MiB" , app.pointerDataSize / 1048576);
|
||||
DEBUG_INFO("Max Frame Size : %u MiB" , app.frameSize / 1048576);
|
||||
DEBUG_INFO("Max Cursor Size : %u MiB", app.pointerDataSize / 1048576);
|
||||
DEBUG_INFO("Max Frame Size : %u MiB", app.frameSize / 1048576);
|
||||
DEBUG_INFO("Cursor : 0x%" PRIXPTR " (0x%08x)", (uintptr_t)app.pointerData, app.pointerOffset);
|
||||
|
||||
for (int i = 0; i < MAX_FRAMES; ++i)
|
||||
{
|
||||
app.frame [i] = app.frames + i * app.frameSize;
|
||||
app.frameOffset[i] = app.frame[i] - shmemMap;
|
||||
app.frame [i] = (FrameBuffer)(app.frames + i * app.frameSize);
|
||||
app.frameOffset[i] = (uint8_t *)app.frame[i] - shmemMap;
|
||||
DEBUG_INFO("Frame %d : 0x%" PRIXPTR " (0x%08x)", i, (uintptr_t)app.frame[i], app.frameOffset[i]);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
#include "app.h"
|
||||
#include "common/KVMFR.h"
|
||||
#include "common/framebuffer.h"
|
||||
|
||||
#define IS_LG_RENDERER_VALID(x) \
|
||||
((x)->get_name && \
|
||||
@@ -89,7 +90,7 @@ typedef void (* LG_RendererDeInitialize)(void * opaque);
|
||||
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 LG_RendererFormat format, const uint8_t * data);
|
||||
typedef bool (* LG_RendererOnFrameEvent)(void * opaque, const LG_RendererFormat format, const FrameBuffer frame);
|
||||
typedef void (* LG_RendererOnAlert )(void * opaque, const LG_MsgAlert alert, const char * message, bool ** closeFlag);
|
||||
typedef bool (* LG_RendererRender )(void * opaque, SDL_Window *window);
|
||||
typedef void (* LG_RendererUpdateFPS )(void * opaque, const float avgUPS, const float avgFPS);
|
||||
|
||||
@@ -4,10 +4,13 @@ project(renderer_EGL LANGUAGES C)
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(RENDERER_EGL_PKGCONFIG REQUIRED
|
||||
egl
|
||||
wayland-egl
|
||||
gl
|
||||
)
|
||||
|
||||
pkg_check_modules(RENDERER_EGL_OPT_PKGCONFIG
|
||||
wayland-egl
|
||||
)
|
||||
|
||||
include(MakeObject)
|
||||
make_object(
|
||||
EGL_SHADER
|
||||
@@ -31,6 +34,7 @@ make_object(
|
||||
|
||||
add_library(renderer_EGL STATIC
|
||||
egl.c
|
||||
debug.c
|
||||
shader.c
|
||||
texture.c
|
||||
model.c
|
||||
@@ -45,6 +49,7 @@ add_library(renderer_EGL STATIC
|
||||
|
||||
target_link_libraries(renderer_EGL
|
||||
${RENDERER_EGL_PKGCONFIG_LIBRARIES}
|
||||
${RENDERER_EGL_OPT_PKGCONFIG_LIBRARIES}
|
||||
lg_common
|
||||
fonts
|
||||
)
|
||||
@@ -54,4 +59,5 @@ target_include_directories(renderer_EGL
|
||||
src
|
||||
${EGL_SHADER_INCS}
|
||||
${RENDERER_EGL_PKGCONFIG_INCLUDE_DIRS}
|
||||
${RENDERER_EGL_OPT_PKGCONFIG_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
@@ -207,24 +207,10 @@ void egl_cursor_render(EGL_Cursor * cursor)
|
||||
|
||||
uint8_t * data = cursor->data;
|
||||
|
||||
// tmp buffer for masked colour
|
||||
uint32_t tmp[cursor->width * cursor->height];
|
||||
|
||||
switch(cursor->type)
|
||||
{
|
||||
case LG_CURSOR_MASKED_COLOR:
|
||||
{
|
||||
for(int i = 0; i < cursor->width * cursor->height; ++i)
|
||||
{
|
||||
const uint32_t c = ((uint32_t *)data)[i];
|
||||
tmp[i] = (c & ~0xFF000000) | (c & 0xFF000000 ? 0x0 : 0xFF000000);
|
||||
}
|
||||
data = (uint8_t *)tmp;
|
||||
// fall through to LG_CURSOR_COLOR
|
||||
//
|
||||
// technically we should also create an XOR texture from the data but this
|
||||
// usage seems very rare in modern software.
|
||||
}
|
||||
// fall through
|
||||
|
||||
case LG_CURSOR_COLOR:
|
||||
{
|
||||
@@ -262,33 +248,42 @@ void egl_cursor_render(EGL_Cursor * cursor)
|
||||
LG_UNLOCK(cursor->lock);
|
||||
}
|
||||
|
||||
if (cursor->type == LG_CURSOR_MONOCHROME)
|
||||
glEnable(GL_BLEND);
|
||||
switch(cursor->type)
|
||||
{
|
||||
glEnable(GL_BLEND);
|
||||
case LG_CURSOR_MONOCHROME:
|
||||
{
|
||||
egl_shader_use(cursor->shader);
|
||||
glUniform4f(cursor->uMousePos, cursor->x, cursor->y, cursor->w, cursor->h / 2);
|
||||
glBlendFunc(GL_ZERO, GL_SRC_COLOR);
|
||||
egl_model_set_texture(cursor->model, cursor->texture);
|
||||
egl_model_render(cursor->model);
|
||||
|
||||
egl_shader_use(cursor->shader);
|
||||
glUniform4f(cursor->uMousePos, cursor->x, cursor->y, cursor->w, cursor->h / 2);
|
||||
glBlendFunc(GL_ZERO, GL_SRC_COLOR);
|
||||
egl_model_set_texture(cursor->model, cursor->texture);
|
||||
egl_model_render(cursor->model);
|
||||
egl_shader_use(cursor->shaderMono);
|
||||
glUniform4f(cursor->uMousePosMono, cursor->x, cursor->y, cursor->w, cursor->h / 2);
|
||||
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
|
||||
egl_model_set_texture(cursor->model, cursor->textureMono);
|
||||
egl_model_render(cursor->model);
|
||||
break;
|
||||
}
|
||||
|
||||
egl_shader_use(cursor->shaderMono);
|
||||
glUniform4f(cursor->uMousePosMono, cursor->x, cursor->y, cursor->w, cursor->h / 2);
|
||||
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
|
||||
egl_model_set_texture(cursor->model, cursor->textureMono);
|
||||
egl_model_render(cursor->model);
|
||||
case LG_CURSOR_COLOR:
|
||||
{
|
||||
egl_shader_use(cursor->shader);
|
||||
glUniform4f(cursor->uMousePos, cursor->x, cursor->y, cursor->w, cursor->h);
|
||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
egl_model_render(cursor->model);
|
||||
break;
|
||||
}
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
else
|
||||
{
|
||||
glEnable(GL_BLEND);
|
||||
|
||||
egl_shader_use(cursor->shader);
|
||||
glUniform4f(cursor->uMousePos, cursor->x, cursor->y, cursor->w, cursor->h);
|
||||
glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_ALPHA);
|
||||
egl_model_render(cursor->model);
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
case LG_CURSOR_MASKED_COLOR:
|
||||
{
|
||||
egl_shader_use(cursor->shaderMono);
|
||||
glUniform4f(cursor->uMousePos, cursor->x, cursor->y, cursor->w, cursor->h);
|
||||
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
|
||||
egl_model_render(cursor->model);
|
||||
break;
|
||||
}
|
||||
}
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
58
client/renderers/EGL/debug.c
Normal file
58
client/renderers/EGL/debug.c
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2019 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 <GL/gl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
void egl_debug_printf(char * format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vfprintf(stderr, format, args);
|
||||
va_end(args);
|
||||
|
||||
GLenum error = glGetError();
|
||||
switch(error)
|
||||
{
|
||||
case GL_NO_ERROR:
|
||||
fprintf(stderr, " (GL_NO_ERROR)\n");
|
||||
break;
|
||||
|
||||
case GL_INVALID_ENUM:
|
||||
fprintf(stderr, " (GL_INVALID_ENUM)\n");
|
||||
break;
|
||||
|
||||
case GL_INVALID_VALUE:
|
||||
fprintf(stderr, " (GL_INVALID_VALUE)\n");
|
||||
break;
|
||||
|
||||
case GL_INVALID_OPERATION:
|
||||
fprintf(stderr, " (GL_INVALID_OPERATION)\n");
|
||||
break;
|
||||
|
||||
case GL_INVALID_FRAMEBUFFER_OPERATION:
|
||||
fprintf(stderr, " (GL_INVALID_FRAMEBUFFER_OPERATION)\n");
|
||||
break;
|
||||
|
||||
case GL_OUT_OF_MEMORY:
|
||||
fprintf(stderr, " (GL_OUT_OF_MEMORY)\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
27
client/renderers/EGL/debug.h
Normal file
27
client/renderers/EGL/debug.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2019 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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <common/debug.h>
|
||||
|
||||
#define EGL_DEBUG_PRINT(type, fmt, ...) do {egl_debug_printf(type " %20s:%-4u | %-30s | " fmt, STRIPPATH(__FILE__), __LINE__, __FUNCTION__, ##__VA_ARGS__);} while (0)
|
||||
#define EGL_ERROR(fmt, ...) EGL_DEBUG_PRINT("[E]", fmt, ##__VA_ARGS__)
|
||||
|
||||
void egl_debug_printf(char * format, ...);
|
||||
@@ -60,7 +60,7 @@ struct EGL_Desktop
|
||||
enum EGL_PixelFormat pixFmt;
|
||||
unsigned int width, height;
|
||||
unsigned int pitch;
|
||||
const uint8_t * data;
|
||||
FrameBuffer frame;
|
||||
bool update;
|
||||
|
||||
// night vision
|
||||
@@ -181,7 +181,7 @@ void egl_desktop_free(EGL_Desktop ** desktop)
|
||||
*desktop = NULL;
|
||||
}
|
||||
|
||||
bool egl_desktop_prepare_update(EGL_Desktop * desktop, const bool sourceChanged, const LG_RendererFormat format, const uint8_t * data)
|
||||
bool egl_desktop_prepare_update(EGL_Desktop * desktop, const bool sourceChanged, const LG_RendererFormat format, const FrameBuffer frame)
|
||||
{
|
||||
if (sourceChanged)
|
||||
{
|
||||
@@ -217,7 +217,7 @@ bool egl_desktop_prepare_update(EGL_Desktop * desktop, const bool sourceChanged,
|
||||
desktop->width = format.width;
|
||||
desktop->height = format.height;
|
||||
desktop->pitch = format.pitch;
|
||||
desktop->data = data;
|
||||
desktop->frame = frame;
|
||||
desktop->update = true;
|
||||
|
||||
/* defer the actual update as the format has changed and we need to issue GL commands first */
|
||||
@@ -226,7 +226,7 @@ bool egl_desktop_prepare_update(EGL_Desktop * desktop, const bool sourceChanged,
|
||||
}
|
||||
|
||||
/* update the texture now */
|
||||
return egl_texture_update(desktop->texture, data);
|
||||
return egl_texture_update_from_frame(desktop->texture, frame);
|
||||
}
|
||||
|
||||
void egl_desktop_perform_update(EGL_Desktop * desktop, const bool sourceChanged)
|
||||
@@ -253,7 +253,7 @@ void egl_desktop_perform_update(EGL_Desktop * desktop, const bool sourceChanged)
|
||||
if (desktop->update)
|
||||
{
|
||||
desktop->update = false;
|
||||
egl_texture_update(desktop->texture, desktop->data);
|
||||
egl_texture_update_from_frame(desktop->texture, desktop->frame);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,6 @@ typedef struct EGL_Desktop EGL_Desktop;
|
||||
bool egl_desktop_init(EGL_Desktop ** desktop);
|
||||
void egl_desktop_free(EGL_Desktop ** desktop);
|
||||
|
||||
bool egl_desktop_prepare_update(EGL_Desktop * desktop, const bool sourceChanged, const LG_RendererFormat format, const uint8_t * data);
|
||||
bool egl_desktop_prepare_update(EGL_Desktop * desktop, const bool sourceChanged, const LG_RendererFormat format, const FrameBuffer frame);
|
||||
void egl_desktop_perform_update(EGL_Desktop * desktop, const bool sourceChanged);
|
||||
bool egl_desktop_render(EGL_Desktop * desktop, const float x, const float y, const float scaleX, const float scaleY, const bool nearest);
|
||||
@@ -296,7 +296,7 @@ bool egl_on_mouse_event(void * opaque, const bool visible, const int x, const in
|
||||
return true;
|
||||
}
|
||||
|
||||
bool egl_on_frame_event(void * opaque, const LG_RendererFormat format, const uint8_t * data)
|
||||
bool egl_on_frame_event(void * opaque, const LG_RendererFormat format, const FrameBuffer frame)
|
||||
{
|
||||
struct Inst * this = (struct Inst *)opaque;
|
||||
this->sourceChanged = (
|
||||
@@ -312,7 +312,7 @@ bool egl_on_frame_event(void * opaque, const LG_RendererFormat format, const uin
|
||||
|
||||
this->useNearest = this->width < format.width || this->height < format.height;
|
||||
|
||||
if (!egl_desktop_prepare_update(this->desktop, this->sourceChanged, format, data))
|
||||
if (!egl_desktop_prepare_update(this->desktop, this->sourceChanged, format, frame))
|
||||
{
|
||||
DEBUG_INFO("Failed to prepare to update the desktop");
|
||||
return false;
|
||||
|
||||
@@ -19,6 +19,9 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
#include "texture.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/locking.h"
|
||||
#include "common/framebuffer.h"
|
||||
#include "debug.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
@@ -43,14 +46,14 @@ struct EGL_Texture
|
||||
GLenum format;
|
||||
GLenum dataType;
|
||||
|
||||
bool hasPBO;
|
||||
GLuint pbo[2];
|
||||
int pboRIndex;
|
||||
int pboWIndex;
|
||||
int pboCount;
|
||||
size_t pboBufferSize;
|
||||
void * pboMap[2];
|
||||
GLsync pboSync[2];
|
||||
bool hasPBO;
|
||||
GLuint pbo[2];
|
||||
int pboRIndex;
|
||||
int pboWIndex;
|
||||
volatile int pboCount;
|
||||
size_t pboBufferSize;
|
||||
void * pboMap[2];
|
||||
GLsync pboSync[2];
|
||||
};
|
||||
|
||||
bool egl_texture_init(EGL_Texture ** texture)
|
||||
@@ -233,7 +236,7 @@ bool egl_texture_setup(EGL_Texture * texture, enum EGL_PixelFormat pixFmt, size_
|
||||
|
||||
if (!texture->pboMap[i])
|
||||
{
|
||||
DEBUG_ERROR("glMapBufferRange failed for %d of %lu bytes", i, texture->pboBufferSize);
|
||||
EGL_ERROR("glMapBufferRange failed for %d of %lu bytes", i, texture->pboBufferSize);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -258,7 +261,7 @@ bool egl_texture_update(EGL_Texture * texture, const uint8_t * buffer)
|
||||
|
||||
if (++texture->pboWIndex == 2)
|
||||
texture->pboWIndex = 0;
|
||||
++texture->pboCount;
|
||||
INTERLOCKED_INC(&texture->pboCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -276,6 +279,24 @@ bool egl_texture_update(EGL_Texture * texture, const uint8_t * buffer)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool egl_texture_update_from_frame(EGL_Texture * texture, const FrameBuffer frame)
|
||||
{
|
||||
if (!texture->streaming)
|
||||
return false;
|
||||
|
||||
if (texture->pboCount == 2)
|
||||
return true;
|
||||
|
||||
framebuffer_read(frame, texture->pboMap[texture->pboWIndex], texture->pboBufferSize);
|
||||
texture->pboSync[texture->pboWIndex] = 0;
|
||||
|
||||
if (++texture->pboWIndex == 2)
|
||||
texture->pboWIndex = 0;
|
||||
INTERLOCKED_INC(&texture->pboCount);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
enum EGL_TexStatus egl_texture_process(EGL_Texture * texture)
|
||||
{
|
||||
if (!texture->streaming)
|
||||
@@ -302,7 +323,7 @@ enum EGL_TexStatus egl_texture_process(EGL_Texture * texture)
|
||||
|
||||
/* wait for the buffer to be ready */
|
||||
pos = texture->pboRIndex;
|
||||
switch(glClientWaitSync(texture->pboSync[pos], GL_SYNC_FLUSH_COMMANDS_BIT, 0))
|
||||
switch(glClientWaitSync(texture->pboSync[pos], 0, 0))
|
||||
{
|
||||
case GL_ALREADY_SIGNALED:
|
||||
case GL_CONDITION_SATISFIED:
|
||||
@@ -313,7 +334,7 @@ enum EGL_TexStatus egl_texture_process(EGL_Texture * texture)
|
||||
|
||||
case GL_WAIT_FAILED:
|
||||
glDeleteSync(texture->pboSync[pos]);
|
||||
DEBUG_ERROR("glClientWaitSync failed");
|
||||
EGL_ERROR("glClientWaitSync failed");
|
||||
return EGL_TEX_STATUS_ERROR;
|
||||
}
|
||||
|
||||
@@ -336,7 +357,7 @@ enum EGL_TexStatus egl_texture_process(EGL_Texture * texture)
|
||||
/* advance the read index */
|
||||
if (++texture->pboRIndex == 2)
|
||||
texture->pboRIndex = 0;
|
||||
--texture->pboCount;
|
||||
INTERLOCKED_DEC(&texture->pboCount);
|
||||
|
||||
texture->ready = true;
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "shader.h"
|
||||
#include "common/framebuffer.h"
|
||||
|
||||
#include <GL/gl.h>
|
||||
|
||||
@@ -46,6 +47,7 @@ void egl_texture_free(EGL_Texture ** tex);
|
||||
|
||||
bool egl_texture_setup (EGL_Texture * texture, enum EGL_PixelFormat pixfmt, size_t width, size_t height, size_t stride, bool streaming);
|
||||
bool egl_texture_update (EGL_Texture * texture, const uint8_t * buffer);
|
||||
bool egl_texture_update_from_frame(EGL_Texture * texture, const FrameBuffer frame);
|
||||
enum EGL_TexStatus egl_texture_process(EGL_Texture * texture);
|
||||
enum EGL_TexStatus egl_texture_bind (EGL_Texture * texture);
|
||||
int egl_texture_count (EGL_Texture * texture);
|
||||
@@ -32,8 +32,8 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/option.h"
|
||||
#include "common/framebuffer.h"
|
||||
#include "utils.h"
|
||||
#include "lg-decoders.h"
|
||||
#include "dynamic/fonts.h"
|
||||
#include "ll.h"
|
||||
|
||||
@@ -122,8 +122,8 @@ struct Inst
|
||||
GLuint vboFormat;
|
||||
GLuint dataFormat;
|
||||
size_t texSize;
|
||||
const LG_Decoder* decoder;
|
||||
void * decoderData;
|
||||
size_t texPos;
|
||||
FrameBuffer frame;
|
||||
|
||||
uint64_t drawStart;
|
||||
bool hasBuffers;
|
||||
@@ -140,7 +140,6 @@ struct Inst
|
||||
bool hasTextures, hasFrames;
|
||||
GLuint frames[BUFFER_COUNT];
|
||||
GLsync fences[BUFFER_COUNT];
|
||||
void * decoderFrames[BUFFER_COUNT];
|
||||
GLuint textures[TEXTURE_COUNT];
|
||||
struct ll * alerts;
|
||||
int alertList;
|
||||
@@ -362,7 +361,7 @@ bool opengl_on_mouse_event(void * opaque, const bool visible, const int x, const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool opengl_on_frame_event(void * opaque, const LG_RendererFormat format, const uint8_t * data)
|
||||
bool opengl_on_frame_event(void * opaque, const LG_RendererFormat format, const FrameBuffer frame)
|
||||
{
|
||||
struct Inst * this = (struct Inst *)opaque;
|
||||
if (!this)
|
||||
@@ -394,12 +393,7 @@ bool opengl_on_frame_event(void * opaque, const LG_RendererFormat format, const
|
||||
LG_UNLOCK(this->formatLock);
|
||||
|
||||
LG_LOCK(this->syncLock);
|
||||
if (!this->decoder->decode(this->decoderData, data, format.pitch))
|
||||
{
|
||||
DEBUG_ERROR("decode returned failure");
|
||||
LG_UNLOCK(this->syncLock);
|
||||
return false;
|
||||
}
|
||||
this->frame = frame;
|
||||
this->frameUpdate = true;
|
||||
LG_UNLOCK(this->syncLock);
|
||||
|
||||
@@ -849,13 +843,21 @@ static bool configure(struct Inst * this, SDL_Window *window)
|
||||
switch(this->format.type)
|
||||
{
|
||||
case FRAME_TYPE_BGRA:
|
||||
case FRAME_TYPE_RGBA:
|
||||
case FRAME_TYPE_RGBA10:
|
||||
this->decoder = &LGD_NULL;
|
||||
this->intFormat = GL_RGBA8;
|
||||
this->vboFormat = GL_BGRA;
|
||||
this->dataFormat = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
|
||||
case FRAME_TYPE_YUV420:
|
||||
this->decoder = &LGD_YUV420;
|
||||
case FRAME_TYPE_RGBA:
|
||||
this->intFormat = GL_RGBA8;
|
||||
this->vboFormat = GL_RGBA;
|
||||
this->dataFormat = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
|
||||
case FRAME_TYPE_RGBA10:
|
||||
this->intFormat = GL_RGB10_A2;
|
||||
this->vboFormat = GL_RGBA;
|
||||
this->dataFormat = GL_UNSIGNED_INT_2_10_10_10_REV;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -863,128 +865,73 @@ static bool configure(struct Inst * this, SDL_Window *window)
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_INFO("Using decoder: %s", this->decoder->name);
|
||||
|
||||
if (!this->decoder->create(&this->decoderData))
|
||||
{
|
||||
DEBUG_ERROR("Failed to create the decoder");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this->decoder->initialize(
|
||||
this->decoderData,
|
||||
this->format,
|
||||
window))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize decoder");
|
||||
return false;
|
||||
}
|
||||
|
||||
switch(this->decoder->get_out_format(this->decoderData))
|
||||
{
|
||||
case LG_OUTPUT_BGRA:
|
||||
this->intFormat = GL_RGBA8;
|
||||
this->vboFormat = GL_BGRA;
|
||||
this->dataFormat = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
|
||||
case LG_OUTPUT_RGBA:
|
||||
this->intFormat = GL_RGBA8;
|
||||
this->vboFormat = GL_RGBA;
|
||||
this->dataFormat = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
|
||||
case LG_OUTPUT_RGBA10:
|
||||
this->intFormat = GL_RGB10_A2;
|
||||
this->vboFormat = GL_RGBA;
|
||||
this->dataFormat = GL_UNSIGNED_INT_2_10_10_10_REV;
|
||||
break;
|
||||
|
||||
case LG_OUTPUT_YUV420:
|
||||
// fixme
|
||||
this->intFormat = GL_RGBA8;
|
||||
this->vboFormat = GL_BGRA;
|
||||
this->dataFormat = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_ERROR("Format not supported");
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return false;
|
||||
}
|
||||
|
||||
// calculate the texture size in bytes
|
||||
this->texSize =
|
||||
this->format.height *
|
||||
this->decoder->get_frame_pitch(this->decoderData);
|
||||
this->texSize = this->format.height * this->format.pitch;
|
||||
this->texPos = 0;
|
||||
|
||||
// generate the pixel unpack buffers if the decoder isn't going to do it for us
|
||||
if (!this->decoder->has_gl)
|
||||
glGenBuffers(BUFFER_COUNT, this->vboID);
|
||||
if (check_gl_error("glGenBuffers"))
|
||||
{
|
||||
glGenBuffers(BUFFER_COUNT, this->vboID);
|
||||
if (check_gl_error("glGenBuffers"))
|
||||
{
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return false;
|
||||
}
|
||||
this->hasBuffers = true;
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return false;
|
||||
}
|
||||
this->hasBuffers = true;
|
||||
|
||||
if (this->amdPinnedMemSupport)
|
||||
{
|
||||
const int pagesize = getpagesize();
|
||||
this->texPixels[0] = memalign(pagesize, this->texSize * BUFFER_COUNT);
|
||||
memset(this->texPixels[0], 0, this->texSize * BUFFER_COUNT);
|
||||
for(int i = 1; i < BUFFER_COUNT; ++i)
|
||||
this->texPixels[i] = this->texPixels[0] + this->texSize;
|
||||
if (this->amdPinnedMemSupport)
|
||||
{
|
||||
const int pagesize = getpagesize();
|
||||
this->texPixels[0] = memalign(pagesize, this->texSize * BUFFER_COUNT);
|
||||
memset(this->texPixels[0], 0, this->texSize * BUFFER_COUNT);
|
||||
for(int i = 1; i < BUFFER_COUNT; ++i)
|
||||
this->texPixels[i] = this->texPixels[0] + this->texSize;
|
||||
|
||||
for(int i = 0; i < BUFFER_COUNT; ++i)
|
||||
for(int i = 0; i < BUFFER_COUNT; ++i)
|
||||
{
|
||||
glBindBuffer(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, this->vboID[i]);
|
||||
|
||||
if (check_gl_error("glBindBuffer"))
|
||||
{
|
||||
glBindBuffer(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, this->vboID[i]);
|
||||
|
||||
if (check_gl_error("glBindBuffer"))
|
||||
{
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return false;
|
||||
}
|
||||
glBufferData(
|
||||
GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD,
|
||||
this->texSize,
|
||||
this->texPixels[i],
|
||||
GL_STREAM_DRAW);
|
||||
|
||||
if (check_gl_error("glBufferData"))
|
||||
{
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return false;
|
||||
}
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return false;
|
||||
}
|
||||
glBindBuffer(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < BUFFER_COUNT; ++i)
|
||||
glBufferData(
|
||||
GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD,
|
||||
this->texSize,
|
||||
this->texPixels[i],
|
||||
GL_STREAM_DRAW);
|
||||
|
||||
if (check_gl_error("glBufferData"))
|
||||
{
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->vboID[i]);
|
||||
if (check_gl_error("glBindBuffer"))
|
||||
{
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return false;
|
||||
}
|
||||
|
||||
glBufferData(
|
||||
GL_PIXEL_UNPACK_BUFFER,
|
||||
this->texSize,
|
||||
NULL,
|
||||
GL_STREAM_DRAW
|
||||
);
|
||||
if (check_gl_error("glBufferData"))
|
||||
{
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return false;
|
||||
}
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return false;
|
||||
}
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
glBindBuffer(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < BUFFER_COUNT; ++i)
|
||||
{
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->vboID[i]);
|
||||
if (check_gl_error("glBindBuffer"))
|
||||
{
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return false;
|
||||
}
|
||||
|
||||
glBufferData(
|
||||
GL_PIXEL_UNPACK_BUFFER,
|
||||
this->texSize,
|
||||
NULL,
|
||||
GL_STREAM_DRAW
|
||||
);
|
||||
if (check_gl_error("glBufferData"))
|
||||
{
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
|
||||
// create the frame textures
|
||||
@@ -1023,26 +970,11 @@ static bool configure(struct Inst * this, SDL_Window *window)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this->decoder->has_gl)
|
||||
{
|
||||
if (!this->decoder->init_gl_texture(
|
||||
this->decoderData,
|
||||
GL_TEXTURE_2D,
|
||||
this->frames[i],
|
||||
&this->decoderFrames[i]))
|
||||
{
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
// 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);
|
||||
|
||||
// create the display lists
|
||||
glNewList(this->texList + i, GL_COMPILE);
|
||||
@@ -1082,19 +1014,6 @@ static void deconfigure(struct Inst * this)
|
||||
|
||||
if (this->hasFrames)
|
||||
{
|
||||
if (this->decoder->has_gl)
|
||||
{
|
||||
for(int i = 0; i < BUFFER_COUNT; ++i)
|
||||
{
|
||||
if (this->decoderFrames[i])
|
||||
this->decoder->free_gl_texture(
|
||||
this->decoderData,
|
||||
this->decoderFrames[i]
|
||||
);
|
||||
this->decoderFrames[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
glDeleteTextures(BUFFER_COUNT, this->frames);
|
||||
this->hasFrames = false;
|
||||
}
|
||||
@@ -1121,12 +1040,6 @@ static void deconfigure(struct Inst * this)
|
||||
}
|
||||
}
|
||||
|
||||
if (this->decoderData)
|
||||
{
|
||||
this->decoder->destroy(this->decoderData);
|
||||
this->decoderData = NULL;
|
||||
}
|
||||
|
||||
this->configured = false;
|
||||
}
|
||||
|
||||
@@ -1277,6 +1190,23 @@ static void update_mouse_shape(struct Inst * this, bool * newShape)
|
||||
LG_UNLOCK(this->mouseLock);
|
||||
}
|
||||
|
||||
static bool opengl_buffer_fn(void * opaque, const void * data, size_t size)
|
||||
{
|
||||
struct Inst * this = (struct Inst *)opaque;
|
||||
|
||||
// update the buffer, this performs a DMA transfer if possible
|
||||
glBufferSubData(
|
||||
GL_PIXEL_UNPACK_BUFFER,
|
||||
this->texPos,
|
||||
size,
|
||||
data
|
||||
);
|
||||
check_gl_error("glBufferSubData");
|
||||
|
||||
this->texPos += size;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool draw_frame(struct Inst * this)
|
||||
{
|
||||
LG_LOCK(this->syncLock);
|
||||
@@ -1293,96 +1223,70 @@ static bool draw_frame(struct Inst * this)
|
||||
LG_UNLOCK(this->syncLock);
|
||||
|
||||
LG_LOCK(this->formatLock);
|
||||
if (this->decoder->has_gl)
|
||||
if (glIsSync(this->fences[this->texIndex]))
|
||||
{
|
||||
if (!this->decoder->update_gl_texture(
|
||||
this->decoderData,
|
||||
this->decoderFrames[this->texIndex]
|
||||
))
|
||||
switch(glClientWaitSync(this->fences[this->texIndex], 0, GL_TIMEOUT_IGNORED))
|
||||
{
|
||||
LG_UNLOCK(this->formatLock);
|
||||
DEBUG_ERROR("Failed to update the texture from the decoder");
|
||||
return false;
|
||||
case GL_ALREADY_SIGNALED:
|
||||
break;
|
||||
|
||||
case GL_CONDITION_SATISFIED:
|
||||
DEBUG_WARN("Had to wait for the sync");
|
||||
break;
|
||||
|
||||
case GL_TIMEOUT_EXPIRED:
|
||||
DEBUG_WARN("Timeout expired, DMA transfers are too slow!");
|
||||
break;
|
||||
|
||||
case GL_WAIT_FAILED:
|
||||
DEBUG_ERROR("Wait failed %s", gluErrorString(glGetError()));
|
||||
break;
|
||||
}
|
||||
|
||||
glDeleteSync(this->fences[this->texIndex]);
|
||||
this->fences[this->texIndex] = NULL;
|
||||
}
|
||||
else
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, this->frames[this->texIndex]);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->vboID[this->texIndex]);
|
||||
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT , 4);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH , this->format.stride);
|
||||
|
||||
this->texPos = 0;
|
||||
framebuffer_read_fn(
|
||||
this->frame,
|
||||
opengl_buffer_fn,
|
||||
this->format.height * this->format.stride,
|
||||
this
|
||||
);
|
||||
|
||||
// update the texture
|
||||
glTexSubImage2D(
|
||||
GL_TEXTURE_2D,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
this->format.width ,
|
||||
this->format.height,
|
||||
this->vboFormat,
|
||||
this->dataFormat,
|
||||
(void*)0
|
||||
);
|
||||
if (check_gl_error("glTexSubImage2D"))
|
||||
{
|
||||
if (glIsSync(this->fences[this->texIndex]))
|
||||
{
|
||||
switch(glClientWaitSync(this->fences[this->texIndex], 0, GL_TIMEOUT_IGNORED))
|
||||
{
|
||||
case GL_ALREADY_SIGNALED:
|
||||
break;
|
||||
|
||||
case GL_CONDITION_SATISFIED:
|
||||
DEBUG_WARN("Had to wait for the sync");
|
||||
break;
|
||||
|
||||
case GL_TIMEOUT_EXPIRED:
|
||||
DEBUG_WARN("Timeout expired, DMA transfers are too slow!");
|
||||
break;
|
||||
|
||||
case GL_WAIT_FAILED:
|
||||
DEBUG_ERROR("Wait failed %s", gluErrorString(glGetError()));
|
||||
break;
|
||||
}
|
||||
|
||||
glDeleteSync(this->fences[this->texIndex]);
|
||||
this->fences[this->texIndex] = NULL;
|
||||
}
|
||||
|
||||
const uint8_t * data = this->decoder->get_buffer(this->decoderData);
|
||||
if (!data)
|
||||
{
|
||||
LG_UNLOCK(this->formatLock);
|
||||
DEBUG_ERROR("Failed to get the buffer from the decoder");
|
||||
return false;
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, this->frames[this->texIndex]);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->vboID[this->texIndex]);
|
||||
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT , 4);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH ,
|
||||
this->decoder->get_frame_stride(this->decoderData)
|
||||
DEBUG_ERROR("texIndex: %u, width: %u, height: %u, vboFormat: %x, texSize: %lu",
|
||||
this->texIndex, this->format.width, this->format.height, this->vboFormat, this->texSize
|
||||
);
|
||||
|
||||
// update the buffer, this performs a DMA transfer if possible
|
||||
glBufferSubData(
|
||||
GL_PIXEL_UNPACK_BUFFER,
|
||||
0,
|
||||
this->texSize,
|
||||
data
|
||||
);
|
||||
check_gl_error("glBufferSubData");
|
||||
|
||||
// update the texture
|
||||
glTexSubImage2D(
|
||||
GL_TEXTURE_2D,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
this->format.width ,
|
||||
this->format.height,
|
||||
this->vboFormat,
|
||||
this->dataFormat,
|
||||
(void*)0
|
||||
);
|
||||
if (check_gl_error("glTexSubImage2D"))
|
||||
{
|
||||
DEBUG_ERROR("texIndex: %u, width: %u, height: %u, vboFormat: %x, texSize: %lu",
|
||||
this->texIndex, this->format.width, this->format.height, this->vboFormat, this->texSize
|
||||
);
|
||||
}
|
||||
|
||||
// set a fence so we don't overwrite a buffer in use
|
||||
this->fences[this->texIndex] =
|
||||
glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
|
||||
// unbind the buffer
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
|
||||
// set a fence so we don't overwrite a buffer in use
|
||||
this->fences[this->texIndex] =
|
||||
glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
|
||||
// unbind the buffer
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
|
||||
const bool mipmap = this->opt.mipmap && (
|
||||
(this->format.width > this->destRect.w) ||
|
||||
(this->format.height > this->destRect.h));
|
||||
|
||||
@@ -48,7 +48,11 @@ static int cursorThread(void * unused);
|
||||
static int renderThread(void * unused);
|
||||
static int frameThread (void * unused);
|
||||
|
||||
struct AppState state;
|
||||
static SDL_Thread *t_spice = NULL;
|
||||
static SDL_Thread *t_render = NULL;
|
||||
static SDL_Cursor *cursor = NULL;
|
||||
|
||||
struct AppState state;
|
||||
|
||||
// this structure is initialized in config.c
|
||||
struct AppParams params = { 0 };
|
||||
@@ -155,7 +159,15 @@ static int renderThread(void * unused)
|
||||
}
|
||||
|
||||
state.running = false;
|
||||
SDL_WaitThread(t_cursor, NULL);
|
||||
|
||||
if (t_cursor)
|
||||
SDL_WaitThread(t_cursor, NULL);
|
||||
|
||||
if (state.t_frame)
|
||||
SDL_WaitThread(state.t_frame, NULL);
|
||||
|
||||
state.lgr->deinitialize(state.lgrData);
|
||||
state.lgr = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -368,9 +380,8 @@ static int frameThread(void * unused)
|
||||
SDL_SetWindowSize(state.window, header.width, header.height);
|
||||
updatePositionInfo();
|
||||
}
|
||||
|
||||
const uint8_t * data = (const uint8_t *)state.shm + header.dataPos;
|
||||
if (!state.lgr->on_frame_event(state.lgrData, lgrFormat, data))
|
||||
FrameBuffer frame = (FrameBuffer)((uint8_t *)state.shm + header.dataPos);
|
||||
if (!state.lgr->on_frame_event(state.lgrData, lgrFormat, frame))
|
||||
{
|
||||
DEBUG_ERROR("renderer on frame event returned failure");
|
||||
break;
|
||||
@@ -582,7 +593,10 @@ int eventFilter(void * userdata, SDL_Event * event)
|
||||
case SDL_QUIT:
|
||||
{
|
||||
if (!params.ignoreQuit)
|
||||
{
|
||||
DEBUG_INFO("Quit event received, exiting...");
|
||||
state.running = false;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -991,11 +1005,8 @@ static void release_key_binds()
|
||||
app_release_keybind(&state.kbCtrlAltFn[i]);
|
||||
}
|
||||
|
||||
int run()
|
||||
static int lg_run()
|
||||
{
|
||||
DEBUG_INFO("Looking Glass (" BUILD_VERSION ")");
|
||||
DEBUG_INFO("Locking Method: " LG_LOCK_MODE);
|
||||
|
||||
memset(&state, 0, sizeof(state));
|
||||
state.running = true;
|
||||
state.scaleX = 1.0f;
|
||||
@@ -1044,6 +1055,45 @@ int run()
|
||||
signal(SIGINT , int_handler);
|
||||
signal(SIGTERM, int_handler);
|
||||
|
||||
// try map the shared memory
|
||||
state.shm = (struct KVMFRHeader *)map_memory();
|
||||
if (!state.shm)
|
||||
{
|
||||
DEBUG_ERROR("Failed to map memory");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// try to connect to the spice server
|
||||
if (params.useSpiceInput || params.useSpiceClipboard)
|
||||
{
|
||||
spice_set_clipboard_cb(
|
||||
spiceClipboardNotice,
|
||||
spiceClipboardData,
|
||||
spiceClipboardRelease,
|
||||
spiceClipboardRequest);
|
||||
|
||||
if (!spice_connect(params.spiceHost, params.spicePort, ""))
|
||||
{
|
||||
DEBUG_ERROR("Failed to connect to spice server");
|
||||
return -1;
|
||||
}
|
||||
|
||||
while(state.running && !spice_ready())
|
||||
if (!spice_process())
|
||||
{
|
||||
state.running = false;
|
||||
DEBUG_ERROR("Failed to process spice messages");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(t_spice = SDL_CreateThread(spiceThread, "spiceThread", NULL)))
|
||||
{
|
||||
DEBUG_ERROR("spice create thread failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// select and init a renderer
|
||||
LG_RendererParams lgrParams;
|
||||
lgrParams.showFPS = params.showFPS;
|
||||
Uint32 sdlFlags;
|
||||
@@ -1079,6 +1129,7 @@ int run()
|
||||
return -1;
|
||||
}
|
||||
|
||||
// all our ducks are in a line, create the window
|
||||
state.window = SDL_CreateWindow(
|
||||
params.windowTitle,
|
||||
params.center ? SDL_WINDOWPOS_CENTERED : params.x,
|
||||
@@ -1127,18 +1178,18 @@ int run()
|
||||
{
|
||||
state.frameTime = 1e9 / (current.refresh_rate * 2);
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
DEBUG_WARN("Unable to capture monitor refresh rate using the default FPS Limit: 200");
|
||||
state.frameTime = 1e9 / 200;
|
||||
}
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
DEBUG_INFO("Using the FPS Limit from args: %d", params.fpsLimit);
|
||||
state.frameTime = 1e9 / params.fpsLimit;
|
||||
}
|
||||
|
||||
|
||||
register_key_binds();
|
||||
|
||||
// set the compositor hint to bypass for low latency
|
||||
@@ -1172,12 +1223,6 @@ int run()
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!state.window)
|
||||
{
|
||||
DEBUG_ERROR("failed to create window");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (state.lgc)
|
||||
{
|
||||
DEBUG_INFO("Using Clipboard: %s", state.lgc->getName());
|
||||
@@ -1190,7 +1235,6 @@ int run()
|
||||
state.cbRequestList = ll_new();
|
||||
}
|
||||
|
||||
SDL_Cursor *cursor = NULL;
|
||||
if (params.hideMouse)
|
||||
{
|
||||
// work around SDL_ShowCursor being non functional
|
||||
@@ -1200,132 +1244,89 @@ int run()
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
}
|
||||
|
||||
SDL_Thread *t_spice = NULL;
|
||||
SDL_Thread *t_frame = NULL;
|
||||
SDL_Thread *t_render = NULL;
|
||||
|
||||
while(1)
|
||||
// start the renderThread so we don't just display junk
|
||||
if (!(t_render = SDL_CreateThread(renderThread, "renderThread", NULL)))
|
||||
{
|
||||
state.shm = (struct KVMFRHeader *)map_memory();
|
||||
if (!state.shm)
|
||||
{
|
||||
DEBUG_ERROR("Failed to map memory");
|
||||
break;
|
||||
}
|
||||
|
||||
// start the renderThread so we don't just display junk
|
||||
if (!(t_render = SDL_CreateThread(renderThread, "renderThread", NULL)))
|
||||
{
|
||||
DEBUG_ERROR("render create thread failed");
|
||||
break;
|
||||
}
|
||||
|
||||
if (params.useSpiceInput || params.useSpiceClipboard)
|
||||
{
|
||||
spice_set_clipboard_cb(
|
||||
spiceClipboardNotice,
|
||||
spiceClipboardData,
|
||||
spiceClipboardRelease,
|
||||
spiceClipboardRequest);
|
||||
|
||||
if (!spice_connect(params.spiceHost, params.spicePort, ""))
|
||||
{
|
||||
DEBUG_ERROR("Failed to connect to spice server");
|
||||
return 0;
|
||||
}
|
||||
|
||||
while(state.running && !spice_ready())
|
||||
if (!spice_process())
|
||||
{
|
||||
state.running = false;
|
||||
DEBUG_ERROR("Failed to process spice messages");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(t_spice = SDL_CreateThread(spiceThread, "spiceThread", NULL)))
|
||||
{
|
||||
DEBUG_ERROR("spice create thread failed");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ensure mouse acceleration is identical in server mode
|
||||
SDL_SetHintWithPriority(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, "1", SDL_HINT_OVERRIDE);
|
||||
SDL_SetEventFilter(eventFilter, 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))
|
||||
SDL_WaitEventTimeout(NULL, 1000);
|
||||
|
||||
if (!state.running)
|
||||
break;
|
||||
|
||||
DEBUG_INFO("Host ready, starting session");
|
||||
|
||||
// check the header's magic and version are valid
|
||||
if (memcmp(state.shm->magic, KVMFR_HEADER_MAGIC, sizeof(KVMFR_HEADER_MAGIC)) != 0)
|
||||
{
|
||||
DEBUG_ERROR("Invalid header magic, is the host running?");
|
||||
break;
|
||||
}
|
||||
|
||||
if (state.shm->version != KVMFR_HEADER_VERSION)
|
||||
{
|
||||
DEBUG_ERROR("KVMFR version missmatch, expected %u but got %u", KVMFR_HEADER_VERSION, state.shm->version);
|
||||
DEBUG_ERROR("This is not a bug, ensure you have the right version of looking-glass-host.exe on the guest");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(t_frame = SDL_CreateThread(frameThread, "frameThread", NULL)))
|
||||
{
|
||||
DEBUG_ERROR("frame create thread failed");
|
||||
break;
|
||||
}
|
||||
|
||||
bool *closeAlert = NULL;
|
||||
while(state.running)
|
||||
{
|
||||
SDL_WaitEventTimeout(NULL, 1000);
|
||||
|
||||
if (closeAlert == NULL)
|
||||
{
|
||||
if (state.shm->flags & KVMFR_HEADER_FLAG_PAUSED)
|
||||
{
|
||||
if (state.lgr && params.showAlerts)
|
||||
state.lgr->on_alert(
|
||||
state.lgrData,
|
||||
LG_ALERT_WARNING,
|
||||
"Stream Paused",
|
||||
&closeAlert
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(state.shm->flags & KVMFR_HEADER_FLAG_PAUSED))
|
||||
{
|
||||
*closeAlert = true;
|
||||
closeAlert = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
DEBUG_ERROR("render create thread failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// ensure mouse acceleration is identical in server mode
|
||||
SDL_SetHintWithPriority(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, "1", SDL_HINT_OVERRIDE);
|
||||
SDL_SetEventFilter(eventFilter, 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))
|
||||
SDL_WaitEventTimeout(NULL, 1000);
|
||||
|
||||
if (!state.running)
|
||||
return -1;
|
||||
|
||||
DEBUG_INFO("Host ready, starting session");
|
||||
|
||||
// check the header's magic and version are valid
|
||||
if (memcmp(state.shm->magic, KVMFR_HEADER_MAGIC, sizeof(KVMFR_HEADER_MAGIC)) != 0)
|
||||
{
|
||||
DEBUG_ERROR("Invalid header magic, is the host running?");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (state.shm->version != KVMFR_HEADER_VERSION)
|
||||
{
|
||||
DEBUG_ERROR("KVMFR version missmatch, expected %u but got %u", KVMFR_HEADER_VERSION, state.shm->version);
|
||||
DEBUG_ERROR("This is not a bug, ensure you have the right version of looking-glass-host.exe on the guest");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(state.t_frame = SDL_CreateThread(frameThread, "frameThread", NULL)))
|
||||
{
|
||||
DEBUG_ERROR("frame create thread failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool *closeAlert = NULL;
|
||||
while(state.running)
|
||||
{
|
||||
SDL_WaitEventTimeout(NULL, 1000);
|
||||
|
||||
if (closeAlert == NULL)
|
||||
{
|
||||
if (state.shm->flags & KVMFR_HEADER_FLAG_PAUSED)
|
||||
{
|
||||
if (state.lgr && params.showAlerts)
|
||||
state.lgr->on_alert(
|
||||
state.lgrData,
|
||||
LG_ALERT_WARNING,
|
||||
"Stream Paused",
|
||||
&closeAlert
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(state.shm->flags & KVMFR_HEADER_FLAG_PAUSED))
|
||||
{
|
||||
*closeAlert = true;
|
||||
closeAlert = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lg_shutdown()
|
||||
{
|
||||
state.running = false;
|
||||
|
||||
if (t_render)
|
||||
SDL_WaitThread(t_render, NULL);
|
||||
|
||||
if (t_frame)
|
||||
SDL_WaitThread(t_frame, NULL);
|
||||
|
||||
// if spice is still connected send key up events for any pressed keys
|
||||
if (params.useSpiceInput && spice_ready())
|
||||
{
|
||||
@@ -1345,9 +1346,6 @@ int run()
|
||||
spice_disconnect();
|
||||
}
|
||||
|
||||
if (state.lgr)
|
||||
state.lgr->deinitialize(state.lgrData);
|
||||
|
||||
if (state.lgc)
|
||||
{
|
||||
state.lgc->free();
|
||||
@@ -1370,12 +1368,15 @@ int run()
|
||||
close(state.shmFD);
|
||||
}
|
||||
|
||||
release_key_binds();
|
||||
SDL_Quit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
DEBUG_INFO("Looking Glass (" BUILD_VERSION ")");
|
||||
DEBUG_INFO("Locking Method: " LG_LOCK_MODE);
|
||||
|
||||
if (!installCrashHandler("/proc/self/exe"))
|
||||
DEBUG_WARN("Failed to install the crash handler");
|
||||
|
||||
@@ -1391,9 +1392,10 @@ int main(int argc, char * argv[])
|
||||
if (params.grabKeyboard)
|
||||
SDL_SetHint(SDL_HINT_GRAB_KEYBOARD, "1");
|
||||
|
||||
const int ret = run();
|
||||
release_key_binds();
|
||||
const int ret = lg_run();
|
||||
lg_shutdown();
|
||||
|
||||
config_free();
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -49,6 +49,8 @@ struct AppState
|
||||
void * lgrData;
|
||||
bool lgrResize;
|
||||
|
||||
SDL_Thread * t_frame;
|
||||
|
||||
const LG_Clipboard * lgc;
|
||||
SpiceDataType cbType;
|
||||
struct ll * cbRequestList;
|
||||
@@ -130,4 +132,4 @@ struct KeybindHandle
|
||||
|
||||
// forwards
|
||||
extern struct AppState state;
|
||||
extern struct AppParams params;
|
||||
extern struct AppParams params;
|
||||
@@ -10,9 +10,10 @@ if(ENABLE_BACKTRACE)
|
||||
endif()
|
||||
|
||||
set(COMMON_SOURCES
|
||||
src/objectlist.c
|
||||
src/stringutils.c
|
||||
src/stringlist.c
|
||||
src/option.c
|
||||
src/framebuffer.c
|
||||
)
|
||||
|
||||
set(LINUX_SOURCES
|
||||
|
||||
@@ -21,7 +21,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include <stdint.h>
|
||||
|
||||
#define KVMFR_HEADER_MAGIC "[[KVMFR]]"
|
||||
#define KVMFR_HEADER_VERSION 8
|
||||
#define KVMFR_HEADER_VERSION 9
|
||||
|
||||
typedef enum FrameType
|
||||
{
|
||||
@@ -49,7 +49,7 @@ CursorType;
|
||||
|
||||
typedef struct KVMFRCursor
|
||||
{
|
||||
uint8_t flags; // KVMFR_CURSOR_FLAGS
|
||||
volatile uint8_t flags; // KVMFR_CURSOR_FLAGS
|
||||
int16_t x, y; // cursor x & y position
|
||||
|
||||
uint32_t version; // shape version
|
||||
@@ -65,7 +65,7 @@ KVMFRCursor;
|
||||
|
||||
typedef struct KVMFRFrame
|
||||
{
|
||||
uint8_t flags; // KVMFR_FRAME_FLAGS
|
||||
volatile uint8_t flags; // KVMFR_FRAME_FLAGS
|
||||
FrameType type; // the frame data type
|
||||
uint32_t width; // the width
|
||||
uint32_t height; // the height
|
||||
@@ -83,7 +83,7 @@ typedef struct KVMFRHeader
|
||||
{
|
||||
char magic[sizeof(KVMFR_HEADER_MAGIC)];
|
||||
uint32_t version; // version of this structure
|
||||
uint8_t flags; // KVMFR_HEADER_FLAGS
|
||||
volatile uint8_t flags; // KVMFR_HEADER_FLAGS
|
||||
KVMFRFrame frame; // the frame information
|
||||
KVMFRCursor cursor; // the cursor information
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if defined(_WIN32) && !defined(__GNUC__)
|
||||
#define DIRECTORY_SEPARATOR '\\'
|
||||
@@ -53,6 +54,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#define DEBUG_WARN(fmt, ...) DEBUG_PRINT("[W]", fmt, ##__VA_ARGS__)
|
||||
#define DEBUG_ERROR(fmt, ...) DEBUG_PRINT("[E]", fmt, ##__VA_ARGS__)
|
||||
#define DEBUG_FIXME(fmt, ...) DEBUG_PRINT("[F]", fmt, ##__VA_ARGS__)
|
||||
#define DEBUG_FATAL(fmt, ...) do {DEBUG_PRINT("[C]", fmt, ##__VA_ARGS__); abort();} while (0)
|
||||
|
||||
#if defined(DEBUG_SPICE) | defined(DEBUG_IVSHMEM)
|
||||
#define DEBUG_PROTO(fmt, args...) DEBUG_PRINT("[P]", fmt, ##args)
|
||||
|
||||
48
common/include/common/framebuffer.h
Normal file
48
common/include/common/framebuffer.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
KVMGFX Client - A KVM Client for VGA Passthrough
|
||||
Copyright (C) 2017-2019 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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct stFrameBuffer * FrameBuffer;
|
||||
|
||||
typedef bool (*FrameBufferReadFn)(void * opaque, const void * src, size_t size);
|
||||
|
||||
/**
|
||||
* Read data from the KVMFRFrame into the dst buffer
|
||||
*/
|
||||
bool framebuffer_read(const FrameBuffer frame, void * dst, size_t size);
|
||||
|
||||
/**
|
||||
* Read data from the KVMFRFrame using a callback
|
||||
*/
|
||||
bool framebuffer_read_fn(const FrameBuffer frame, FrameBufferReadFn fn, size_t size, void * opaque);
|
||||
|
||||
/**
|
||||
* Prepare the framebuffer for writing
|
||||
*/
|
||||
void framebuffer_prepare(const FrameBuffer frame);
|
||||
|
||||
/**
|
||||
* Write data from the src buffer into the KVMFRFrame
|
||||
*/
|
||||
bool framebuffer_write(const FrameBuffer frame, const void * src, size_t size);
|
||||
@@ -19,9 +19,13 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#pragma once
|
||||
|
||||
#if defined(__GCC__) || defined(__GNUC__)
|
||||
#define INTERLOCKED_AND8 __sync_fetch_and_and
|
||||
#define INTERLOCKED_OR8 __sync_fetch_and_or
|
||||
#define INTERLOCKED_AND8 __sync_fetch_and_and
|
||||
#define INTERLOCKED_OR8 __sync_fetch_and_or
|
||||
#define INTERLOCKED_INC(x) __sync_fetch_and_add((x), 1)
|
||||
#define INTERLOCKED_DEC(x) __sync_fetch_and_sub((x), 1)
|
||||
#else
|
||||
#define INTERLOCKED_OR8 InterlockedOr8
|
||||
#define INTERLOCKED_OR8 InterlockedOr8
|
||||
#define INTERLOCKED_AND8 InterlockedAnd8
|
||||
#define INTERLOCKED_INC InterlockedIncrement
|
||||
#define INTERLOCKED_DEC InterlockedDecrement
|
||||
#endif
|
||||
35
common/include/common/objectlist.h
Normal file
35
common/include/common/objectlist.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
KVMGFX Client - A KVM Client for VGA Passthrough
|
||||
Copyright (C) 2017-2019 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 <stdbool.h>
|
||||
|
||||
typedef struct ObjectList * ObjectList;
|
||||
|
||||
typedef void (*ObjectFreeFn)(void * object);
|
||||
|
||||
ObjectList objectlist_new (ObjectFreeFn free_fn);
|
||||
void objectlist_free (ObjectList * ol);
|
||||
int objectlist_push (ObjectList ol, void * object);
|
||||
void * objectlist_pop (ObjectList ol);
|
||||
bool objectlist_remove(ObjectList ol, unsigned int index);
|
||||
unsigned int objectlist_count (ObjectList ol);
|
||||
void * objectlist_at (ObjectList ol, unsigned int index);
|
||||
|
||||
// generic free method
|
||||
void objectlist_free_item(void *object);
|
||||
@@ -17,12 +17,31 @@ this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "objectlist.h"
|
||||
|
||||
typedef struct StringList * StringList;
|
||||
typedef ObjectList StringList;
|
||||
|
||||
StringList stringlist_new (bool owns_strings);
|
||||
void stringlist_free (StringList * sl);
|
||||
int stringlist_push (StringList sl, char * str);
|
||||
unsigned int stringlist_count(StringList sl);
|
||||
char * stringlist_at (StringList sl, unsigned int index);
|
||||
inline static StringList stringlist_new(bool owns_strings)
|
||||
{
|
||||
return objectlist_new(owns_strings ? objectlist_free_item : 0);
|
||||
}
|
||||
|
||||
inline static void stringlist_free(StringList * sl)
|
||||
{
|
||||
return objectlist_free(sl);
|
||||
}
|
||||
|
||||
inline static int stringlist_push (StringList sl, char * str)
|
||||
{
|
||||
return objectlist_push(sl, str);
|
||||
}
|
||||
|
||||
inline static unsigned int stringlist_count(StringList sl)
|
||||
{
|
||||
return objectlist_count(sl);
|
||||
}
|
||||
|
||||
inline static char * stringlist_at(StringList sl, unsigned int index)
|
||||
{
|
||||
return objectlist_at(sl, index);
|
||||
}
|
||||
95
common/src/framebuffer.c
Normal file
95
common/src/framebuffer.c
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
KVMGFX Client - A KVM Client for VGA Passthrough
|
||||
Copyright (C) 2017-2019 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 "common/framebuffer.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
#include <string.h>
|
||||
#define FB_CHUNK_SIZE 1024
|
||||
|
||||
struct stFrameBuffer
|
||||
{
|
||||
uint64_t wp;
|
||||
uint8_t data[0];
|
||||
};
|
||||
|
||||
bool framebuffer_read(const FrameBuffer frame, void * dst, size_t size)
|
||||
{
|
||||
uint8_t *d = (uint8_t*)dst;
|
||||
uint64_t rp = 0;
|
||||
while(rp < size)
|
||||
{
|
||||
/* spinlock */
|
||||
while(rp == frame->wp) { }
|
||||
|
||||
/* copy what we can */
|
||||
uint64_t avail = frame->wp - rp;
|
||||
avail = avail > size ? size : avail;
|
||||
|
||||
memcpy(d, frame->data + rp, avail);
|
||||
|
||||
rp += avail;
|
||||
d += avail;
|
||||
size -= avail;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool framebuffer_read_fn(const FrameBuffer frame, FrameBufferReadFn fn, size_t size, void * opaque)
|
||||
{
|
||||
uint64_t rp = 0;
|
||||
while(rp < size)
|
||||
{
|
||||
/* spinlock */
|
||||
while(rp == frame->wp) { }
|
||||
|
||||
/* copy what we can */
|
||||
uint64_t avail = frame->wp - rp;
|
||||
avail = avail > size ? size : avail;
|
||||
|
||||
if (!fn(opaque, frame->data + rp, avail))
|
||||
return false;
|
||||
|
||||
rp += avail;
|
||||
size -= avail;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the framebuffer for writing
|
||||
*/
|
||||
void framebuffer_prepare(const FrameBuffer frame)
|
||||
{
|
||||
frame->wp = 0;
|
||||
}
|
||||
|
||||
bool framebuffer_write(FrameBuffer frame, const void * src, size_t size)
|
||||
{
|
||||
/* copy in chunks */
|
||||
while(size)
|
||||
{
|
||||
size_t copy = size < FB_CHUNK_SIZE ? FB_CHUNK_SIZE : size;
|
||||
memcpy(frame->data + frame->wp, src, copy);
|
||||
__sync_fetch_and_add(&frame->wp, copy);
|
||||
size -= copy;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
112
common/src/objectlist.c
Normal file
112
common/src/objectlist.c
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
KVMGFX Client - A KVM Client for VGA Passthrough
|
||||
Copyright (C) 2017-2019 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 "common/objectlist.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
struct ObjectList
|
||||
{
|
||||
ObjectFreeFn free_fn;
|
||||
unsigned int size;
|
||||
unsigned int count;
|
||||
void ** list;
|
||||
};
|
||||
|
||||
ObjectList objectlist_new(ObjectFreeFn free_fn)
|
||||
{
|
||||
ObjectList ol = malloc(sizeof(struct ObjectList));
|
||||
|
||||
ol->free_fn = free_fn;
|
||||
ol->size = 32;
|
||||
ol->count = 0;
|
||||
ol->list = malloc(sizeof(void *) * ol->size);
|
||||
|
||||
return ol;
|
||||
}
|
||||
|
||||
void objectlist_free(ObjectList * ol)
|
||||
{
|
||||
if ((*ol)->free_fn)
|
||||
for(unsigned int i = 0; i < (*ol)->count; ++i)
|
||||
(*ol)->free_fn((*ol)->list[i]);
|
||||
|
||||
free((*ol)->list);
|
||||
free((*ol));
|
||||
*ol = NULL;
|
||||
}
|
||||
|
||||
int objectlist_push(ObjectList ol, void * object)
|
||||
{
|
||||
if (ol->count == ol->size)
|
||||
{
|
||||
ol->size += 32;
|
||||
ol->list = realloc(ol->list, sizeof(void *) * ol->size);
|
||||
}
|
||||
|
||||
unsigned int index = ol->count;
|
||||
ol->list[ol->count++] = object;
|
||||
return index;
|
||||
}
|
||||
|
||||
void * objectlist_pop(ObjectList ol)
|
||||
{
|
||||
if (ol->count == 0)
|
||||
return NULL;
|
||||
|
||||
return ol->list[--ol->count];
|
||||
}
|
||||
|
||||
bool objectlist_remove(ObjectList ol, unsigned int index)
|
||||
{
|
||||
if (index >= ol->count)
|
||||
return false;
|
||||
|
||||
if (ol->free_fn)
|
||||
ol->free_fn(ol->list[index]);
|
||||
|
||||
void ** newlist = malloc(sizeof(void *) * ol->size);
|
||||
|
||||
--ol->count;
|
||||
memcpy(&newlist[0], &ol->list[0], index * sizeof(void *));
|
||||
memcpy(&newlist[index], &ol->list[index + 1], (ol->count - index) * sizeof(void *));
|
||||
|
||||
free(ol->list);
|
||||
ol->list = newlist;
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int objectlist_count(ObjectList ol)
|
||||
{
|
||||
return ol->count;
|
||||
}
|
||||
|
||||
void * objectlist_at(ObjectList ol, unsigned int index)
|
||||
{
|
||||
if (index >= ol->count)
|
||||
return NULL;
|
||||
|
||||
return ol->list[index];
|
||||
}
|
||||
|
||||
void objectlist_free_item(void *object)
|
||||
{
|
||||
free(object);
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
KVMGFX Client - A KVM Client for VGA Passthrough
|
||||
Copyright (C) 2017-2019 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 "common/stringlist.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
struct StringList
|
||||
{
|
||||
bool owns_strings;
|
||||
unsigned int size;
|
||||
unsigned int count;
|
||||
char ** list;
|
||||
};
|
||||
|
||||
StringList stringlist_new(bool owns_strings)
|
||||
{
|
||||
StringList sl = malloc(sizeof(struct StringList));
|
||||
|
||||
sl->owns_strings = owns_strings;
|
||||
sl->size = 32;
|
||||
sl->count = 0;
|
||||
sl->list = malloc(sizeof(char *) * sl->size);
|
||||
|
||||
return sl;
|
||||
}
|
||||
|
||||
void stringlist_free(StringList * sl)
|
||||
{
|
||||
if ((*sl)->owns_strings)
|
||||
for(unsigned int i = 0; i < (*sl)->count; ++i)
|
||||
free((*sl)->list[i]);
|
||||
|
||||
free((*sl)->list);
|
||||
free((*sl));
|
||||
*sl = NULL;
|
||||
}
|
||||
|
||||
int stringlist_push (StringList sl, char * str)
|
||||
{
|
||||
if (sl->count == sl->size)
|
||||
{
|
||||
sl->size += 32;
|
||||
sl->list = realloc(sl->list, sizeof(char *) * sl->size);
|
||||
}
|
||||
|
||||
unsigned int index = sl->count;
|
||||
sl->list[sl->count++] = str;
|
||||
return index;
|
||||
}
|
||||
|
||||
unsigned int stringlist_count(StringList sl)
|
||||
{
|
||||
return sl->count;
|
||||
}
|
||||
|
||||
char * stringlist_at(StringList sl, unsigned int index)
|
||||
{
|
||||
if (index >= sl->count)
|
||||
return NULL;
|
||||
|
||||
return sl->list[index];
|
||||
}
|
||||
33
porthole/CMakeLists.txt
Normal file
33
porthole/CMakeLists.txt
Normal file
@@ -0,0 +1,33 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(porthole LANGUAGES C)
|
||||
|
||||
include_directories(
|
||||
${PROJECT_SOURCE_DIR}/include
|
||||
)
|
||||
|
||||
add_library(porthole STATIC
|
||||
src/porthole.c
|
||||
src/util.c
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
add_subdirectory(src/windows)
|
||||
set(PLATFORM "windows")
|
||||
else()
|
||||
add_subdirectory(src/linux)
|
||||
set(PLATFORM "linux")
|
||||
endif()
|
||||
|
||||
target_link_libraries(porthole
|
||||
PRIVATE
|
||||
porthole-${PLATFORM}
|
||||
lg_common
|
||||
pthread
|
||||
)
|
||||
|
||||
target_include_directories(porthole
|
||||
INTERFACE
|
||||
include
|
||||
PRIVATE
|
||||
src
|
||||
)
|
||||
81
porthole/include/porthole/client.h
Normal file
81
porthole/include/porthole/client.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2019 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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
typedef struct PortholeClient *PortholeClient;
|
||||
|
||||
/**
|
||||
* Memory map event callback.
|
||||
*
|
||||
* @param type The type ID provided by the guest
|
||||
* @param map The new mapping
|
||||
*
|
||||
* @note this is called from the socket thread
|
||||
*/
|
||||
typedef void (*PortholeMapEvent)(uint32_t type, PortholeMap * map);
|
||||
|
||||
/**
|
||||
* Memory unmap event callback.
|
||||
*
|
||||
* @param id The id of the mapping that has been unmapped by the guest
|
||||
*
|
||||
* @note this is called from the socket thread
|
||||
*/
|
||||
typedef void (*PortholeUnmapEvent)(uint32_t id);
|
||||
|
||||
/**
|
||||
* Unexpected client disconnection event.
|
||||
*
|
||||
* When this occurs all mappings become invalid and should no longer be used.
|
||||
*
|
||||
* @note this is called from the socket thread
|
||||
*/
|
||||
typedef void (*PortholeDisconEvent)();
|
||||
|
||||
/**
|
||||
* Open the porthole device
|
||||
*
|
||||
* @param handle Returned handle if successful, otherwise undefined
|
||||
* @param socket_path Path to the unix socket of the porthole char device
|
||||
* @param map_event Callback for map events from the guest
|
||||
* @param unmap_event Callback for unmap events from the guest
|
||||
* @param discon_event Callback for client socket disconnection
|
||||
* @returns true on success
|
||||
*
|
||||
* If successful the handle must be closed to free resources when finished.
|
||||
*/
|
||||
bool porthole_client_open(
|
||||
PortholeClient * handle,
|
||||
const char * socket_path,
|
||||
PortholeMapEvent map_cb,
|
||||
PortholeUnmapEvent unmap_cb,
|
||||
PortholeDisconEvent discon_cb);
|
||||
|
||||
/**
|
||||
* Close the porthole devce
|
||||
*
|
||||
* @param handle The porthole client handle obtained from porthole_client_open
|
||||
*
|
||||
* handle will be set to NULL and is no longer valid after calling this
|
||||
* function and all mappings will become invalid.
|
||||
*/
|
||||
void porthole_client_close(PortholeClient * handle);
|
||||
123
porthole/include/porthole/device.h
Normal file
123
porthole/include/porthole/device.h
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2019 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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct PortholeDev *PortholeDev;
|
||||
typedef int PortholeID;
|
||||
typedef enum PortholeState
|
||||
{
|
||||
PH_STATE_NEW_SESSION,
|
||||
PH_STATE_CONNECTED,
|
||||
PH_STATE_DISCONNECTED
|
||||
}
|
||||
PortholeState;
|
||||
|
||||
/**
|
||||
* Open the porthole device
|
||||
*
|
||||
* @param handle The returned handle if successful, otherwise undefined
|
||||
* @param vendor_id The subsystem vendor and device id to match
|
||||
* @return true on success
|
||||
*
|
||||
* If successful the handle must be closed to free resources when finished.
|
||||
*/
|
||||
bool porthole_dev_open(PortholeDev *handle, const uint32_t vendor_id);
|
||||
|
||||
/**
|
||||
* Close the porthole devce
|
||||
*
|
||||
* @param handle The porthole device handle obtained from porthole_dev_open
|
||||
*
|
||||
* handle will be set to NULL and is no longer valid after calling this function.
|
||||
*/
|
||||
void porthole_dev_close(PortholeDev *handle);
|
||||
|
||||
/**
|
||||
* Get the state of the porthole device
|
||||
*
|
||||
* @param handle The porthole device handle obtained form porthole_dev_open
|
||||
* @return The current state of the connection
|
||||
*
|
||||
* This method will return the current state of the porthole device
|
||||
*
|
||||
* * PH_STATE_NEW_SESSION = The client has connected
|
||||
* * PH_STATE_CONNECTED = The client is still connected
|
||||
* * PH_STATE_DISCONNECTED = There is no client connection
|
||||
*/
|
||||
PortholeState porthole_dev_get_state(PortholeDev handle);
|
||||
|
||||
/**
|
||||
* Wait for the specified state
|
||||
*
|
||||
* @param handle The porthole device handle obtained from porthole_dev_open
|
||||
* @param state The state to wait for
|
||||
* @param timeout The maximum amount of time to wait in milliseconds for the state (0 = infinite)
|
||||
* @return true on success, false on timeout
|
||||
*/
|
||||
bool porthole_dev_wait_state(PortholeDev handle, const PortholeState state, const unsigned int timeout);
|
||||
|
||||
/**
|
||||
* Share the provided buffer over the porthole device
|
||||
*
|
||||
* @param handle The porthole device
|
||||
* @param type The type
|
||||
* @param buffer The buffer to share
|
||||
* @param size The size of the buffer
|
||||
* @return the porthole mapping ID, or -1 on failure
|
||||
*
|
||||
* This function locks the supplied buffer in RAM via the porthole device
|
||||
* driver and is then shared with the device for use outside the guest.
|
||||
*
|
||||
* The type parameter is application defined and is sent along with the buffer
|
||||
* to the client application for buffer type identification.
|
||||
*
|
||||
* If successful the byffer must be unlocked with `porthole_dev_unlock` before
|
||||
* the buffer can be freed.
|
||||
*
|
||||
* This is an expensive operation, the idea is that you allocate fixed buffers
|
||||
* and share them with the host at initialization.
|
||||
*
|
||||
* @note the device & driver are hard limited to 32 shares.
|
||||
*/
|
||||
PortholeID porthole_dev_map(PortholeDev handle, const uint32_t type, void *buffer, size_t size);
|
||||
|
||||
/**
|
||||
* Unmap a previously shared buffer
|
||||
*
|
||||
* @param handle The porthole device
|
||||
* @param id The porthole map id returned by porthole_dev_share
|
||||
* @return true on success
|
||||
*
|
||||
* Unmaps a previously shared buffer. Once this has been done the buffer can
|
||||
* be freed or re-used. The client application should no longer attempt to
|
||||
* access this buffer as it may be paged out of RAM.
|
||||
*
|
||||
* Note that this is not strictly required as closing the device will cause
|
||||
* the driver to cleanup any prior locked buffers.
|
||||
*
|
||||
* The client application will be notified that the mapping is about to become
|
||||
* invalid and is expected to clean up and notify when it is done. If your
|
||||
* application hangs calling this method the issue is very likely with your
|
||||
* client application.
|
||||
*/
|
||||
bool porthole_dev_unmap(PortholeDev handle, PortholeID id);
|
||||
31
porthole/include/porthole/types.h
Normal file
31
porthole/include/porthole/types.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2019 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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t id;
|
||||
unsigned int size;
|
||||
unsigned int num_segments;
|
||||
}
|
||||
PortholeMap;
|
||||
62
porthole/include/porthole/util.h
Normal file
62
porthole/include/porthole/util.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2019 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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include <sys/types.h>
|
||||
|
||||
/**
|
||||
* Copy from memory to a PortholeMap
|
||||
*
|
||||
* @param src The source buffer
|
||||
* @param dst The destination map
|
||||
* @param len The data length to copy
|
||||
* @param off The offset into the dst PortholeMap
|
||||
*/
|
||||
void porthole_copy_mem_to_map(void * src, PortholeMap * dst, size_t len, off_t off);
|
||||
|
||||
/**
|
||||
* Copy from a PortholeMap to memory
|
||||
*
|
||||
* @param src The source buffer
|
||||
* @param dst The destination buffer
|
||||
* @param len The data length to copy
|
||||
* @param off The offset into the src PortholeMap
|
||||
*/
|
||||
void porthole_copy_map_to_mem(PortholeMap * src, void * dst, size_t len, off_t off);
|
||||
|
||||
/**
|
||||
* Copy from a PortholeMap to a PortholeMap
|
||||
*
|
||||
* @param src The source buffer
|
||||
* @param dst The destination buffer
|
||||
* @param len The data length to copy
|
||||
* @param src_off The offset into the src PortholeMap
|
||||
* @param dst_off The offset into the dst PortholeMap
|
||||
*/
|
||||
void porthole_copy_map_to_map(PortholeMap * src, PortholeMap * dst, size_t len, off_t src_off, off_t dst_off);
|
||||
|
||||
/**
|
||||
* Get the pointer to the base of a PortholeMap
|
||||
*
|
||||
* @param map The map to get the pointer for
|
||||
* @return The base address of the mapping, or NULL if the mapping is not contiguous
|
||||
*/
|
||||
void * porthole_get_map_ptr(PortholeMap *map);
|
||||
15
porthole/src/linux/CMakeLists.txt
Normal file
15
porthole/src/linux/CMakeLists.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(porthole-linux LANGUAGES C)
|
||||
|
||||
add_library(porthole-linux STATIC
|
||||
client.c
|
||||
)
|
||||
|
||||
target_link_libraries(porthole-linux
|
||||
lg_common
|
||||
)
|
||||
|
||||
target_include_directories(porthole-linux
|
||||
PRIVATE
|
||||
src
|
||||
)
|
||||
438
porthole/src/linux/client.c
Normal file
438
porthole/src/linux/client.c
Normal file
@@ -0,0 +1,438 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2019 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 "porthole/client.h"
|
||||
#include "common/objectlist.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
#include "../types.h"
|
||||
#include "../phmsg.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t id;
|
||||
int fd;
|
||||
int refcount;
|
||||
uint8_t * map;
|
||||
size_t size;
|
||||
}
|
||||
SharedFD;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SharedFD * sfd;
|
||||
uint64_t addr;
|
||||
uint32_t size;
|
||||
}
|
||||
Segment;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t id;
|
||||
ObjectList segments;
|
||||
size_t size;
|
||||
}
|
||||
Mapping;
|
||||
|
||||
struct PortholeClient
|
||||
{
|
||||
int socket;
|
||||
char rxbuf[1024];
|
||||
int rxbuf_len;
|
||||
|
||||
PortholeMapEvent map_cb;
|
||||
PortholeUnmapEvent unmap_cb;
|
||||
PortholeDisconEvent discon_cb;
|
||||
ObjectList fds;
|
||||
ObjectList intmaps;
|
||||
Mapping * current;
|
||||
ObjectList maps;
|
||||
bool running;
|
||||
pthread_t thread;
|
||||
bool thread_valid;
|
||||
};
|
||||
|
||||
// forwards
|
||||
static void * porthole_socket_thread(void * opaque);
|
||||
static void porthole_free_map(Mapping * map);
|
||||
static void porthole_sharedfd_free_handler(void * opaque);
|
||||
static void porthole_intmaps_free_handler(void * opaque);
|
||||
static void porthole_segment_free_handler(void * opaque);
|
||||
static Mapping * porthole_intmap_new();
|
||||
static void porthole_sharedfd_new(PortholeClient handle, const uint32_t id, const int fd);
|
||||
static void porthole_segment_new(ObjectList fds, Mapping *map, const uint32_t fd_id, const uint64_t addr, const uint32_t size);
|
||||
static void porthole_do_map(PortholeClient handle, Mapping * map, const uint32_t type);
|
||||
|
||||
|
||||
// implementation
|
||||
bool porthole_client_open(
|
||||
PortholeClient * handle,
|
||||
const char * socket_path,
|
||||
PortholeMapEvent map_cb,
|
||||
PortholeUnmapEvent unmap_cb,
|
||||
PortholeDisconEvent discon_cb)
|
||||
{
|
||||
assert(handle);
|
||||
|
||||
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd == -1)
|
||||
{
|
||||
DEBUG_ERROR("Failed to create a unix socket");
|
||||
return false;
|
||||
}
|
||||
|
||||
struct timeval tv = { .tv_sec = 1, .tv_usec = 0 };
|
||||
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof(tv));
|
||||
|
||||
struct sockaddr_un addr = { .sun_family = AF_UNIX };
|
||||
strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path)-1);
|
||||
|
||||
if (connect(fd, (const struct sockaddr*)&addr, sizeof(addr)) == -1)
|
||||
{
|
||||
DEBUG_ERROR("Failed to connect to the socket");
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
*handle = (PortholeClient)calloc(sizeof(struct PortholeClient), 1);
|
||||
|
||||
(*handle)->socket = fd;
|
||||
(*handle)->map_cb = map_cb;
|
||||
(*handle)->unmap_cb = unmap_cb;
|
||||
(*handle)->discon_cb = discon_cb;
|
||||
(*handle)->fds = objectlist_new(porthole_sharedfd_free_handler);
|
||||
(*handle)->intmaps = objectlist_new(porthole_intmaps_free_handler);
|
||||
(*handle)->maps = objectlist_new(objectlist_free_item);
|
||||
(*handle)->running = true;
|
||||
|
||||
if (pthread_create(&(*handle)->thread, NULL, porthole_socket_thread, *handle) != 0)
|
||||
{
|
||||
DEBUG_ERROR("Failed to create porthole socket thread");
|
||||
porthole_client_close(handle);
|
||||
return false;
|
||||
}
|
||||
|
||||
(*handle)->thread_valid = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void porthole_client_close(PortholeClient * handle)
|
||||
{
|
||||
assert(handle && *handle);
|
||||
|
||||
if ((*handle)->thread_valid)
|
||||
{
|
||||
(*handle)->running = false;
|
||||
pthread_join((*handle)->thread, NULL);
|
||||
}
|
||||
|
||||
close((*handle)->socket);
|
||||
|
||||
if ((*handle)->current)
|
||||
porthole_free_map((*handle)->current);
|
||||
|
||||
objectlist_free(&(*handle)->maps );
|
||||
objectlist_free(&(*handle)->intmaps);
|
||||
objectlist_free(&(*handle)->fds );
|
||||
|
||||
free(*handle);
|
||||
*handle = NULL;
|
||||
}
|
||||
|
||||
static void * porthole_socket_thread(void * opaque)
|
||||
{
|
||||
PortholeClient handle = (PortholeClient)opaque;
|
||||
DEBUG_INFO("Porthole socket thread started");
|
||||
|
||||
while(handle->running)
|
||||
{
|
||||
struct iovec io =
|
||||
{
|
||||
.iov_base = &handle->rxbuf[handle->rxbuf_len],
|
||||
.iov_len = sizeof(handle->rxbuf) - handle->rxbuf_len
|
||||
};
|
||||
|
||||
char cbuffer[256] = {0};
|
||||
struct msghdr msghdr =
|
||||
{
|
||||
.msg_iov = &io,
|
||||
.msg_iovlen = 1,
|
||||
.msg_control = &cbuffer,
|
||||
.msg_controllen = sizeof(cbuffer)
|
||||
};
|
||||
|
||||
handle->rxbuf_len = recvmsg(handle->socket, &msghdr, 0);
|
||||
if (handle->rxbuf_len < 0)
|
||||
{
|
||||
handle->rxbuf_len = 0;
|
||||
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK)
|
||||
continue;
|
||||
|
||||
DEBUG_ERROR("Failed to recieve the message");
|
||||
if (handle->discon_cb)
|
||||
handle->discon_cb();
|
||||
break;
|
||||
}
|
||||
|
||||
int pos = 0;
|
||||
while(pos != handle->rxbuf_len)
|
||||
{
|
||||
PHMsg *msg = (PHMsg *)&handle->rxbuf[pos];
|
||||
|
||||
// check for an incomplete message
|
||||
if (pos + msg->len > handle->rxbuf_len)
|
||||
break;
|
||||
|
||||
assert(msg->len >= PH_MSG_BASE_SIZE);
|
||||
pos += msg->len;
|
||||
|
||||
/* process the message */
|
||||
switch(msg->msg)
|
||||
{
|
||||
case PH_MSG_MAP:
|
||||
if (handle->current)
|
||||
{
|
||||
DEBUG_WARN("Started a new map before finishing the last one");
|
||||
porthole_free_map(handle->current);
|
||||
}
|
||||
|
||||
handle->current = porthole_intmap_new();
|
||||
break;
|
||||
|
||||
case PH_MSG_FD:
|
||||
{
|
||||
struct cmsghdr * cmsg = CMSG_FIRSTHDR(&msghdr);
|
||||
int * fds = (int *)CMSG_DATA(cmsg);
|
||||
porthole_sharedfd_new(handle, msg->u.fd.id, fds[0]);
|
||||
break;
|
||||
}
|
||||
|
||||
case PH_MSG_SEGMENT:
|
||||
{
|
||||
if (!handle->current)
|
||||
DEBUG_FATAL("Segment sent before map, this is a bug in the guest porthole device or driver");
|
||||
|
||||
porthole_segment_new(
|
||||
handle->fds,
|
||||
handle->current,
|
||||
msg->u.segment.fd_id,
|
||||
msg->u.segment.addr,
|
||||
msg->u.segment.size
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case PH_MSG_FINISH:
|
||||
if (!handle->current)
|
||||
DEBUG_FATAL("Finished map before starting one");
|
||||
|
||||
handle->current->id = msg->u.finish.id;
|
||||
objectlist_push(handle->intmaps, handle->current);
|
||||
porthole_do_map(handle, handle->current, msg->u.finish.type);
|
||||
handle->current = NULL;
|
||||
break;
|
||||
|
||||
case PH_MSG_UNMAP:
|
||||
{
|
||||
// notify the application of the unmap
|
||||
handle->unmap_cb(msg->u.unmap.id);
|
||||
|
||||
// remove the PortholeMap object
|
||||
unsigned int count = objectlist_count(handle->maps);
|
||||
for(unsigned int i = 0; i < count; ++i)
|
||||
{
|
||||
PortholeMap *m = (PortholeMap *)objectlist_at(handle->maps, i);
|
||||
if (m->id == msg->u.unmap.id)
|
||||
{
|
||||
objectlist_remove(handle->maps, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// remove the internal mapping object
|
||||
count = objectlist_count(handle->intmaps);
|
||||
for(unsigned int i = 0; i < count; ++i)
|
||||
{
|
||||
Mapping *m = (Mapping *)objectlist_at(handle->intmaps, i);
|
||||
if (m->id == msg->u.unmap.id)
|
||||
{
|
||||
objectlist_remove(handle->intmaps, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// reply to the guest to allow it to continue
|
||||
uint32_t reply = PH_MSG_UNMAP;
|
||||
msghdr.msg_controllen = 0;
|
||||
io.iov_base = &reply;
|
||||
io.iov_len = sizeof(reply);
|
||||
if (sendmsg(handle->socket, &msghdr, 0) < 0)
|
||||
{
|
||||
DEBUG_ERROR("Failed to respond to the guest");
|
||||
handle->running = false;
|
||||
if (handle->discon_cb)
|
||||
handle->discon_cb();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// save any remaining bytes for the next recv
|
||||
handle->rxbuf_len -= pos;
|
||||
memmove(handle->rxbuf, &handle->rxbuf[pos], handle->rxbuf_len);
|
||||
}
|
||||
|
||||
handle->running = false;
|
||||
DEBUG_INFO("Porthole socket thread stopped");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void porthole_sharedfd_new(PortholeClient handle, const uint32_t id, const int fd)
|
||||
{
|
||||
struct stat st;
|
||||
fstat(fd, &st);
|
||||
|
||||
SharedFD * sfd = (SharedFD *)calloc(sizeof(SharedFD), 1);
|
||||
sfd->id = id;
|
||||
sfd->fd = fd;
|
||||
sfd->size = st.st_size;
|
||||
|
||||
DEBUG_INFO("Guest FD ID %u (FD:%d, Size:%lu)", sfd->id, sfd->fd, sfd->size);
|
||||
objectlist_push(handle->fds, sfd);
|
||||
}
|
||||
|
||||
static void porthole_sharedfd_inc_ref(SharedFD * sfd)
|
||||
{
|
||||
if (sfd->refcount == 0)
|
||||
{
|
||||
sfd->map = mmap(NULL, sfd->size, PROT_READ | PROT_WRITE, MAP_SHARED, sfd->fd, 0);
|
||||
if(!sfd->map)
|
||||
DEBUG_FATAL("Failed to map shared memory");
|
||||
}
|
||||
++sfd->refcount;
|
||||
}
|
||||
|
||||
static void porthole_sharedfd_dec_ref(SharedFD * sfd)
|
||||
{
|
||||
if (sfd->refcount == 0)
|
||||
return;
|
||||
|
||||
munmap(sfd->map, sfd->size);
|
||||
sfd->map = NULL;
|
||||
|
||||
--sfd->refcount;
|
||||
}
|
||||
|
||||
static void porthole_sharedfd_free_handler(void * opaque)
|
||||
{
|
||||
SharedFD * sfd = (SharedFD *)opaque;
|
||||
|
||||
if (sfd->map)
|
||||
{
|
||||
munmap(sfd->map, sfd->size);
|
||||
sfd->map = NULL;
|
||||
}
|
||||
|
||||
close(sfd->fd);
|
||||
free(sfd);
|
||||
}
|
||||
|
||||
static Mapping * porthole_intmap_new()
|
||||
{
|
||||
Mapping * map = (Mapping *)calloc(sizeof(Mapping), 1);
|
||||
map->segments = objectlist_new(porthole_segment_free_handler);
|
||||
return map;
|
||||
}
|
||||
|
||||
static void porthole_free_map(Mapping * map)
|
||||
{
|
||||
objectlist_free(&map->segments);
|
||||
free(map);
|
||||
}
|
||||
|
||||
static void porthole_intmaps_free_handler(void * opaque)
|
||||
{
|
||||
porthole_free_map((Mapping *)opaque);
|
||||
}
|
||||
|
||||
static void porthole_segment_new(ObjectList fds, Mapping *map, const uint32_t fd_id, const uint64_t addr, const uint32_t size)
|
||||
{
|
||||
Segment * seg = calloc(sizeof(Segment), 1);
|
||||
seg->addr = addr;
|
||||
seg->size = size;
|
||||
|
||||
const unsigned int count = objectlist_count(fds);
|
||||
for(unsigned int i = 0; i < count; ++i)
|
||||
{
|
||||
SharedFD *sfd = (SharedFD*)objectlist_at(fds, i);
|
||||
if (sfd->id == fd_id)
|
||||
{
|
||||
seg->sfd = sfd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!seg->sfd)
|
||||
DEBUG_FATAL("Unable to find the FD for the segment, this is a bug in the porthole device!");
|
||||
|
||||
map->size += size;
|
||||
porthole_sharedfd_inc_ref(seg->sfd);
|
||||
objectlist_push(map->segments, seg);
|
||||
}
|
||||
|
||||
static void porthole_segment_free_handler(void * opaque)
|
||||
{
|
||||
Segment * seg = (Segment *)opaque;
|
||||
porthole_sharedfd_dec_ref(seg->sfd);
|
||||
free(seg);
|
||||
}
|
||||
|
||||
static void porthole_do_map(PortholeClient handle, Mapping * map, const uint32_t type)
|
||||
{
|
||||
const unsigned int count = objectlist_count(map->segments);
|
||||
|
||||
PortholeMap *m = calloc(sizeof(PortholeMap) + sizeof(PortholeSegment) * count, 1);
|
||||
|
||||
m->id = map->id;
|
||||
m->size = map->size;
|
||||
m->num_segments = count;
|
||||
|
||||
for(unsigned int i = 0; i < count; ++i)
|
||||
{
|
||||
Segment * seg = (Segment *)objectlist_at(map->segments, i);
|
||||
PH_SEGMENTS(m)[i].size = seg->size;
|
||||
PH_SEGMENTS(m)[i].data = seg->sfd->map + seg->addr;
|
||||
}
|
||||
|
||||
objectlist_push(handle->maps, m);
|
||||
handle->map_cb(type, m);
|
||||
}
|
||||
64
porthole/src/phmsg.h
Normal file
64
porthole/src/phmsg.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2019 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 <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
uint32_t id; // the ID of the FD
|
||||
} __attribute__ ((packed)) PHMsgFd;
|
||||
|
||||
typedef struct {
|
||||
uint32_t fd_id; // the ID of the FD for this segment
|
||||
uint32_t size; // the size of this segment
|
||||
uint64_t addr; // the base address of this segment
|
||||
} __attribute__ ((packed)) PHMsgSegment;
|
||||
|
||||
typedef struct {
|
||||
uint32_t type; // the application defined type
|
||||
uint32_t id; // the ID of the new mapping
|
||||
} __attribute__ ((packed)) PHMsgFinish;
|
||||
|
||||
typedef struct {
|
||||
uint32_t id; // the mapping ID
|
||||
} __attribute__ ((packed)) PHMsgUnmap;
|
||||
|
||||
typedef struct {
|
||||
uint8_t len; // the message length
|
||||
uint8_t msg; // the message ID
|
||||
union
|
||||
{
|
||||
PHMsgFd fd;
|
||||
PHMsgSegment segment;
|
||||
PHMsgFinish finish;
|
||||
PHMsgUnmap unmap;
|
||||
} u;
|
||||
} __attribute__ ((packed)) PHMsg;
|
||||
|
||||
#define PH_MSG_MAP 0x1 // start of a map sequence
|
||||
#define PH_MSG_FD 0x2 // file descriptor
|
||||
#define PH_MSG_SEGMENT 0x3 // map segment
|
||||
#define PH_MSG_FINISH 0x4 // finish of map sequence
|
||||
#define PH_MSG_UNMAP 0x5 // unmap a previous map
|
||||
|
||||
#define PH_MSG_BASE_SIZE (sizeof(uint8_t) + sizeof(uint8_t))
|
||||
#define PH_MSG_MAP_SIZE (PH_MSG_BASE_SIZE)
|
||||
#define PH_MSG_FD_SIZE (PH_MSG_BASE_SIZE + sizeof(PHMsgFd))
|
||||
#define PH_MSG_SEGMENT_SIZE (PH_MSG_BASE_SIZE + sizeof(PHMsgSegment))
|
||||
#define PH_MSG_FINISH_SIZE (PH_MSG_BASE_SIZE + sizeof(PHMsgFinish))
|
||||
#define PH_MSG_UNMAP_SIZE (PH_MSG_BASE_SIZE + sizeof(PHMsgUnmap))
|
||||
18
porthole/src/porthole.c
Normal file
18
porthole/src/porthole.c
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2019 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
|
||||
*/
|
||||
31
porthole/src/types.h
Normal file
31
porthole/src/types.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2019 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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "porthole/types.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned int size;
|
||||
void * data;
|
||||
}
|
||||
PortholeSegment;
|
||||
|
||||
#define PH_SEGMENTS(map) ((PortholeSegment *)((map)+1))
|
||||
161
porthole/src/util.c
Normal file
161
porthole/src/util.c
Normal file
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2019 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 "types.h"
|
||||
#include "porthole/util.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
void porthole_copy_mem_to_map(void * src, PortholeMap * dst, size_t len, off_t off)
|
||||
{
|
||||
if (off + len > dst->size)
|
||||
DEBUG_FATAL("Attempt to write beyond the length of destination mapping");
|
||||
|
||||
/* find the start segment */
|
||||
PortholeSegment * seg = PH_SEGMENTS(dst);
|
||||
while(off)
|
||||
{
|
||||
if (seg->size > off)
|
||||
break;
|
||||
|
||||
off -= seg->size;
|
||||
++seg;
|
||||
}
|
||||
|
||||
/* copy into each segment until the length has been satisfied */
|
||||
while(len)
|
||||
{
|
||||
char * buf = (char *)seg->data + off;
|
||||
size_t avail = seg->size - off;
|
||||
off = 0;
|
||||
|
||||
if (avail > len)
|
||||
avail = len;
|
||||
|
||||
memcpy(buf, src, avail);
|
||||
|
||||
src = (char *)src + avail;
|
||||
len -= avail;
|
||||
++seg;
|
||||
}
|
||||
}
|
||||
|
||||
void porthole_copy_map_to_mem(PortholeMap * src, void * dst, size_t len, off_t off)
|
||||
{
|
||||
if (off + len > src->size)
|
||||
DEBUG_FATAL("Attempt to read beyond the length of the source mapping");
|
||||
|
||||
/* find the start segment */
|
||||
PortholeSegment * seg = PH_SEGMENTS(src);
|
||||
while(off)
|
||||
{
|
||||
if (seg->size > off)
|
||||
break;
|
||||
|
||||
off -= seg->size;
|
||||
++seg;
|
||||
}
|
||||
|
||||
/* copy from each segment until the length has been satisfied */
|
||||
while(len)
|
||||
{
|
||||
char * buf = (char *)seg->data + off;
|
||||
size_t avail = seg->size - off;
|
||||
off = 0;
|
||||
|
||||
if (avail > len)
|
||||
avail = len;
|
||||
|
||||
memcpy(dst, buf, avail);
|
||||
|
||||
dst = (char *)dst + avail;
|
||||
len -= avail;
|
||||
++seg;
|
||||
}
|
||||
}
|
||||
|
||||
void porthole_copy_map_to_map(PortholeMap * src, PortholeMap * dst, size_t len, off_t src_off, off_t dst_off)
|
||||
{
|
||||
if (src_off + len > src->size)
|
||||
DEBUG_FATAL("Attempt to read beyond th elength of the source mapping");
|
||||
|
||||
if (dst_off + len > dst->size)
|
||||
DEBUG_FATAL("Attempt to write beyond the length of the destination mapping");
|
||||
|
||||
/* find the start segments */
|
||||
PortholeSegment * src_seg = PH_SEGMENTS(src);
|
||||
while(src_off)
|
||||
{
|
||||
if (src_seg->size > src_off)
|
||||
break;
|
||||
|
||||
src_off -= src_seg->size;
|
||||
++src_seg;
|
||||
}
|
||||
|
||||
PortholeSegment * dst_seg = PH_SEGMENTS(dst);
|
||||
while(dst_off)
|
||||
{
|
||||
if (dst_seg->size > dst_off)
|
||||
break;
|
||||
|
||||
dst_off -= dst_seg->size;
|
||||
++dst_seg;
|
||||
}
|
||||
|
||||
while(len)
|
||||
{
|
||||
char * src_buf = (char *)src_seg->data + src_off;
|
||||
char * dst_buf = (char *)dst_seg->data + dst_off;
|
||||
size_t src_avail = src_seg->size - src_off;
|
||||
size_t dst_avail = dst_seg->size - dst_off;
|
||||
src_off = 0;
|
||||
dst_off = 0;
|
||||
|
||||
size_t avail = src_avail > dst_avail ? dst_avail : src_avail;
|
||||
if (avail > len)
|
||||
avail = len;
|
||||
|
||||
memcpy(dst_buf, src_buf, avail);
|
||||
|
||||
src_avail -= avail;
|
||||
dst_avail -= avail;
|
||||
|
||||
if (src_avail == 0)
|
||||
++src_seg;
|
||||
else
|
||||
src_off = src_avail;
|
||||
|
||||
if (dst_avail == 0)
|
||||
++dst_seg;
|
||||
else
|
||||
dst_off = dst_avail;
|
||||
|
||||
len -= avail;
|
||||
}
|
||||
}
|
||||
|
||||
void * porthole_get_map_ptr(PortholeMap *map)
|
||||
{
|
||||
if (map->num_segments != 1)
|
||||
return NULL;
|
||||
|
||||
return PH_SEGMENTS(map)[0].data;
|
||||
}
|
||||
16
porthole/src/windows/CMakeLists.txt
Normal file
16
porthole/src/windows/CMakeLists.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(porthole-windows LANGUAGES C)
|
||||
|
||||
add_library(porthole-windows STATIC
|
||||
device.c
|
||||
)
|
||||
|
||||
target_link_libraries(porthole-windows
|
||||
setupapi
|
||||
lg_common
|
||||
)
|
||||
|
||||
target_include_directories(porthole-windows
|
||||
PRIVATE
|
||||
src
|
||||
)
|
||||
256
porthole/src/windows/device.c
Normal file
256
porthole/src/windows/device.c
Normal file
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2019 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 "porthole/device.h"
|
||||
#include "driver.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <setupapi.h>
|
||||
#include <assert.h>
|
||||
|
||||
struct PortholeDev
|
||||
{
|
||||
HANDLE dev;
|
||||
bool connected;
|
||||
PortholeEvents events;
|
||||
};
|
||||
|
||||
bool porthole_dev_open(PortholeDev *handle, const uint32_t vendor_id)
|
||||
{
|
||||
HDEVINFO devInfo = {0};
|
||||
PSP_DEVICE_INTERFACE_DETAIL_DATA infData = NULL;
|
||||
SP_DEVICE_INTERFACE_DATA devInfData = {0};
|
||||
HANDLE dev;
|
||||
|
||||
assert(handle);
|
||||
|
||||
devInfo = SetupDiGetClassDevs(NULL, NULL, NULL, DIGCF_PRESENT | DIGCF_ALLCLASSES | DIGCF_DEVICEINTERFACE);
|
||||
devInfData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
||||
|
||||
for(int devIndex = 0; ; ++devIndex)
|
||||
{
|
||||
if (SetupDiEnumDeviceInterfaces(devInfo, NULL, &GUID_DEVINTERFACE_PORTHOLE, devIndex, &devInfData) == FALSE)
|
||||
{
|
||||
DWORD error = GetLastError();
|
||||
if (error == ERROR_NO_MORE_ITEMS)
|
||||
{
|
||||
DEBUG_ERROR("Unable to enumerate the device, is it attached?");
|
||||
SetupDiDestroyDeviceInfoList(devInfo);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
DWORD reqSize = 0;
|
||||
SetupDiGetDeviceInterfaceDetail(devInfo, &devInfData, NULL, 0, &reqSize, NULL);
|
||||
if (!reqSize)
|
||||
{
|
||||
DEBUG_WARN("SetupDiGetDeviceInterfaceDetail for %lu failed\n", reqSize);
|
||||
continue;
|
||||
}
|
||||
|
||||
infData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)calloc(reqSize, 1);
|
||||
infData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
||||
if (!SetupDiGetDeviceInterfaceDetail(devInfo, &devInfData, infData, reqSize, NULL, NULL))
|
||||
{
|
||||
free(infData);
|
||||
DEBUG_WARN("SetupDiGetDeviceInterfaceDetail for %lu failed\n", reqSize);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* get the subsys id from the device */
|
||||
unsigned int vendorID, deviceID, subsysID;
|
||||
if (sscanf(infData->DevicePath, "\\\\?\\pci#ven_%4x&dev_%4x&subsys_%8x", &vendorID, &deviceID, &subsysID) != 3)
|
||||
{
|
||||
free(infData);
|
||||
DEBUG_ERROR("Failed to parse: %s", infData->DevicePath);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (subsysID != vendor_id)
|
||||
{
|
||||
DEBUG_INFO("Skipping device %d, vendor_id 0x%x != 0x%x", devIndex, subsysID, vendor_id);
|
||||
free(infData);
|
||||
continue;
|
||||
}
|
||||
|
||||
dev = CreateFile(infData->DevicePath, 0, 0, NULL, OPEN_EXISTING, 0, 0);
|
||||
if (dev == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
DEBUG_ERROR("Failed to open device %d", devIndex);
|
||||
free(infData);
|
||||
continue;
|
||||
}
|
||||
|
||||
DEBUG_INFO("Device found");
|
||||
|
||||
free(infData);
|
||||
break;
|
||||
}
|
||||
|
||||
*handle = (PortholeDev)calloc(sizeof(struct PortholeDev), 1);
|
||||
if (!*handle)
|
||||
{
|
||||
DEBUG_ERROR("Failed to allocate PortholeDev struct, out of memory!");
|
||||
CloseHandle(dev);
|
||||
return false;
|
||||
}
|
||||
|
||||
(*handle)->dev = dev;
|
||||
|
||||
/* create the events and register them */
|
||||
(*handle)->events.connect = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
(*handle)->events.disconnect = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
|
||||
DWORD returned;
|
||||
if (!DeviceIoControl(dev, IOCTL_PORTHOLE_REGISTER_EVENTS, &(*handle)->events, sizeof(PortholeEvents), NULL, 0, &returned, NULL))
|
||||
{
|
||||
DEBUG_ERROR("Failed to register the events");
|
||||
CloseHandle((*handle)->events.connect );
|
||||
CloseHandle((*handle)->events.disconnect);
|
||||
CloseHandle(dev);
|
||||
free(*handle);
|
||||
*handle = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void porthole_dev_close(PortholeDev *handle)
|
||||
{
|
||||
assert(handle && *handle);
|
||||
|
||||
CloseHandle((*handle)->events.connect );
|
||||
CloseHandle((*handle)->events.disconnect);
|
||||
CloseHandle((*handle)->dev);
|
||||
free(*handle);
|
||||
*handle = NULL;
|
||||
}
|
||||
|
||||
static PortholeState get_state(PortholeDev handle, unsigned int timeout)
|
||||
{
|
||||
if (handle->connected)
|
||||
{
|
||||
switch(WaitForSingleObject(handle->events.disconnect, timeout))
|
||||
{
|
||||
case WAIT_OBJECT_0:
|
||||
handle->connected = false;
|
||||
return PH_STATE_DISCONNECTED;
|
||||
|
||||
case WAIT_TIMEOUT:
|
||||
return PH_STATE_CONNECTED;
|
||||
|
||||
default:
|
||||
DEBUG_FATAL("Error waiting on disconnect event");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch(WaitForSingleObject(handle->events.connect, timeout))
|
||||
{
|
||||
case WAIT_OBJECT_0:
|
||||
handle->connected = true;
|
||||
return PH_STATE_NEW_SESSION;
|
||||
|
||||
case WAIT_TIMEOUT:
|
||||
return PH_STATE_DISCONNECTED;
|
||||
|
||||
default:
|
||||
DEBUG_FATAL("Error waiting on connection event");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PortholeState porthole_dev_get_state(PortholeDev handle)
|
||||
{
|
||||
return get_state(handle, 0);
|
||||
}
|
||||
|
||||
bool porthole_dev_wait_state(PortholeDev handle, const PortholeState state, const unsigned int timeout)
|
||||
{
|
||||
const DWORD to = (timeout == 0) ? INFINITE : timeout;
|
||||
PortholeState lastState = get_state(handle, 0);
|
||||
|
||||
if (state == lastState)
|
||||
return true;
|
||||
|
||||
while(true)
|
||||
{
|
||||
PortholeState nextState;
|
||||
switch(lastState)
|
||||
{
|
||||
case PH_STATE_DISCONNECTED:
|
||||
nextState = PH_STATE_NEW_SESSION;
|
||||
break;
|
||||
|
||||
case PH_STATE_NEW_SESSION:
|
||||
nextState = PH_STATE_CONNECTED;
|
||||
break;
|
||||
|
||||
case PH_STATE_CONNECTED:
|
||||
nextState = PH_STATE_DISCONNECTED;
|
||||
break;
|
||||
}
|
||||
|
||||
PortholeState newState = get_state(handle, to);
|
||||
if (newState == lastState || newState != nextState)
|
||||
return false;
|
||||
|
||||
if (newState == state)
|
||||
return true;
|
||||
|
||||
lastState = newState;
|
||||
}
|
||||
}
|
||||
|
||||
PortholeID porthole_dev_map(PortholeDev handle, const uint32_t type, void *buffer, size_t size)
|
||||
{
|
||||
assert(handle);
|
||||
|
||||
DWORD returned;
|
||||
|
||||
PortholeMsg msg = {
|
||||
.type = type,
|
||||
.addr = buffer,
|
||||
.size = size
|
||||
};
|
||||
|
||||
PortholeMapID out;
|
||||
|
||||
if (!DeviceIoControl(handle->dev, IOCTL_PORTHOLE_SEND_MSG, &msg, sizeof(PortholeMsg), &out, sizeof(PortholeMapID), &returned, NULL))
|
||||
return -1;
|
||||
|
||||
PortholeID ret = out;
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool porthole_dev_unmap(PortholeDev handle, PortholeID id)
|
||||
{
|
||||
assert(handle);
|
||||
|
||||
DWORD returned;
|
||||
|
||||
PortholeMapID msg = id;
|
||||
if (!DeviceIoControl(handle->dev, IOCTL_PORTHOLE_UNLOCK_BUFFER, &msg, sizeof(PortholeMapID), NULL, 0, &returned, NULL))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
27
porthole/src/windows/driver.h
Normal file
27
porthole/src/windows/driver.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#include <windows.h>
|
||||
#include <initguid.h>
|
||||
|
||||
DEFINE_GUID (GUID_DEVINTERFACE_PORTHOLE,
|
||||
0x10ccc0ac,0xf4b0,0x4d78,0xba,0x41,0x1e,0xbb,0x38,0x5a,0x52,0x85);
|
||||
// {10ccc0ac-f4b0-4d78-ba41-1ebb385a5285}
|
||||
|
||||
typedef struct _PortholeMsg
|
||||
{
|
||||
UINT32 type;
|
||||
PVOID addr;
|
||||
UINT32 size;
|
||||
}
|
||||
PortholeMsg, *PPortholeMsg;
|
||||
|
||||
typedef int PortholeMapID, *PPortholeMapID;
|
||||
|
||||
typedef struct _PortholeEvents
|
||||
{
|
||||
HANDLE connect;
|
||||
HANDLE disconnect;
|
||||
}
|
||||
PortholeEvents, *PPortholeEvents;
|
||||
|
||||
#define IOCTL_PORTHOLE_SEND_MSG CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_PORTHOLE_UNLOCK_BUFFER CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_PORTHOLE_REGISTER_EVENTS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
13
vendor/ivshmem/ivshmem.h
vendored
13
vendor/ivshmem/ivshmem.h
vendored
@@ -7,6 +7,19 @@ DEFINE_GUID (GUID_DEVINTERFACE_IVSHMEM,
|
||||
typedef UINT16 IVSHMEM_PEERID;
|
||||
typedef UINT64 IVSHMEM_SIZE;
|
||||
|
||||
#define IVSHMEM_CACHE_NONCACHED 0
|
||||
#define IVSHMEM_CACHE_CACHED 1
|
||||
#define IVSHMEM_CACHE_WRITECOMBINED 2
|
||||
|
||||
/*
|
||||
This structure is for use with the IOCTL_IVSHMEM_REQUEST_MMAP IOCTL
|
||||
*/
|
||||
typedef struct IVSHMEM_MMAP_CONFIG
|
||||
{
|
||||
UINT8 cacheMode; // the caching mode of the mapping, see IVSHMEM_CACHE_* for options
|
||||
}
|
||||
IVSHMEM_MMAP_CONFIG, *PIVSHMEM_MMAP_CONFIG;
|
||||
|
||||
/*
|
||||
This structure is for use with the IOCTL_IVSHMEM_REQUEST_MMAP IOCTL
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user