Compare commits

...

14 Commits

Author SHA1 Message Date
Geoffrey McRae
76710ef201 [all] updated issue template and readme in preperation for B2 2020-10-08 20:04:52 +11:00
Geoffrey McRae
e20c8a5cc7 [host] dxgi: don't try to get the hotspot of a null cursor 2020-10-06 23:24:01 +11:00
Geoffrey McRae
4f4d2dbf42 [host] dxgi: fix memory leak if an error occurs 2020-10-06 22:32:10 +11:00
Geoffrey McRae
8692e9af80 [client] don't hide the cursor when SPICE is disabled
Fixes #304
2020-08-21 15:40:22 +10:00
Geoffrey McRae
7d2b39058c [client] ensure the cursor is updated when the window looses/gains focus 2020-08-20 16:05:55 +10:00
Geoffrey McRae
6927dbecd2 [client] added new input:mouseRedraw option
This new option, when enabled (the default) enables cursor movements to
trigger frame updates in the client, improving responsiveness at the
cost of increased FPS while the mouse is moving around.
2020-08-20 15:50:33 +10:00
Geoffrey McRae
f9b6dcc986 [client] only resync the timer if we got an early frame
This prevents a slow update (ie, 30ups) from pulling the refresh rate
below the minimum (ie, 60fps).
2020-08-20 15:18:45 +10:00
Geoffrey McRae
5c912e3c27 [client] spice: improve mouse syncronization with the host 2020-08-20 14:52:24 +10:00
Geoffrey McRae
7e362050f7 [all] update KVMFR to provide cursor hotspot information
This commit bumps the KVMFR protocol version as it adds additional
hotspot x & y fields to the KVMFRCursor struct. This corrects the issue
of invalid alignment of the local mouse when the shape has an offset
such as the 'I' beam.
2020-08-20 13:51:01 +10:00
Ash
10fbdeb294 update client/README.md: spice:captureOnStart from #278 2020-08-19 23:08:34 +10:00
camr0
72d70e8322 Update host/README.md: c-host -> host 2020-08-17 11:44:52 +10:00
Geoffrey McRae
c66a339bbc [client] egl: ensure overflow occurs for state value checks 2020-08-15 22:39:10 +10:00
Geoffrey McRae
1c7961daeb [host] dxgi: rework locking and retry logic for lower latency 2020-08-15 20:49:49 +10:00
Geoffrey McRae
cdc3384883 [host] dxgi: improve frame signaling mechanics 2020-08-15 18:16:11 +10:00
15 changed files with 327 additions and 140 deletions

View File

@@ -1,11 +1,69 @@
### Required information
### Issues are for Bug Reports and Feature Requests Only!
Host CPU:
Host GPU:
Guest GPU:
Host Kernel version:
Host QEMU version:
If you are looking for help or support please use one of the following methods
Please describe what were you doing when the problem occured. If the Windows host application crashed please check for file named `looking-glass-host.dmp` and attach it to this bug report.
Create a New Topic on the Level1Tech's forum under the Looking Glass category:
* https://forum.level1techs.com/c/software/lookingGlass/142
**Reports that do not include this information will be ignored and closed**
Ask for help in #looking-glass in the VFIO discord server
* https://discord.gg/4ahCn4c
*Issues that are not bug reports or feature requests will be closed & ignored*
### Errors that are not bugs
Some errors generated by the LG client are not bugs, but rather issues with your
system's configuration and/or timing. Please do not report these, but rather use
one of the above resources to ask for advice/help.
* `LGMP_ERR_QUEUE_UNSUBSCRIBED` - Failure to heed advice on things such as
using `isolcpus` and CPU pinning may result in this message, especially if you
are over-taxing your CPU.
* `Could not create an SDL window: *` - Failure to create a SDL window is not an
issue with Looking Glass but rather a more substantial issue with your system,
such as missing hardware support for the RGBA32 pixmap format, or missing
required OpenGL EGL features.
* `The host application is not compatible with this client` - The Looking Glass
Host application in Windows is the incorrect version and is not compatible,
you need to make sure you run matching versions of both the host and client
applications.
### Bug Report Required Information
The entire (not truncated) output from the client application (if applicable).
To obtain this run `looking-glass-client` in a terminal.
```
PASTE CLIENT OUTPUT HERE
```
The entire (not truncated) log file from the host application (if applicable).
To obtain this locate the log file on your system, it will be in one of the
following two locations depending on how you are launching the Looking Glass Host
application:
* C:\Windows\Temp\looking-glass.txt
* C:\Users\YOUR_USER\AppData\Local\Temp\looking-glass.txt
This log may be quite long, please delete the file first and then proceed to
launch the host and reproduce the issue so that the log only contains the
pertinent information.
```
PASTE HOST LOG FILE CONTENTS HERE
```
If the client is unexpetedly exiting without a backtrace, please provide one via
gdb with the command `thread apply all bt`. If you are unsure how to do this
please watch the video below on how to perform a Debug build and generate this
backtrace.
https://www.youtube.com/watch?v=EqxxJK9Yo64
```
PASTE FULL BACKTRACE HERE
```

View File

@@ -4,6 +4,7 @@ An extremely low latency KVMFR (KVM FrameRelay) implementation for guests with
VGA PCI Passthrough.
* Project Website: https://looking-glass.hostfission.com
* Getting Started: https://looking-glass.hostfission.com/wiki/Installation
## Donations
@@ -23,18 +24,20 @@ support me directly using the following platforms.
** IMPORTANT **
This project contains submodules that must be checked out if building from the
git repository!
git repository! If you are not a developer and just want to compile Looking
Glass please download the source archive from the website instead:
https://looking-glass.hostfission.com/downloads
Please also be sure to see the following files for more information
Note: The `README.md` files are slowly being deprecated from this project in
favor of the wiki at https://looking-glass.hostfission.com/wiki, and as such the
information in these files may be dated.
* [client/README.md](client/README.md)
* [host/README.md](host/README.md)
* [module/README.md](module/README.md)
## Obtaining and using Looking Glass
Please see https://looking-glass.hostfission.com/wiki/
## Latest Version
If you would like to use the latest bleeding edge version of Looking Glass please

View File

@@ -1 +1 @@
B2-rc3-33-gdc4d1d49fa+1
B2-rc4-11-g8692e9af80+1

View File

@@ -142,6 +142,7 @@ Command line arguments will override any options loaded from the config files.
| spice:clipboardToVM | | yes | Allow the clipboard to be syncronized TO the VM |
| spice:clipboardToLocal | | yes | Allow the clipboard to be syncronized FROM the VM |
| spice:scaleCursor | -j | yes | Scale cursor input position to screen size when up/down scaled |
| spice:captureOnStart | | no | Capture mouse and keyboard on start |
|------------------------------------------------------------------------------------------------------------------|
|--------------------------------------------------------------------------|

View File

@@ -319,7 +319,7 @@ bool egl_texture_update(EGL_Texture * texture, const uint8_t * buffer)
const uint8_t sw =
atomic_load_explicit(&texture->state.w, memory_order_acquire);
if (atomic_load_explicit(&texture->state.u, memory_order_acquire) == sw + 1)
if (atomic_load_explicit(&texture->state.u, memory_order_acquire) == (uint8_t)(sw + 1))
{
egl_warn_slow();
return true;
@@ -355,7 +355,7 @@ bool egl_texture_update_from_frame(EGL_Texture * texture, const FrameBuffer * fr
const uint8_t sw =
atomic_load_explicit(&texture->state.w, memory_order_acquire);
if (atomic_load_explicit(&texture->state.u, memory_order_acquire) == sw + 1)
if (atomic_load_explicit(&texture->state.u, memory_order_acquire) == (uint8_t)(sw + 1))
{
egl_warn_slow();
return true;
@@ -457,7 +457,7 @@ enum EGL_TexStatus egl_texture_bind(EGL_Texture * texture)
}
}
if (ss != sd && ss != sd+1)
if (ss != sd && ss != (uint8_t)(sd + 1))
sd = atomic_fetch_add_explicit(&texture->state.d, 1,
memory_order_release) + 1;
}

View File

@@ -247,6 +247,13 @@ static struct Option options[] =
.type = OPTION_TYPE_INT,
.value.x_int = 0,
},
{
.module = "input",
.name = "mouseRedraw",
.description = "Mouse movements trigger redraws (ignores FPS minimum)",
.type = OPTION_TYPE_BOOL,
.value.x_bool = true,
},
// spice options
{
@@ -399,6 +406,7 @@ bool config_load(int argc, char * argv[])
params.escapeKey = option_get_int ("input", "escapeKey" );
params.hideMouse = option_get_bool ("input", "hideCursor" );
params.mouseSens = option_get_int ("input", "mouseSens" );
params.mouseRedraw = option_get_bool ("input", "mouseRedraw" );
params.minimizeOnFocusLoss = option_get_bool("win", "minimizeOnFocusLoss");

View File

@@ -213,12 +213,16 @@ static int renderThread(void * unused)
if (atomic_fetch_sub_explicit(&a_framesPending, 1, memory_order_release) > 1)
continue;
if (lgWaitEventAbs(e_frame, &time))
if (lgWaitEventAbs(e_frame, &time) && state.frameTime > 0)
{
if (state.frameTime > 0)
/* only resync the timer if we got an early frame */
struct timespec now, diff;
clock_gettime(CLOCK_REALTIME, &now);
tsDiff(&diff, &now, &time);
if (diff.tv_sec == 0 && diff.tv_nsec < state.frameTime)
{
resyncCheck = 0;
clock_gettime(CLOCK_REALTIME, &time);
memcpy(&time, &now, sizeof(struct timespec));
tsAdd(&time, state.frameTime);
}
}
@@ -277,10 +281,12 @@ static int cursorThread(void * unused)
state.lgr->on_mouse_event
(
state.lgrData,
state.cursorVisible && state.drawCursor && state.cursorInView,
state.cursorVisible && state.drawCursor,
state.cursor.x,
state.cursor.y
);
lgSignalEvent(e_frame);
}
usleep(params.cursorPollInterval);
@@ -302,19 +308,6 @@ static int cursorThread(void * unused)
state.cursorVisible =
msg.udata & CURSOR_FLAG_VISIBLE;
if (msg.udata & CURSOR_FLAG_POSITION)
{
state.cursor.x = cursor->x;
state.cursor.y = cursor->y;
state.haveCursorPos = true;
if (!state.haveAligned && state.haveSrcSize && state.haveCurLocal)
{
alignMouseWithHost();
state.haveAligned = true;
}
}
if (msg.udata & CURSOR_FLAG_SHAPE)
{
switch(cursor->type)
@@ -328,6 +321,9 @@ static int cursorThread(void * unused)
continue;
}
state.cursor.hx = cursor->hx;
state.cursor.hy = cursor->hy;
const uint8_t * data = (const uint8_t *)(cursor + 1);
if (!state.lgr->on_mouse_shape(
state.lgrData,
@@ -344,6 +340,16 @@ static int cursorThread(void * unused)
}
}
if (msg.udata & CURSOR_FLAG_POSITION)
{
state.cursor.x = cursor->x;
state.cursor.y = cursor->y;
state.haveCursorPos = true;
if (state.haveSrcSize && state.haveCurLocal && !state.serverMode)
alignMouseWithGuest();
}
lgmpClientMessageDone(queue);
state.updateCursor = false;
@@ -354,6 +360,9 @@ static int cursorThread(void * unused)
state.cursor.x,
state.cursor.y
);
if (params.mouseRedraw)
lgSignalEvent(e_frame);
}
lgmpClientUnsubscribe(&queue);
@@ -668,10 +677,22 @@ void spiceClipboardRequest(const SpiceDataType type)
state.lgc->request(spice_type_to_clipboard_type(type));
}
static void warpMouse(int x, int y)
{
if (state.warpState != WARP_STATE_ON)
return;
state.warpFromX = state.curLastX;
state.warpFromY = state.curLastY;
state.warpToX = x;
state.warpToY = y;
state.warpState = WARP_STATE_ACTIVE;
SDL_WarpMouseInWindow(state.window, x, y);
}
static void handleMouseMoveEvent(int ex, int ey)
{
static bool wrapping = false;
static int wrapX, wrapY;
state.curLocalX = ex;
state.curLocalY = ey;
@@ -680,28 +701,23 @@ static void handleMouseMoveEvent(int ex, int ey)
if (state.ignoreInput || !params.useSpiceInput)
return;
if (state.warpState == WARP_STATE_ACTIVE)
{
if (ex == state.warpToX && ey == state.warpToY)
{
state.curLastX += state.warpToX - state.warpFromX;
state.curLastY += state.warpToY - state.warpFromY;
state.warpState = WARP_STATE_ON;
}
}
if (state.serverMode)
{
if (wrapping)
if (
ex < 100 || ex > state.windowW - 100 ||
ey < 100 || ey > state.windowH - 100)
{
if (ex == state.windowW / 2 && ey == state.windowH / 2)
{
state.curLastX += (state.windowW / 2) - wrapX;
state.curLastY += (state.windowH / 2) - wrapY;
wrapping = false;
}
}
else
{
if (
ex < 100 || ex > state.windowW - 100 ||
ey < 100 || ey > state.windowH - 100)
{
wrapping = true;
wrapX = ex;
wrapY = ey;
SDL_WarpMouseInWindow(state.window, state.windowW / 2, state.windowH / 2);
}
warpMouse(state.windowW / 2, state.windowH / 2);
}
}
else
@@ -713,6 +729,10 @@ static void handleMouseMoveEvent(int ex, int ey)
{
state.cursorInView = false;
state.updateCursor = true;
state.warpState = WARP_STATE_OFF;
if (params.useSpiceInput)
state.drawCursor = false;
return;
}
}
@@ -721,6 +741,9 @@ static void handleMouseMoveEvent(int ex, int ey)
{
state.cursorInView = true;
state.updateCursor = true;
state.drawCursor = true;
if (state.warpState == WARP_STATE_ARMED)
state.warpState = WARP_STATE_ON;
}
int rx = ex - state.curLastX;
@@ -760,9 +783,9 @@ static void alignMouseWithGuest()
if (state.ignoreInput || !params.useSpiceInput)
return;
state.curLastX = (int)round((float)state.cursor.x / state.scaleX) + state.dstRect.x;
state.curLastY = (int)round((float)state.cursor.y / state.scaleY) + state.dstRect.y;
SDL_WarpMouseInWindow(state.window, state.curLastX, state.curLastY);
state.curLastX = (int)round((float)(state.cursor.x + state.cursor.hx) / state.scaleX) + state.dstRect.x;
state.curLastY = (int)round((float)(state.cursor.y + state.cursor.hy) / state.scaleY) + state.dstRect.y;
warpMouse(state.curLastX, state.curLastY);
}
static void alignMouseWithHost()
@@ -773,8 +796,8 @@ static void alignMouseWithHost()
if (!state.haveCursorPos || state.serverMode)
return;
state.curLastX = (int)round((float)state.cursor.x / state.scaleX) + state.dstRect.x;
state.curLastY = (int)round((float)state.cursor.y / state.scaleY) + state.dstRect.y;
state.curLastX = (int)round((float)(state.cursor.x + state.cursor.hx) / state.scaleX) + state.dstRect.x;
state.curLastY = (int)round((float)(state.cursor.y + state.cursor.hy) / state.scaleY) + state.dstRect.y;
handleMouseMoveEvent(state.curLocalX, state.curLocalY);
}
@@ -796,6 +819,7 @@ static void handleWindowLeave()
state.drawCursor = false;
state.cursorInView = false;
state.updateCursor = true;
state.warpState = WARP_STATE_OFF;
}
static void handleWindowEnter()
@@ -806,6 +830,7 @@ static void handleWindowEnter()
alignMouseWithHost();
state.drawCursor = true;
state.updateCursor = true;
state.warpState = WARP_STATE_ARMED;
}
int eventFilter(void * userdata, SDL_Event * event)
@@ -948,7 +973,9 @@ int eventFilter(void * userdata, SDL_Event * event)
state.serverMode ? "Capture Enabled" : "Capture Disabled"
);
if (!state.serverMode)
if (state.serverMode)
state.warpState = WARP_STATE_ON;
else
alignMouseWithGuest();
}
}
@@ -1331,13 +1358,15 @@ static int lg_run()
// ensure renderer viewport is aware of the current window size
updatePositionInfo();
// use a default of 60FPS now that frame updates are host update triggered
if (params.fpsMin == -1)
state.frameTime = 1e9 / 60;
{
// minimum 60fps to keep interactivity decent
state.frameTime = 1000000000ULL / 60ULL;
}
else
{
DEBUG_INFO("Using the FPS minimum from args: %d", params.fpsMin);
state.frameTime = 1e9 / params.fpsMin;
state.frameTime = 1000000000ULL / (unsigned long long)params.fpsMin;
}
register_key_binds();

View File

@@ -36,6 +36,20 @@ enum RunState
APP_STATE_SHUTDOWN
};
struct CursorInfo
{
int x , y;
int hx, hy;
};
enum WarpState
{
WARP_STATE_ARMED,
WARP_STATE_ON,
WARP_STATE_ACTIVE,
WARP_STATE_OFF
};
struct AppState
{
enum RunState state;
@@ -49,7 +63,7 @@ struct AppState
int windowW, windowH;
SDL_Point srcSize;
LG_RendererRect dstRect;
SDL_Point cursor;
struct CursorInfo cursor;
bool cursorVisible;
bool serverMode;
@@ -67,6 +81,10 @@ struct AppState
int curLocalY;
bool haveAligned;
enum WarpState warpState;
int warpFromX, warpFromY;
int warpToX , warpToY;
const LG_Renderer * lgr;
void * lgrData;
bool lgrResize;
@@ -142,6 +160,7 @@ struct AppParams
const char * windowTitle;
int mouseSens;
bool mouseRedraw;
};
struct CBRequest

View File

@@ -51,7 +51,7 @@ typedef enum CursorType
CursorType;
#define KVMFR_MAGIC "KVMFR---"
#define KVMFR_VERSION 2
#define KVMFR_VERSION 3
typedef struct KVMFR
{
@@ -65,6 +65,7 @@ typedef struct KVMFRCursor
{
int16_t x, y; // cursor x & y position
CursorType type; // shape buffer data type
int8_t hx, hy; // shape hotspot x & y
uint32_t width; // width of the shape
uint32_t height; // height of the shape
uint32_t pitch; // row length in bytes of the shape

View File

@@ -20,8 +20,8 @@ Currently only Windows is supported however there is some initial support for Li
6. configure the project and build it
```
mkdir LookingGlass/c-host/build
cd LookingGlass/c-host/build
mkdir LookingGlass/host/build
cd LookingGlass/host/build
cmake -G "MSYS Makefiles" ..
make
```

View File

@@ -67,6 +67,7 @@ typedef struct CapturePointer
bool shapeUpdate;
CaptureFormat format;
unsigned int hx, hy;
unsigned int width, height;
unsigned int pitch;
}

View File

@@ -26,6 +26,8 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include "common/event.h"
#include <assert.h>
#include <stdatomic.h>
#include <unistd.h>
#include <dxgi.h>
#include <d3d11.h>
#include <d3dcommon.h>
@@ -55,7 +57,7 @@ enum TextureState
typedef struct Texture
{
enum TextureState state;
volatile enum TextureState state;
ID3D11Texture2D * tex;
D3D11_MAPPED_SUBRESOURCE map;
}
@@ -82,7 +84,7 @@ struct iface
Texture * texture;
int texRIndex;
int texWIndex;
volatile int texReady;
atomic_int texReady;
bool needsRelease;
CaptureGetPointerBuffer getPointerBufferFn;
@@ -228,9 +230,9 @@ static bool dxgi_init()
this->stop = false;
this->texRIndex = 0;
this->texWIndex = 0;
this->texReady = 0;
atomic_store(&this->texReady, 0);
lgResetEvent(this->frameEvent );
lgResetEvent(this->frameEvent);
status = CreateDXGIFactory1(&IID_IDXGIFactory1, (void **)&this->factory);
if (FAILED(status))
@@ -705,6 +707,15 @@ static CaptureResult dxgi_capture()
DXGI_OUTDUPL_FRAME_INFO frameInfo;
IDXGIResource * res;
bool copyFrame = false;
bool copyPointer = false;
ID3D11Texture2D * src;
bool postPointer = false;
CapturePointer pointer = { 0 };
void * pointerShape = NULL;
UINT pointerShapeSize = 0;
// release the prior frame
result = dxgi_releaseFrame();
if (result != CAPTURE_RESULT_OK)
@@ -736,7 +747,7 @@ static CaptureResult dxgi_capture()
// check if the texture is free, if not skip the frame to keep up
if (tex->state == TEXTURE_STATE_UNUSED)
{
ID3D11Texture2D * src;
copyFrame = true;
status = IDXGIResource_QueryInterface(res, &IID_ID3D11Texture2D, (void **)&src);
if (FAILED(status))
{
@@ -744,19 +755,51 @@ static CaptureResult dxgi_capture()
IDXGIResource_Release(res);
return CAPTURE_RESULT_ERROR;
}
}
}
LOCKED({
// issue the copy from GPU to CPU RAM and release the src
IDXGIResource_Release(res);
// if the pointer shape has changed
uint32_t bufferSize;
if (frameInfo.PointerShapeBufferSize > 0)
{
if(!this->getPointerBufferFn(&pointerShape, &bufferSize))
DEBUG_WARN("Failed to obtain a buffer for the pointer shape");
else
copyPointer = true;
}
if (copyFrame || copyPointer)
{
DXGI_OUTDUPL_POINTER_SHAPE_INFO shapeInfo;
LOCKED(
{
if (copyFrame)
{
// issue the copy from GPU to CPU RAM
ID3D11DeviceContext_CopyResource(this->deviceContext,
(ID3D11Resource *)tex->tex, (ID3D11Resource *)src);
});
}
if (copyPointer)
{
// grab the pointer shape
status = IDXGIOutputDuplication_GetFramePointerShape(
this->dup, bufferSize, pointerShape, &pointerShapeSize, &shapeInfo);
}
ID3D11DeviceContext_Flush(this->deviceContext);
});
if (copyFrame)
{
ID3D11Texture2D_Release(src);
// set the state, and signal
tex->state = TEXTURE_STATE_PENDING_MAP;
INTERLOCKED_INC(&this->texReady);
lgSignalEvent(this->frameEvent);
if (atomic_fetch_add_explicit(&this->texReady, 1, memory_order_relaxed) == 0)
lgSignalEvent(this->frameEvent);
// advance the write index
if (++this->texWIndex == this->maxTextures)
@@ -765,16 +808,63 @@ static CaptureResult dxgi_capture()
// update the last frame time
this->frameTime.QuadPart = frameInfo.LastPresentTime.QuadPart;
}
if (copyPointer)
{
result = dxgi_hResultToCaptureResult(status);
if (result != CAPTURE_RESULT_OK)
{
if (result == CAPTURE_RESULT_ERROR)
DEBUG_WINERROR("Failed to get the new pointer shape", status);
return result;
}
switch(shapeInfo.Type)
{
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR : pointer.format = CAPTURE_FMT_COLOR ; break;
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR: pointer.format = CAPTURE_FMT_MASKED; break;
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME : pointer.format = CAPTURE_FMT_MONO ; break;
default:
DEBUG_ERROR("Unsupported cursor format");
return CAPTURE_RESULT_ERROR;
}
CURSORINFO ci = { .cbSize = sizeof(CURSORINFO) };
if (!GetCursorInfo(&ci))
{
DEBUG_WINERROR("GetCursorInfo failed", GetLastError());
return CAPTURE_RESULT_ERROR;
}
if (ci.hCursor)
{
ICONINFO ii;
if (!GetIconInfo(ci.hCursor, &ii))
{
DEBUG_WINERROR("GetIconInfo failed", GetLastError());
return CAPTURE_RESULT_ERROR;
}
DeleteObject(ii.hbmMask);
DeleteObject(ii.hbmColor);
pointer.hx = ii.xHotspot;
pointer.hy = ii.yHotspot;
}
else
{
pointer.hx = 0;
pointer.hy = 0;
}
pointer.shapeUpdate = true;
pointer.width = shapeInfo.Width;
pointer.height = shapeInfo.Height;
pointer.pitch = shapeInfo.Pitch;
postPointer = true;
}
}
IDXGIResource_Release(res);
// if the pointer has moved or changed state
bool postPointer = false;
CapturePointer pointer = { 0 };
void * pointerShape = NULL;
UINT pointerShapeSize = 0;
if (frameInfo.LastMouseUpdateTime.QuadPart)
{
/* the pointer position is only valid if the pointer is visible */
@@ -799,43 +889,6 @@ static CaptureResult dxgi_capture()
}
}
// if the pointer shape has changed
if (frameInfo.PointerShapeBufferSize > 0)
{
uint32_t bufferSize;
if(!this->getPointerBufferFn(&pointerShape, &bufferSize))
DEBUG_WARN("Failed to obtain a buffer for the pointer shape");
else
{
DXGI_OUTDUPL_POINTER_SHAPE_INFO shapeInfo;
LOCKED({status = IDXGIOutputDuplication_GetFramePointerShape(this->dup, bufferSize, pointerShape, &pointerShapeSize, &shapeInfo);});
result = dxgi_hResultToCaptureResult(status);
if (result != CAPTURE_RESULT_OK)
{
if (result == CAPTURE_RESULT_ERROR)
DEBUG_WINERROR("Failed to get the new pointer shape", status);
return result;
}
switch(shapeInfo.Type)
{
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR : pointer.format = CAPTURE_FMT_COLOR ; break;
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR: pointer.format = CAPTURE_FMT_MASKED; break;
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME : pointer.format = CAPTURE_FMT_MONO ; break;
default:
DEBUG_ERROR("Unsupported cursor format");
return CAPTURE_RESULT_ERROR;
}
pointer.shapeUpdate = true;
pointer.width = shapeInfo.Width;
pointer.height = shapeInfo.Height;
pointer.pitch = shapeInfo.Pitch;
postPointer = true;
}
}
// post back the pointer information
if (postPointer)
{
@@ -852,27 +905,39 @@ static CaptureResult dxgi_waitFrame(CaptureFrame * frame)
assert(this->initialized);
// NOTE: the event may be signaled when there are no frames available
if(this->texReady == 0)
if(atomic_load_explicit(&this->texReady, memory_order_acquire) == 0)
{
if (!lgWaitEvent(this->frameEvent, 1000))
return CAPTURE_RESULT_TIMEOUT;
if (this->texReady == 0)
// the count will still be zero if we are stopping
if(atomic_load_explicit(&this->texReady, memory_order_acquire) == 0)
return CAPTURE_RESULT_TIMEOUT;
}
Texture * tex = &this->texture[this->texRIndex];
// try to map the resource, but don't wait for it
HRESULT status;
LOCKED({status = ID3D11DeviceContext_Map(this->deviceContext, (ID3D11Resource*)tex->tex, 0, D3D11_MAP_READ, 0x100000L, &tex->map);});
if (status == DXGI_ERROR_WAS_STILL_DRAWING)
return CAPTURE_RESULT_TIMEOUT;
if (FAILED(status))
for (int i = 0; ; ++i)
{
DEBUG_WINERROR("Failed to map the texture", status);
return CAPTURE_RESULT_ERROR;
HRESULT status;
LOCKED({status = ID3D11DeviceContext_Map(this->deviceContext, (ID3D11Resource*)tex->tex, 0, D3D11_MAP_READ, 0x100000L, &tex->map);});
if (status == DXGI_ERROR_WAS_STILL_DRAWING)
{
if (i == 100)
return CAPTURE_RESULT_TIMEOUT;
usleep(1);
continue;
}
if (FAILED(status))
{
DEBUG_WINERROR("Failed to map the texture", status);
return CAPTURE_RESULT_ERROR;
}
break;
}
tex->state = TEXTURE_STATE_MAPPED;
@@ -883,7 +948,7 @@ static CaptureResult dxgi_waitFrame(CaptureFrame * frame)
frame->stride = this->stride;
frame->format = this->format;
INTERLOCKED_DEC(&this->texReady);
atomic_fetch_sub_explicit(&this->texReady, 1, memory_order_release);
return CAPTURE_RESULT_OK;
}

View File

@@ -352,8 +352,8 @@ static int pointerThread(void * unused)
}
this->mouseVisible = pointer.visible;
this->mouseHotX = pointer.x;
this->mouseHotY = pointer.y;
this->mouseHotX = pointer.hx;
this->mouseHotY = pointer.hy;
}
if (events[0])

View File

@@ -288,8 +288,8 @@ CaptureResult NvFBCToSysGetCursor(NvFBCHandle handle, CapturePointer * pointer,
return CAPTURE_RESULT_ERROR;
}
pointer->x = params.dwXHotSpot;
pointer->y = params.dwYHotSpot;
pointer->hx = params.dwXHotSpot;
pointer->hy = params.dwYHotSpot;
pointer->width = params.dwWidth;
pointer->height = params.dwHeight;
pointer->pitch = params.dwPitch;
@@ -327,4 +327,4 @@ CaptureResult NvFBCToSysGetCursor(NvFBCHandle handle, CapturePointer * pointer,
memcpy(buffer, params.pBits, params.dwBufferSize);
return CAPTURE_RESULT_OK;
}
}

View File

@@ -336,7 +336,8 @@ static void sendPointer(bool newClient)
if (app.pointerInfo.shapeUpdate)
{
// remember which slot has the latest shape
cursor->hx = app.pointerInfo.hx;
cursor->hy = app.pointerInfo.hy;
cursor->width = app.pointerInfo.width;
cursor->height = app.pointerInfo.height;
cursor->pitch = app.pointerInfo.pitch;
@@ -489,6 +490,7 @@ int app_main(int argc, char * argv[])
DEBUG_ERROR("lgmpHostMemAlloc Failed (Pointer): %s", lgmpStatusString(status));
goto fail;
}
memset(lgmpHostMemPtr(app.pointerMemory[i]), 0, MAX_POINTER_SIZE);
}
app.pointerShapeValid = false;