mirror of
https://github.com/gnif/LookingGlass.git
synced 2025-12-16 13:08:13 +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:
|
If you are looking for help or support please use one of the following methods
|
||||||
Host GPU:
|
|
||||||
Guest GPU:
|
|
||||||
Host Kernel version:
|
|
||||||
Host QEMU version:
|
|
||||||
|
|
||||||
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.
|
VGA PCI Passthrough.
|
||||||
|
|
||||||
* Project Website: https://looking-glass.hostfission.com
|
* Project Website: https://looking-glass.hostfission.com
|
||||||
|
* Getting Started: https://looking-glass.hostfission.com/wiki/Installation
|
||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
@@ -23,18 +24,20 @@ support me directly using the following platforms.
|
|||||||
|
|
||||||
** IMPORTANT **
|
** IMPORTANT **
|
||||||
This project contains submodules that must be checked out if building from the
|
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
|
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)
|
* [client/README.md](client/README.md)
|
||||||
* [host/README.md](host/README.md)
|
* [host/README.md](host/README.md)
|
||||||
* [module/README.md](module/README.md)
|
* [module/README.md](module/README.md)
|
||||||
|
|
||||||
## Obtaining and using Looking Glass
|
|
||||||
|
|
||||||
Please see https://looking-glass.hostfission.com/wiki/
|
|
||||||
|
|
||||||
## Latest Version
|
## Latest Version
|
||||||
|
|
||||||
If you would like to use the latest bleeding edge version of Looking Glass please
|
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:clipboardToVM | | yes | Allow the clipboard to be syncronized TO the VM |
|
||||||
| spice:clipboardToLocal | | yes | Allow the clipboard to be syncronized FROM 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: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 =
|
const uint8_t sw =
|
||||||
atomic_load_explicit(&texture->state.w, memory_order_acquire);
|
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();
|
egl_warn_slow();
|
||||||
return true;
|
return true;
|
||||||
@@ -355,7 +355,7 @@ bool egl_texture_update_from_frame(EGL_Texture * texture, const FrameBuffer * fr
|
|||||||
const uint8_t sw =
|
const uint8_t sw =
|
||||||
atomic_load_explicit(&texture->state.w, memory_order_acquire);
|
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();
|
egl_warn_slow();
|
||||||
return true;
|
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,
|
sd = atomic_fetch_add_explicit(&texture->state.d, 1,
|
||||||
memory_order_release) + 1;
|
memory_order_release) + 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -247,6 +247,13 @@ static struct Option options[] =
|
|||||||
.type = OPTION_TYPE_INT,
|
.type = OPTION_TYPE_INT,
|
||||||
.value.x_int = 0,
|
.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
|
// spice options
|
||||||
{
|
{
|
||||||
@@ -399,6 +406,7 @@ bool config_load(int argc, char * argv[])
|
|||||||
params.escapeKey = option_get_int ("input", "escapeKey" );
|
params.escapeKey = option_get_int ("input", "escapeKey" );
|
||||||
params.hideMouse = option_get_bool ("input", "hideCursor" );
|
params.hideMouse = option_get_bool ("input", "hideCursor" );
|
||||||
params.mouseSens = option_get_int ("input", "mouseSens" );
|
params.mouseSens = option_get_int ("input", "mouseSens" );
|
||||||
|
params.mouseRedraw = option_get_bool ("input", "mouseRedraw" );
|
||||||
|
|
||||||
params.minimizeOnFocusLoss = option_get_bool("win", "minimizeOnFocusLoss");
|
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)
|
if (atomic_fetch_sub_explicit(&a_framesPending, 1, memory_order_release) > 1)
|
||||||
continue;
|
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;
|
resyncCheck = 0;
|
||||||
clock_gettime(CLOCK_REALTIME, &time);
|
memcpy(&time, &now, sizeof(struct timespec));
|
||||||
tsAdd(&time, state.frameTime);
|
tsAdd(&time, state.frameTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -277,10 +281,12 @@ static int cursorThread(void * unused)
|
|||||||
state.lgr->on_mouse_event
|
state.lgr->on_mouse_event
|
||||||
(
|
(
|
||||||
state.lgrData,
|
state.lgrData,
|
||||||
state.cursorVisible && state.drawCursor && state.cursorInView,
|
state.cursorVisible && state.drawCursor,
|
||||||
state.cursor.x,
|
state.cursor.x,
|
||||||
state.cursor.y
|
state.cursor.y
|
||||||
);
|
);
|
||||||
|
|
||||||
|
lgSignalEvent(e_frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
usleep(params.cursorPollInterval);
|
usleep(params.cursorPollInterval);
|
||||||
@@ -302,19 +308,6 @@ static int cursorThread(void * unused)
|
|||||||
state.cursorVisible =
|
state.cursorVisible =
|
||||||
msg.udata & CURSOR_FLAG_VISIBLE;
|
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)
|
if (msg.udata & CURSOR_FLAG_SHAPE)
|
||||||
{
|
{
|
||||||
switch(cursor->type)
|
switch(cursor->type)
|
||||||
@@ -328,6 +321,9 @@ static int cursorThread(void * unused)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.cursor.hx = cursor->hx;
|
||||||
|
state.cursor.hy = cursor->hy;
|
||||||
|
|
||||||
const uint8_t * data = (const uint8_t *)(cursor + 1);
|
const uint8_t * data = (const uint8_t *)(cursor + 1);
|
||||||
if (!state.lgr->on_mouse_shape(
|
if (!state.lgr->on_mouse_shape(
|
||||||
state.lgrData,
|
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);
|
lgmpClientMessageDone(queue);
|
||||||
state.updateCursor = false;
|
state.updateCursor = false;
|
||||||
|
|
||||||
@@ -354,6 +360,9 @@ static int cursorThread(void * unused)
|
|||||||
state.cursor.x,
|
state.cursor.x,
|
||||||
state.cursor.y
|
state.cursor.y
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (params.mouseRedraw)
|
||||||
|
lgSignalEvent(e_frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
lgmpClientUnsubscribe(&queue);
|
lgmpClientUnsubscribe(&queue);
|
||||||
@@ -668,10 +677,22 @@ void spiceClipboardRequest(const SpiceDataType type)
|
|||||||
state.lgc->request(spice_type_to_clipboard_type(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 void handleMouseMoveEvent(int ex, int ey)
|
||||||
{
|
{
|
||||||
static bool wrapping = false;
|
|
||||||
static int wrapX, wrapY;
|
|
||||||
|
|
||||||
state.curLocalX = ex;
|
state.curLocalX = ex;
|
||||||
state.curLocalY = ey;
|
state.curLocalY = ey;
|
||||||
@@ -680,28 +701,23 @@ static void handleMouseMoveEvent(int ex, int ey)
|
|||||||
if (state.ignoreInput || !params.useSpiceInput)
|
if (state.ignoreInput || !params.useSpiceInput)
|
||||||
return;
|
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 (state.serverMode)
|
||||||
{
|
|
||||||
if (wrapping)
|
|
||||||
{
|
|
||||||
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 (
|
if (
|
||||||
ex < 100 || ex > state.windowW - 100 ||
|
ex < 100 || ex > state.windowW - 100 ||
|
||||||
ey < 100 || ey > state.windowH - 100)
|
ey < 100 || ey > state.windowH - 100)
|
||||||
{
|
{
|
||||||
wrapping = true;
|
warpMouse(state.windowW / 2, state.windowH / 2);
|
||||||
wrapX = ex;
|
|
||||||
wrapY = ey;
|
|
||||||
SDL_WarpMouseInWindow(state.window, state.windowW / 2, state.windowH / 2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -713,6 +729,10 @@ static void handleMouseMoveEvent(int ex, int ey)
|
|||||||
{
|
{
|
||||||
state.cursorInView = false;
|
state.cursorInView = false;
|
||||||
state.updateCursor = true;
|
state.updateCursor = true;
|
||||||
|
state.warpState = WARP_STATE_OFF;
|
||||||
|
|
||||||
|
if (params.useSpiceInput)
|
||||||
|
state.drawCursor = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -721,6 +741,9 @@ static void handleMouseMoveEvent(int ex, int ey)
|
|||||||
{
|
{
|
||||||
state.cursorInView = true;
|
state.cursorInView = true;
|
||||||
state.updateCursor = true;
|
state.updateCursor = true;
|
||||||
|
state.drawCursor = true;
|
||||||
|
if (state.warpState == WARP_STATE_ARMED)
|
||||||
|
state.warpState = WARP_STATE_ON;
|
||||||
}
|
}
|
||||||
|
|
||||||
int rx = ex - state.curLastX;
|
int rx = ex - state.curLastX;
|
||||||
@@ -760,9 +783,9 @@ static void alignMouseWithGuest()
|
|||||||
if (state.ignoreInput || !params.useSpiceInput)
|
if (state.ignoreInput || !params.useSpiceInput)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
state.curLastX = (int)round((float)state.cursor.x / state.scaleX) + state.dstRect.x;
|
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.scaleY) + state.dstRect.y;
|
state.curLastY = (int)round((float)(state.cursor.y + state.cursor.hy) / state.scaleY) + state.dstRect.y;
|
||||||
SDL_WarpMouseInWindow(state.window, state.curLastX, state.curLastY);
|
warpMouse(state.curLastX, state.curLastY);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void alignMouseWithHost()
|
static void alignMouseWithHost()
|
||||||
@@ -773,8 +796,8 @@ static void alignMouseWithHost()
|
|||||||
if (!state.haveCursorPos || state.serverMode)
|
if (!state.haveCursorPos || state.serverMode)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
state.curLastX = (int)round((float)state.cursor.x / state.scaleX) + state.dstRect.x;
|
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.scaleY) + state.dstRect.y;
|
state.curLastY = (int)round((float)(state.cursor.y + state.cursor.hy) / state.scaleY) + state.dstRect.y;
|
||||||
handleMouseMoveEvent(state.curLocalX, state.curLocalY);
|
handleMouseMoveEvent(state.curLocalX, state.curLocalY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -796,6 +819,7 @@ static void handleWindowLeave()
|
|||||||
state.drawCursor = false;
|
state.drawCursor = false;
|
||||||
state.cursorInView = false;
|
state.cursorInView = false;
|
||||||
state.updateCursor = true;
|
state.updateCursor = true;
|
||||||
|
state.warpState = WARP_STATE_OFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handleWindowEnter()
|
static void handleWindowEnter()
|
||||||
@@ -806,6 +830,7 @@ static void handleWindowEnter()
|
|||||||
alignMouseWithHost();
|
alignMouseWithHost();
|
||||||
state.drawCursor = true;
|
state.drawCursor = true;
|
||||||
state.updateCursor = true;
|
state.updateCursor = true;
|
||||||
|
state.warpState = WARP_STATE_ARMED;
|
||||||
}
|
}
|
||||||
|
|
||||||
int eventFilter(void * userdata, SDL_Event * event)
|
int eventFilter(void * userdata, SDL_Event * event)
|
||||||
@@ -948,7 +973,9 @@ int eventFilter(void * userdata, SDL_Event * event)
|
|||||||
state.serverMode ? "Capture Enabled" : "Capture Disabled"
|
state.serverMode ? "Capture Enabled" : "Capture Disabled"
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!state.serverMode)
|
if (state.serverMode)
|
||||||
|
state.warpState = WARP_STATE_ON;
|
||||||
|
else
|
||||||
alignMouseWithGuest();
|
alignMouseWithGuest();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1331,13 +1358,15 @@ static int lg_run()
|
|||||||
// ensure renderer viewport is aware of the current window size
|
// ensure renderer viewport is aware of the current window size
|
||||||
updatePositionInfo();
|
updatePositionInfo();
|
||||||
|
|
||||||
// use a default of 60FPS now that frame updates are host update triggered
|
|
||||||
if (params.fpsMin == -1)
|
if (params.fpsMin == -1)
|
||||||
state.frameTime = 1e9 / 60;
|
{
|
||||||
|
// minimum 60fps to keep interactivity decent
|
||||||
|
state.frameTime = 1000000000ULL / 60ULL;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DEBUG_INFO("Using the FPS minimum from args: %d", params.fpsMin);
|
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();
|
register_key_binds();
|
||||||
|
|||||||
@@ -36,6 +36,20 @@ enum RunState
|
|||||||
APP_STATE_SHUTDOWN
|
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
|
struct AppState
|
||||||
{
|
{
|
||||||
enum RunState state;
|
enum RunState state;
|
||||||
@@ -49,7 +63,7 @@ struct AppState
|
|||||||
int windowW, windowH;
|
int windowW, windowH;
|
||||||
SDL_Point srcSize;
|
SDL_Point srcSize;
|
||||||
LG_RendererRect dstRect;
|
LG_RendererRect dstRect;
|
||||||
SDL_Point cursor;
|
struct CursorInfo cursor;
|
||||||
bool cursorVisible;
|
bool cursorVisible;
|
||||||
|
|
||||||
bool serverMode;
|
bool serverMode;
|
||||||
@@ -67,6 +81,10 @@ struct AppState
|
|||||||
int curLocalY;
|
int curLocalY;
|
||||||
bool haveAligned;
|
bool haveAligned;
|
||||||
|
|
||||||
|
enum WarpState warpState;
|
||||||
|
int warpFromX, warpFromY;
|
||||||
|
int warpToX , warpToY;
|
||||||
|
|
||||||
const LG_Renderer * lgr;
|
const LG_Renderer * lgr;
|
||||||
void * lgrData;
|
void * lgrData;
|
||||||
bool lgrResize;
|
bool lgrResize;
|
||||||
@@ -142,6 +160,7 @@ struct AppParams
|
|||||||
|
|
||||||
const char * windowTitle;
|
const char * windowTitle;
|
||||||
int mouseSens;
|
int mouseSens;
|
||||||
|
bool mouseRedraw;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CBRequest
|
struct CBRequest
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ typedef enum CursorType
|
|||||||
CursorType;
|
CursorType;
|
||||||
|
|
||||||
#define KVMFR_MAGIC "KVMFR---"
|
#define KVMFR_MAGIC "KVMFR---"
|
||||||
#define KVMFR_VERSION 2
|
#define KVMFR_VERSION 3
|
||||||
|
|
||||||
typedef struct KVMFR
|
typedef struct KVMFR
|
||||||
{
|
{
|
||||||
@@ -65,6 +65,7 @@ typedef struct KVMFRCursor
|
|||||||
{
|
{
|
||||||
int16_t x, y; // cursor x & y position
|
int16_t x, y; // cursor x & y position
|
||||||
CursorType type; // shape buffer data type
|
CursorType type; // shape buffer data type
|
||||||
|
int8_t hx, hy; // shape hotspot x & y
|
||||||
uint32_t width; // width of the shape
|
uint32_t width; // width of the shape
|
||||||
uint32_t height; // height of the shape
|
uint32_t height; // height of the shape
|
||||||
uint32_t pitch; // row length in bytes 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
|
6. configure the project and build it
|
||||||
|
|
||||||
```
|
```
|
||||||
mkdir LookingGlass/c-host/build
|
mkdir LookingGlass/host/build
|
||||||
cd LookingGlass/c-host/build
|
cd LookingGlass/host/build
|
||||||
cmake -G "MSYS Makefiles" ..
|
cmake -G "MSYS Makefiles" ..
|
||||||
make
|
make
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ typedef struct CapturePointer
|
|||||||
|
|
||||||
bool shapeUpdate;
|
bool shapeUpdate;
|
||||||
CaptureFormat format;
|
CaptureFormat format;
|
||||||
|
unsigned int hx, hy;
|
||||||
unsigned int width, height;
|
unsigned int width, height;
|
||||||
unsigned int pitch;
|
unsigned int pitch;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
#include "common/event.h"
|
#include "common/event.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <stdatomic.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include <dxgi.h>
|
#include <dxgi.h>
|
||||||
#include <d3d11.h>
|
#include <d3d11.h>
|
||||||
#include <d3dcommon.h>
|
#include <d3dcommon.h>
|
||||||
@@ -55,7 +57,7 @@ enum TextureState
|
|||||||
|
|
||||||
typedef struct Texture
|
typedef struct Texture
|
||||||
{
|
{
|
||||||
enum TextureState state;
|
volatile enum TextureState state;
|
||||||
ID3D11Texture2D * tex;
|
ID3D11Texture2D * tex;
|
||||||
D3D11_MAPPED_SUBRESOURCE map;
|
D3D11_MAPPED_SUBRESOURCE map;
|
||||||
}
|
}
|
||||||
@@ -82,7 +84,7 @@ struct iface
|
|||||||
Texture * texture;
|
Texture * texture;
|
||||||
int texRIndex;
|
int texRIndex;
|
||||||
int texWIndex;
|
int texWIndex;
|
||||||
volatile int texReady;
|
atomic_int texReady;
|
||||||
bool needsRelease;
|
bool needsRelease;
|
||||||
|
|
||||||
CaptureGetPointerBuffer getPointerBufferFn;
|
CaptureGetPointerBuffer getPointerBufferFn;
|
||||||
@@ -228,9 +230,9 @@ static bool dxgi_init()
|
|||||||
this->stop = false;
|
this->stop = false;
|
||||||
this->texRIndex = 0;
|
this->texRIndex = 0;
|
||||||
this->texWIndex = 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);
|
status = CreateDXGIFactory1(&IID_IDXGIFactory1, (void **)&this->factory);
|
||||||
if (FAILED(status))
|
if (FAILED(status))
|
||||||
@@ -705,6 +707,15 @@ static CaptureResult dxgi_capture()
|
|||||||
DXGI_OUTDUPL_FRAME_INFO frameInfo;
|
DXGI_OUTDUPL_FRAME_INFO frameInfo;
|
||||||
IDXGIResource * res;
|
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
|
// release the prior frame
|
||||||
result = dxgi_releaseFrame();
|
result = dxgi_releaseFrame();
|
||||||
if (result != CAPTURE_RESULT_OK)
|
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
|
// check if the texture is free, if not skip the frame to keep up
|
||||||
if (tex->state == TEXTURE_STATE_UNUSED)
|
if (tex->state == TEXTURE_STATE_UNUSED)
|
||||||
{
|
{
|
||||||
ID3D11Texture2D * src;
|
copyFrame = true;
|
||||||
status = IDXGIResource_QueryInterface(res, &IID_ID3D11Texture2D, (void **)&src);
|
status = IDXGIResource_QueryInterface(res, &IID_ID3D11Texture2D, (void **)&src);
|
||||||
if (FAILED(status))
|
if (FAILED(status))
|
||||||
{
|
{
|
||||||
@@ -744,18 +755,50 @@ static CaptureResult dxgi_capture()
|
|||||||
IDXGIResource_Release(res);
|
IDXGIResource_Release(res);
|
||||||
return CAPTURE_RESULT_ERROR;
|
return CAPTURE_RESULT_ERROR;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LOCKED({
|
IDXGIResource_Release(res);
|
||||||
// issue the copy from GPU to CPU RAM and release the src
|
|
||||||
|
// 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,
|
ID3D11DeviceContext_CopyResource(this->deviceContext,
|
||||||
(ID3D11Resource *)tex->tex, (ID3D11Resource *)src);
|
(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);
|
ID3D11Texture2D_Release(src);
|
||||||
|
|
||||||
// set the state, and signal
|
// set the state, and signal
|
||||||
tex->state = TEXTURE_STATE_PENDING_MAP;
|
tex->state = TEXTURE_STATE_PENDING_MAP;
|
||||||
INTERLOCKED_INC(&this->texReady);
|
if (atomic_fetch_add_explicit(&this->texReady, 1, memory_order_relaxed) == 0)
|
||||||
lgSignalEvent(this->frameEvent);
|
lgSignalEvent(this->frameEvent);
|
||||||
|
|
||||||
// advance the write index
|
// advance the write index
|
||||||
@@ -765,15 +808,62 @@ static CaptureResult dxgi_capture()
|
|||||||
// update the last frame time
|
// update the last frame time
|
||||||
this->frameTime.QuadPart = frameInfo.LastPresentTime.QuadPart;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
IDXGIResource_Release(res);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
// if the pointer has moved or changed state
|
CURSORINFO ci = { .cbSize = sizeof(CURSORINFO) };
|
||||||
bool postPointer = false;
|
if (!GetCursorInfo(&ci))
|
||||||
CapturePointer pointer = { 0 };
|
{
|
||||||
void * pointerShape = NULL;
|
DEBUG_WINERROR("GetCursorInfo failed", GetLastError());
|
||||||
UINT pointerShapeSize = 0;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (frameInfo.LastMouseUpdateTime.QuadPart)
|
if (frameInfo.LastMouseUpdateTime.QuadPart)
|
||||||
{
|
{
|
||||||
@@ -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
|
// post back the pointer information
|
||||||
if (postPointer)
|
if (postPointer)
|
||||||
{
|
{
|
||||||
@@ -852,29 +905,41 @@ static CaptureResult dxgi_waitFrame(CaptureFrame * frame)
|
|||||||
assert(this->initialized);
|
assert(this->initialized);
|
||||||
|
|
||||||
// NOTE: the event may be signaled when there are no frames available
|
// 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))
|
if (!lgWaitEvent(this->frameEvent, 1000))
|
||||||
return CAPTURE_RESULT_TIMEOUT;
|
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;
|
return CAPTURE_RESULT_TIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture * tex = &this->texture[this->texRIndex];
|
Texture * tex = &this->texture[this->texRIndex];
|
||||||
|
|
||||||
// try to map the resource, but don't wait for it
|
// try to map the resource, but don't wait for it
|
||||||
|
for (int i = 0; ; ++i)
|
||||||
|
{
|
||||||
HRESULT status;
|
HRESULT status;
|
||||||
LOCKED({status = ID3D11DeviceContext_Map(this->deviceContext, (ID3D11Resource*)tex->tex, 0, D3D11_MAP_READ, 0x100000L, &tex->map);});
|
LOCKED({status = ID3D11DeviceContext_Map(this->deviceContext, (ID3D11Resource*)tex->tex, 0, D3D11_MAP_READ, 0x100000L, &tex->map);});
|
||||||
if (status == DXGI_ERROR_WAS_STILL_DRAWING)
|
if (status == DXGI_ERROR_WAS_STILL_DRAWING)
|
||||||
|
{
|
||||||
|
if (i == 100)
|
||||||
return CAPTURE_RESULT_TIMEOUT;
|
return CAPTURE_RESULT_TIMEOUT;
|
||||||
|
|
||||||
|
usleep(1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (FAILED(status))
|
if (FAILED(status))
|
||||||
{
|
{
|
||||||
DEBUG_WINERROR("Failed to map the texture", status);
|
DEBUG_WINERROR("Failed to map the texture", status);
|
||||||
return CAPTURE_RESULT_ERROR;
|
return CAPTURE_RESULT_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
tex->state = TEXTURE_STATE_MAPPED;
|
tex->state = TEXTURE_STATE_MAPPED;
|
||||||
|
|
||||||
frame->width = this->width;
|
frame->width = this->width;
|
||||||
@@ -883,7 +948,7 @@ static CaptureResult dxgi_waitFrame(CaptureFrame * frame)
|
|||||||
frame->stride = this->stride;
|
frame->stride = this->stride;
|
||||||
frame->format = this->format;
|
frame->format = this->format;
|
||||||
|
|
||||||
INTERLOCKED_DEC(&this->texReady);
|
atomic_fetch_sub_explicit(&this->texReady, 1, memory_order_release);
|
||||||
return CAPTURE_RESULT_OK;
|
return CAPTURE_RESULT_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -352,8 +352,8 @@ static int pointerThread(void * unused)
|
|||||||
}
|
}
|
||||||
|
|
||||||
this->mouseVisible = pointer.visible;
|
this->mouseVisible = pointer.visible;
|
||||||
this->mouseHotX = pointer.x;
|
this->mouseHotX = pointer.hx;
|
||||||
this->mouseHotY = pointer.y;
|
this->mouseHotY = pointer.hy;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (events[0])
|
if (events[0])
|
||||||
|
|||||||
@@ -288,8 +288,8 @@ CaptureResult NvFBCToSysGetCursor(NvFBCHandle handle, CapturePointer * pointer,
|
|||||||
return CAPTURE_RESULT_ERROR;
|
return CAPTURE_RESULT_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
pointer->x = params.dwXHotSpot;
|
pointer->hx = params.dwXHotSpot;
|
||||||
pointer->y = params.dwYHotSpot;
|
pointer->hy = params.dwYHotSpot;
|
||||||
pointer->width = params.dwWidth;
|
pointer->width = params.dwWidth;
|
||||||
pointer->height = params.dwHeight;
|
pointer->height = params.dwHeight;
|
||||||
pointer->pitch = params.dwPitch;
|
pointer->pitch = params.dwPitch;
|
||||||
|
|||||||
@@ -336,7 +336,8 @@ static void sendPointer(bool newClient)
|
|||||||
|
|
||||||
if (app.pointerInfo.shapeUpdate)
|
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->width = app.pointerInfo.width;
|
||||||
cursor->height = app.pointerInfo.height;
|
cursor->height = app.pointerInfo.height;
|
||||||
cursor->pitch = app.pointerInfo.pitch;
|
cursor->pitch = app.pointerInfo.pitch;
|
||||||
@@ -489,6 +490,7 @@ int app_main(int argc, char * argv[])
|
|||||||
DEBUG_ERROR("lgmpHostMemAlloc Failed (Pointer): %s", lgmpStatusString(status));
|
DEBUG_ERROR("lgmpHostMemAlloc Failed (Pointer): %s", lgmpStatusString(status));
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
memset(lgmpHostMemPtr(app.pointerMemory[i]), 0, MAX_POINTER_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
app.pointerShapeValid = false;
|
app.pointerShapeValid = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user