mirror of
https://github.com/gnif/LookingGlass.git
synced 2025-12-15 12:38:14 +00:00
Compare commits
14 Commits
B2-rc4
...
Release/B2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76710ef201 | ||
|
|
e20c8a5cc7 | ||
|
|
4f4d2dbf42 | ||
|
|
8692e9af80 | ||
|
|
7d2b39058c | ||
|
|
6927dbecd2 | ||
|
|
f9b6dcc986 | ||
|
|
5c912e3c27 | ||
|
|
7e362050f7 | ||
|
|
10fbdeb294 | ||
|
|
72d70e8322 | ||
|
|
c66a339bbc | ||
|
|
1c7961daeb | ||
|
|
cdc3384883 |
74
.github/issue_template.md
vendored
74
.github/issue_template.md
vendored
@@ -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
|
||||
```
|
||||
|
||||
13
README.md
13
README.md
@@ -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
|
||||
|
||||
@@ -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 |
|
||||
|------------------------------------------------------------------------------------------------------------------|
|
||||
|
||||
|--------------------------------------------------------------------------|
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -67,6 +67,7 @@ typedef struct CapturePointer
|
||||
|
||||
bool shapeUpdate;
|
||||
CaptureFormat format;
|
||||
unsigned int hx, hy;
|
||||
unsigned int width, height;
|
||||
unsigned int pitch;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user