Compare commits

..

43 Commits

Author SHA1 Message Date
Quantum
2973319bff [client] opengl: remove glu dependency
We only use gluOrtho2D, which is trivially replaced with glOrtho, and
gluErrorString which can be replaced with a small lookup table.
2021-02-20 12:44:32 +11:00
Quantum
ec921d7f39 [client] spice: correctly ungrab keyboard when grabKeyboardOnFocus=no
When input:grabKeyboardOnFocus=no, exiting capture mode should ungrab
the keyboard. Otherwise, focusing the window doesn't grab the keyboard,
but toggling capture mode would leave the keyboard stuck in a grabbed
state until defocused.
2021-02-09 20:06:27 +11:00
Tudor Brindus
637a7625d2 [client] wayland: allow EGL/OpenGL vsync to be set to on
This effectively reverts 4bceaf5.

Upstream ticket: https://gitlab.freedesktop.org/mesa/mesa/-/issues/4180

Commit 941c651 makes working around the hang in LG itself not as
annoying as before.

In the future, we can bypass this entire issue by implementing our own
swapchain and listening to frame callbacks ourselves.
2021-02-09 09:12:57 +11:00
Jonathan Rubenstein
32d8a47cd9 [doc] Change Level1Techs link to Looking Glass forum
Originally linked to Triage thread, which has been locked and replaced
by the forum
2021-02-02 08:42:54 +11:00
Jonathan Rubenstein
b4787fcfd1 [doc] Add new Looking Glass discord to README.md 2021-02-02 08:42:54 +11:00
Tudor Brindus
e6ebcec689 [client] spice: don't send zero deltas for Wayland input
While a compositor will never send us 0-delta motion events, they can
still end up as 0-deltas post-projection, consuming QEMU buffer space
for no reason.

This should help with mouse skipping issues.
2021-02-01 11:09:46 +11:00
Quantum
ff0a859ceb [host] nvfbc: avoid recreating mouse hook and 1x1 window
The mouse hook code is very fragile, and we would like to avoid unhooking
and re-hooking as much as possible.

After this commit, this is done only once, and the hook and 1x1 window is
only destroyed upon exit. This, of course, comes with the downside of
the slight performance penalty if the guest machine is used directly while
the host is running and the client is not running.
2021-01-31 12:17:14 +11:00
Quantum
1b48ac842a [host] nvfbc: fix resource leak when pointer thread creation fails
Moving NvFBCToSysSetup to nvfbc_init means that when the pointer thread
fails to be created, NvFBCToSysRelease needs to be called.

To resolve such cleanup issues in the future, we instead call nvfbc_deinit,
which should cleanup everything that needs to be cleaned up. fails.
2021-01-31 11:21:24 +11:00
Quantum
a702c912ae [host] nvfbc: move NvFBCToSysCreate into nvfbc_init
When NvFBCToSysCapture reports recreation is required, we return
CAPTURE_RESULT_REINIT, which eventually calls nvfbc_deinit and then
nvfbc_init.

However, the NvFBC object is actually created in nvfbc_create, which
means the NvFBC object is never actually recreated. The result is an
endless cycle of NvFBC asking for recreation. This commonly manifests
as the client waiting endlessly for the host when the guest machine
reboots.

In this commit, the NvFBC object creation is moved into nvfbc_init,
and when recreation is required, it will actually be recreated.
2021-01-31 10:57:51 +11:00
Quantum
acc3298344 [host] nvfbc: cleanup threads created by nvfbc_init on failure
mouseHook_install and dwmForceComposition both create threads, but these
are only freed in nvfbc_deinit which is not called if nvfbc_init fails.
These should be freed if the pointer thread fails to be created, as
nothing else could be cleaning it up.
2021-01-31 09:57:07 +11:00
Quantum
25e74301be [client] wayland: fix copying rich text into guest
Before this, copying rich text ends up with a lot of funky behaviour,
for example:
* copying text from Discord shows up as HTML unless pasted into a text
  editor first
* copying text from Firefox shows up as the single letter h

This commit fixes all the above issues.

Due to the change in logic, we now use the first text format offered
instead of the last, which is almost certainly the preferred form.
Doing this gets us proper Unicode support, or Unicode characters would
end up as escapes of the form \uXXXX (this is used in the fallback
forms for applications without UTF-8 support).
2021-01-30 16:55:57 +11:00
Quantum
327d472d64 [all] update discord link for issue template
Use link to the new Looking Glass discord server. Also fixed a typo.
2021-01-30 15:41:44 +11:00
Geoffrey McRae
e951aaad2d [client] spice: fix errant keyboard grab/ungrab behaviour 2021-01-30 12:01:20 +11:00
Quantum
bbfe5aea37 [all] update issue template to reflect new log file path 2021-01-29 15:56:01 +11:00
Quantum
0d28ea160e [host] update README.md to reflect new log paths 2021-01-29 15:56:01 +11:00
Quantum
4fbaf18c89 [client] update host log file path 2021-01-29 15:56:01 +11:00
Quantum
c91b7f647d [host] installer: create start menu shortcut to log directory
This commit makes the installer create a shortcut to the log directory
introduced by the previous commit.
2021-01-29 15:56:01 +11:00
Quantum
1761ea2b9b [host] windows: move log path to %ProgramData%\Looking Glass (host)
Instead of using %windir%\Temp, which is not accessible by default and
contains a lot of unrelated files, as the location for our log files,
this commit moves it to %ProgramData%\Looking Glass (host), which will
be a dedicated directory just for the LG host log files. This applies
to both the host application logs and the service logs.

Also, we now switched to using PathCombineA from shlwapi.dll instead
of using snprintf, which greatly simplifies the code. PathCombineA
guarantees that the path would not overflow a buffer of MAX_PATH.
2021-01-29 15:56:01 +11:00
Quantum
fb916cbac1 [host] nvfbc: always update cursor shape on startup
This ensures that the top-left position of the cursor sprite is correctly
computed.
2021-01-28 11:18:02 +11:00
Quantum
b97130cf20 [host] nvfbc: generate cursor position update on startup
Before this commit, the NvFBC backend only generated the first cursor
position update when the mouse moves. Therefore, if the user does
not move the mouse, the cursor will be shown at (0, 0), which is not
ideal.

This commit changes this behaviour to unconditionally generate a
cursor update when the mouse hook initializes.
2021-01-28 11:18:02 +11:00
Geoffrey McRae
05f2305fa0 [client] correct error in variable name from last commit 2021-01-28 09:04:52 +11:00
Geoffrey McRae
b76fedeb67 [client] all: don't trigger cursor redraws if the cursor is not visible 2021-01-28 08:58:59 +11:00
Geoffrey McRae
6b5842d2ff [host] cmake: use -march=nehalem by default
Nehalem is the minimum requirement for the host application as it makes
use of SSE4.1 instructions, as such we should default to compling with
it instead of `-march=native` so that when the binary is distributed it
will operate on foreign systems.

Fixed #416
2021-01-28 08:09:31 +11:00
Quantum
7e15ec5e66 [common] windows: implement crash handler for stack traces
This commit uses the DbgHelp library which is shipped with Windows to
generate stack traces with function names and line number information.
It takes advantage of the pdb file generated by cv2pdb that is now
installed with looking-glass-host.exe.
2021-01-27 07:56:12 +11:00
Geoffrey McRae
1808adc2de [host] app: fix possible string overflow 2021-01-27 01:28:29 +11:00
Geoffrey McRae
e2e49bce13 [host] service: fix possible use of unitialized variable 2021-01-27 01:23:58 +11:00
Geoffrey McRae
0d7be70b56 [host] dxgi: fix maybe uninitialized warning 2021-01-27 01:21:06 +11:00
Geoffrey McRae
6b0699e664 [host] installer: include the debug PDB if it is available 2021-01-26 22:55:25 +11:00
Geoffrey McRae
9e96156912 [client] egl: use eglGetPlatformDisplay(EXT) if possible 2021-01-25 16:04:33 +11:00
Geoffrey McRae
837858c214 [client] prevent lgInit from resetting the run state
If the renderer fails to start it sets the run state to stopped, having
lgInit where it was causes this to be reset to running triggering
invalid usage of g_state.lgmp.
2021-01-25 15:25:52 +11:00
Geoffrey McRae
3783a25211 [spice] update the PureSpice submodule 2021-01-25 15:06:21 +11:00
Tudor Brindus
941c651fad [client] unconditionally quit on second SIGINT
Under some circumstances, Looking Glass can hang when SIGINT'd, for
instance, if it's stuck waiting on spice I/O that won't complete because
the guest is misbehaving.

This commit provides an escape hatch for such cases, so one doesn't have
to reach for `kill -9 $(pidof looking-glass-client)`.
2021-01-25 09:39:35 +11:00
Quantum
f9ec32b255 [host] service: disable buffering on the log file
Before this change, the log is buffered, so if the host application exits
for any reason, it usually would not show up in the log file immediately,
and the service has to be restarted for the logs to be flushed.

This commit disables the buffering so that any log entries shows up
immediately.
2021-01-25 09:35:03 +11:00
Geoffrey McRae
8caf951c41 [client] x11: don't attempt to grab the pointer on window resize 2021-01-25 09:25:01 +11:00
Geoffrey McRae
ef54e1be7f [client] x11: add error checking around XIGrabDevice 2021-01-25 06:52:23 +11:00
Geoffrey McRae
4c1893fe20 [all] fix numerous memory leaks at application shutdown 2021-01-24 21:47:53 +11:00
Geoffrey McRae
086f73721d [client] egl: remove accidental commit 2021-01-24 19:19:43 +11:00
Geoffrey McRae
202739c5be [client] egl: better debug output for EGL errors 2021-01-24 13:17:11 +11:00
Geoffrey McRae
88b15cb3fe [client] egl: nit, fix case of function name 2021-01-24 12:18:56 +11:00
Geoffrey McRae
6990d7f7e3 [client] egl: commit missed files for the last changeset 2021-01-24 12:16:39 +11:00
Geoffrey McRae
9941a4bb83 [client] egl: runtime detect support for glEGLImageTargetTexture2DOES 2021-01-24 12:06:10 +11:00
Quantum
d610aaf2cf [host] nvfbc: update cursor position on shape change
This is because we keep track of the top-left corner of the cursor, not
the location of the hotspot. When the cursor shape changes, the hotspot
location may also change. When it does, the position of the top-left
corner changes and requires an update.

In the case that we do not have the current cursor position, which
happens on startup, we do not generate this update.
2021-01-23 20:37:09 +11:00
Quantum
908aa84599 [client] wayland: use acceleration in capture mode unless rawMouse
We are forced to use accelerated movement in regular mode as that is how the
host machine cursor moves and we want the cursors to line up (since Wayland
cannot do warps). To avoid a change in sensitivity when toggling capture
mode on/off, we should use accelerated deltas for capture mode as well,
unless the user explicitly asks for raw input with input:rawMouse.
2021-01-23 20:18:20 +11:00
32 changed files with 676 additions and 265 deletions

View File

@@ -5,8 +5,8 @@ If you are looking for help or support please use one of the following methods
Create a New Topic on the Level1Tech's forum under the Looking Glass category:
* https://forum.level1techs.com/c/software/lookingGlass/142
Ask for help in #looking-glass in the VFIO discord server
* https://discord.gg/4ahCn4c
Ask for help in the Looking Glass discord server
* https://discord.gg/52SMupxkvt
*Issues that are not bug reports or feature requests will be closed & ignored*
@@ -40,12 +40,9 @@ 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:
Normally, this is found on the guest system at:
* C:\Windows\Temp\looking-glass.txt
* C:\Users\YOUR_USER\AppData\Local\Temp\looking-glass.txt
%ProgramData%\Looking Glass (host)\looking-glass-host.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
@@ -56,7 +53,7 @@ pertinent information.
PASTE HOST LOG FILE CONTENTS HERE
```
If the client is unexpetedly exiting without a backtrace, please provide one via
If the client is unexpectedly 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.

View File

@@ -51,11 +51,12 @@ https://looking-glass.io/downloads
## Web
https://forum.level1techs.com/t/looking-glass-triage/130952
https://forum.level1techs.com/c/software/lookingglass/142
## Discord
https://discord.gg/4ahCn4c
* Looking Glass: https://discord.gg/52SMupxkvt
* VFIO: https://discord.gg/4ahCn4c
## IRC

View File

@@ -391,9 +391,18 @@ static void relativePointerMotionHandler(void * data,
wl_fixed_t dxW, wl_fixed_t dyW, wl_fixed_t dxUnaccelW,
wl_fixed_t dyUnaccelW)
{
double dxUnaccel = wl_fixed_to_double(dxUnaccelW);
double dyUnaccel = wl_fixed_to_double(dyUnaccelW);
app_handleMouseGrabbed(dxUnaccel, dyUnaccel);
double dx, dy;
if (app_cursorWantsRaw())
{
dx = wl_fixed_to_double(dxUnaccelW);
dy = wl_fixed_to_double(dyUnaccelW);
}
else
{
dx = wl_fixed_to_double(dxW);
dy = wl_fixed_to_double(dyW);
}
app_handleMouseGrabbed(dx, dy);
}
static const struct zwp_relative_pointer_v1_listener relativePointerListener = {
@@ -635,11 +644,12 @@ static void dataOfferHandleOffer(void * data, struct wl_data_offer * offer,
const char * mimetype)
{
enum LG_ClipboardData type = mimetypeToCbType(mimetype);
// Oftentimes we'll get text/html alongside text/png, but would prefer to send
// image/png. In general, prefer images over text content.
// We almost never prefer text/html, as that's used to represent rich text.
// Since we can't copy or paste rich text, we should instead prefer actual
// images or plain text.
if (type != LG_CLIPBOARD_DATA_NONE &&
(wcb.stashedType == LG_CLIPBOARD_DATA_NONE ||
wcb.stashedType == LG_CLIPBOARD_DATA_TEXT))
strstr(wcb.stashedMimetype, "html")))
{
wcb.stashedType = type;
if (wcb.stashedMimetype)

View File

@@ -261,6 +261,7 @@ static bool x11GetProp(LG_DSProperty prop, void *ret)
maxSamples = samples;
}
XFree(visuals);
XCloseDisplay(dpy);
*(int*)ret = maxSamples;
@@ -336,12 +337,14 @@ static bool x11EventFilter(SDL_Event * event)
return true;
XIFocusOutEvent *xie = cookie->data;
if (xie->mode != NotifyNormal && xie->mode != XINotifyWhileGrabbed)
if (xie->mode != XINotifyNormal &&
xie->mode != XINotifyWhileGrabbed &&
xie->mode != XINotifyUngrab)
return true;
x11.focused = true;
app_updateCursorPos(xie->event_x, xie->event_y);
app_handleFocusEvent(true);
x11.focused = true;
return true;
}
@@ -351,7 +354,9 @@ static bool x11EventFilter(SDL_Event * event)
return true;
XIFocusOutEvent *xie = cookie->data;
if (xie->mode != NotifyNormal && xie->mode != XINotifyWhileGrabbed)
if (xie->mode != XINotifyNormal &&
xie->mode != XINotifyWhileGrabbed &&
xie->mode != XINotifyGrab)
return true;
app_updateCursorPos(xie->event_x, xie->event_y);
@@ -470,6 +475,9 @@ static bool x11EventFilter(SDL_Event * event)
{
XIDeviceEvent *device = cookie->data;
app_updateCursorPos(device->event_x, device->event_y);
if (!x11.pointerGrabbed)
app_handleMouseNormal(0.0, 0.0);
return true;
}
@@ -571,6 +579,25 @@ static bool x11EventFilter(SDL_Event * event)
}
}
static void x11PrintGrabError(const char * type, int dev, Status ret)
{
const char * errStr;
switch(ret)
{
case AlreadyGrabbed : errStr = "AlreadyGrabbed" ; break;
case GrabNotViewable: errStr = "GrabNotViewable"; break;
case GrabFrozen : errStr = "GrabFrozen" ; break;
case GrabInvalidTime: errStr = "GrabInvalidTime"; break;
default:
errStr = "Unknown";
break;
}
DEBUG_ERROR("XIGrabDevice failed for %s dev %d with 0x%x (%s)",
type, dev, ret, errStr);
}
static void x11GrabPointer(void)
{
if (x11.pointerGrabbed)
@@ -588,18 +615,22 @@ static void x11GrabPointer(void)
XISetMask(mask.mask, XI_RawMotion );
XISetMask(mask.mask, XI_Motion );
XIGrabDevice(
Status ret = XIGrabDevice(
x11.display,
x11.pointerDev,
x11.window,
CurrentTime,
None,
GrabModeAsync,
GrabModeAsync,
false,
XIGrabModeAsync,
XIGrabModeAsync,
XINoOwnerEvents,
&mask);
XSync(x11.display, False);
if (ret != Success)
{
x11PrintGrabError("pointer", x11.pointerDev, ret);
return;
}
x11.pointerGrabbed = true;
}
@@ -630,18 +661,22 @@ static void x11GrabKeyboard(void)
XISetMask(mask.mask, XI_RawKeyPress );
XISetMask(mask.mask, XI_RawKeyRelease);
XIGrabDevice(
Status ret = XIGrabDevice(
x11.display,
x11.keyboardDev,
x11.window,
CurrentTime,
None,
GrabModeAsync,
GrabModeAsync,
false,
XIGrabModeAsync,
XIGrabModeAsync,
XINoOwnerEvents,
&mask);
XSync(x11.display, False);
if (ret != Success)
{
x11PrintGrabError("keyboard", x11.keyboardDev, ret);
return;
}
x11.keyboardGrabbed = true;
}

View File

@@ -36,19 +36,21 @@ struct Inst
static bool lgf_sdl_create(LG_FontObj * opaque, const char * font_name, unsigned int size)
{
if (g_initCount++ == 0)
bool ret = false;
if (g_initCount == 0)
{
if (TTF_Init() < 0)
{
DEBUG_ERROR("TTF_Init Failed");
return false;
goto fail;
}
g_fontConfig = FcInitLoadConfigAndFonts();
if (!g_fontConfig)
{
DEBUG_ERROR("FcInitLoadConfigAndFonts Failed");
return false;
goto fail_init;
}
}
@@ -56,50 +58,95 @@ static bool lgf_sdl_create(LG_FontObj * opaque, const char * font_name, unsigned
if (!*opaque)
{
DEBUG_INFO("Failed to allocate %lu bytes", sizeof(struct Inst));
return false;
goto fail_config;
}
memset(*opaque, 0, sizeof(struct Inst));
memset(*opaque, 0, sizeof(struct Inst));
struct Inst * this = (struct Inst *)*opaque;
if (!font_name)
if (!font_name)
font_name = "FreeMono";
FcPattern * pat = FcNameParse((const FcChar8*)font_name);
FcConfigSubstitute (g_fontConfig, pat, FcMatchPattern);
if (!pat)
{
DEBUG_ERROR("FCNameParse failed");
goto fail_opaque;
}
FcConfigSubstitute(g_fontConfig, pat, FcMatchPattern);
FcDefaultSubstitute(pat);
FcResult result;
FcChar8 * file = NULL;
FcPattern * font = FcFontMatch(g_fontConfig, pat, &result);
FcPattern * match = FcFontMatch(g_fontConfig, pat, &result);
if (font && (FcPatternGetString(font, FC_FILE, 0, &file) == FcResultMatch))
if (!match)
{
DEBUG_ERROR("FcFontMatch Failed");
goto fail_parse;
}
if (FcPatternGetString(match, FC_FILE, 0, &file) == FcResultMatch)
{
this->font = TTF_OpenFont((char *)file, size);
if (!this->font)
{
DEBUG_ERROR("TTL_OpenFont Failed");
return false;
goto fail_match;
}
}
else
{
DEBUG_ERROR("Failed to locate the requested font: %s", font_name);
return false;
goto fail_match;
}
++g_initCount;
ret = true;
fail_match:
FcPatternDestroy(match);
fail_parse:
FcPatternDestroy(pat);
return true;
if (ret)
return true;
fail_opaque:
free(this);
*opaque = NULL;
fail_config:
if (g_initCount == 0)
{
FcConfigDestroy(g_fontConfig);
g_fontConfig = NULL;
}
fail_init:
if (g_initCount == 0)
TTF_Quit();
fail:
return false;
}
static void lgf_sdl_destroy(LG_FontObj opaque)
{
struct Inst * this = (struct Inst *)opaque;
if (this->font)
TTF_CloseFont(this->font);
free(this);
if (--g_initCount == 0)
{
FcConfigDestroy(g_fontConfig);
g_fontConfig = NULL;
TTF_Quit();
}
}
static LG_FontBitmap * lgf_sdl_render(LG_FontObj opaque, unsigned int fg_color, const char * text)

View File

@@ -33,7 +33,8 @@ make_object(
add_library(renderer_EGL STATIC
egl.c
debug.c
dynprocs.c
egldebug.c
shader.c
texture.c
model.c

View File

@@ -1,58 +0,0 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <GL/gl.h>
#include <stdarg.h>
#include <stdio.h>
void egl_debug_printf(char * format, ...)
{
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
GLenum error = glGetError();
switch(error)
{
case GL_NO_ERROR:
fprintf(stderr, " (GL_NO_ERROR)\n");
break;
case GL_INVALID_ENUM:
fprintf(stderr, " (GL_INVALID_ENUM)\n");
break;
case GL_INVALID_VALUE:
fprintf(stderr, " (GL_INVALID_VALUE)\n");
break;
case GL_INVALID_OPERATION:
fprintf(stderr, " (GL_INVALID_OPERATION)\n");
break;
case GL_INVALID_FRAMEBUFFER_OPERATION:
fprintf(stderr, " (GL_INVALID_FRAMEBUFFER_OPERATION)\n");
break;
case GL_OUT_OF_MEMORY:
fprintf(stderr, " (GL_OUT_OF_MEMORY)\n");
break;
}
}

View File

@@ -0,0 +1,32 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2021 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "dynprocs.h"
struct EGLDynProcs g_dynprocs = {0};
void egl_dynProcsInit(void)
{
g_dynprocs.eglGetPlatformDisplay = (eglGetPlatformDisplayEXT_t)
eglGetProcAddress("eglGetPlatformDisplay");
g_dynprocs.eglGetPlatformDisplayEXT = (eglGetPlatformDisplayEXT_t)
eglGetProcAddress("eglGetPlatformDisplayEXT");
g_dynprocs.glEGLImageTargetTexture2DOES = (glEGLImageTargetTexture2DOES_t)
eglGetProcAddress("glEGLImageTargetTexture2DOES");
};

View File

@@ -0,0 +1,37 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2021 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <SDL2/SDL_egl.h>
#include <GL/gl.h>
typedef EGLDisplay (*eglGetPlatformDisplayEXT_t)(EGLenum platform,
void *native_display, const EGLint *attrib_list);
typedef void (*glEGLImageTargetTexture2DOES_t)(GLenum target,
GLeglImageOES image);
struct EGLDynProcs
{
eglGetPlatformDisplayEXT_t eglGetPlatformDisplay;
eglGetPlatformDisplayEXT_t eglGetPlatformDisplayEXT;
glEGLImageTargetTexture2DOES_t glEGLImageTargetTexture2DOES;
};
extern struct EGLDynProcs g_dynprocs;
void egl_dynProcsInit(void);

View File

@@ -37,6 +37,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <assert.h>
#include "app.h"
#include "dynprocs.h"
#include "model.h"
#include "shader.h"
#include "desktop.h"
@@ -102,17 +103,6 @@ struct Inst
LG_FontObj fontObj;
};
static bool egl_vsync_option_validator(struct Option * opt, const char ** error)
{
if (opt->value.x_bool && getenv("WAYLAND_DISPLAY"))
{
DEBUG_WARN("Cannot disable vsync on Wayland, forcing egl:vsync=off");
opt->value.x_bool = false;
}
return true;
}
static struct Option egl_options[] =
{
{
@@ -121,7 +111,6 @@ static struct Option egl_options[] =
.description = "Enable vsync",
.type = OPTION_TYPE_BOOL,
.value.x_bool = false,
.validator = &egl_vsync_option_validator
},
{
.module = "egl",
@@ -241,6 +230,16 @@ void egl_deinitialize(void * opaque)
LG_LOCK_FREE(this->lock);
eglMakeCurrent(this->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (this->frameContext)
eglDestroyContext(this->display, this->frameContext);
if (this->context)
eglDestroyContext(this->display, this->context);
eglTerminate(this->display);
free(this);
}
@@ -495,43 +494,28 @@ bool egl_render_startup(void * opaque, SDL_Window * window)
return false;
}
bool useNative = false;
{
const char *client_exts = eglQueryString(NULL, EGL_EXTENSIONS);
if (strstr(client_exts, "EGL_KHR_platform_base") != NULL)
useNative = true;
}
egl_dynProcsInit();
DEBUG_INFO("use native: %s", useNative ? "true" : "false");
EGLNativeDisplayType native;
EGLenum platform;
switch(wminfo.subsystem)
{
case SDL_SYSWM_X11:
{
if (!useNative)
this->display = eglGetPlatformDisplay(EGL_PLATFORM_X11_KHR, wminfo.info.x11.display, NULL);
else
{
EGLNativeDisplayType native = (EGLNativeDisplayType)wminfo.info.x11.display;
this->display = eglGetDisplay(native);
}
native = (EGLNativeDisplayType)wminfo.info.x11.display;
platform = EGL_PLATFORM_X11_KHR;
this->nativeWind = (EGLNativeWindowType)wminfo.info.x11.window;
break;
}
#if defined(SDL_VIDEO_DRIVER_WAYLAND)
case SDL_SYSWM_WAYLAND:
{
int width, height;
SDL_GetWindowSize(window, &width, &height);
if (!useNative)
this->display = eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, wminfo.info.wl.display, NULL);
else
{
EGLNativeDisplayType native = (EGLNativeDisplayType)wminfo.info.wl.display;
this->display = eglGetDisplay(native);
}
this->nativeWind = (EGLNativeWindowType)wl_egl_window_create(wminfo.info.wl.surface, width, height);
native = (EGLNativeDisplayType)wminfo.info.wl.display;
platform = EGL_PLATFORM_WAYLAND_KHR;
this->nativeWind = (EGLNativeWindowType)wl_egl_window_create(
wminfo.info.wl.surface, width, height);
break;
}
#endif
@@ -541,6 +525,25 @@ bool egl_render_startup(void * opaque, SDL_Window * window)
return false;
}
const char *early_exts = eglQueryString(NULL, EGL_EXTENSIONS);
if (strstr(early_exts, "EGL_KHR_platform_base") != NULL &&
g_dynprocs.eglGetPlatformDisplay)
{
DEBUG_INFO("Using eglGetPlatformDisplay");
this->display = g_dynprocs.eglGetPlatformDisplay(platform, native, NULL);
}
else if (strstr(early_exts, "EGL_EXT_platform_base") != NULL &&
g_dynprocs.eglGetPlatformDisplayEXT)
{
DEBUG_INFO("Using eglGetPlatformDisplayEXT");
this->display = g_dynprocs.eglGetPlatformDisplayEXT(platform, native, NULL);
}
else
{
DEBUG_INFO("Using eglGetDisplay");
this->display = eglGetDisplay(native);
}
if (this->display == EGL_NO_DISPLAY)
{
DEBUG_ERROR("eglGetDisplay failed");
@@ -636,20 +639,27 @@ bool egl_render_startup(void * opaque, SDL_Window * window)
DEBUG_INFO("EGL APIs : %s", eglQueryString(this->display, EGL_CLIENT_APIS));
DEBUG_INFO("Extensions: %s", client_exts);
if (strstr(client_exts, "EGL_EXT_image_dma_buf_import") != NULL)
if (g_dynprocs.glEGLImageTargetTexture2DOES)
{
/*
* As of version 455.45.01 NVidia started advertising support for this
* feature, however even on the latest version 460.27.04 this is still
* broken and does not work, until this is fixed and we have way to detect
* this early just disable dma for all NVIDIA devices.
*
* ref: https://forums.developer.nvidia.com/t/egl-ext-image-dma-buf-import-broken-egl-bad-alloc-with-tons-of-free-ram/165552
*/
if (strstr(vendor, "NVIDIA") != NULL)
DEBUG_WARN("NVIDIA driver detected, ignoring broken DMA support");
else
this->dmaSupport = true;
if (strstr(client_exts, "EGL_EXT_image_dma_buf_import") != NULL)
{
/*
* As of version 455.45.01 NVidia started advertising support for this
* feature, however even on the latest version 460.27.04 this is still
* broken and does not work, until this is fixed and we have way to detect
* this early just disable dma for all NVIDIA devices.
*
* ref: https://forums.developer.nvidia.com/t/egl-ext-image-dma-buf-import-broken-egl-bad-alloc-with-tons-of-free-ram/165552
*/
if (strstr(vendor, "NVIDIA") != NULL)
DEBUG_WARN("NVIDIA driver detected, ignoring broken DMA support");
else
this->dmaSupport = true;
}
}
else
{
DEBUG_INFO("glEGLImageTargetTexture2DOES unavilable, DMA support disabled");
}
eglSwapInterval(this->display, this->opt.vsync ? 1 : 0);

View File

@@ -0,0 +1,45 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2021 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "egldebug.h"
#include <SDL2/SDL_egl.h>
#include <GL/gl.h>
const char * egl_getErrorStr(void)
{
switch (eglGetError())
{
case EGL_SUCCESS : return "EGL_SUCCESS";
case EGL_NOT_INITIALIZED : return "EGL_NOT_INITIALIZED";
case EGL_BAD_ACCESS : return "EGL_BAD_ACCESS";
case EGL_BAD_ALLOC : return "EGL_BAD_ALLOC";
case EGL_BAD_ATTRIBUTE : return "EGL_BAD_ATTRIBUTE";
case EGL_BAD_CONTEXT : return "EGL_BAD_CONTEXT";
case EGL_BAD_CONFIG : return "EGL_BAD_CONFIG";
case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE";
case EGL_BAD_DISPLAY : return "EGL_BAD_DISPLAY";
case EGL_BAD_SURFACE : return "EGL_BAD_SURFACE";
case EGL_BAD_MATCH : return "EGL_BAD_MATCH";
case EGL_BAD_PARAMETER : return "EGL_BAD_PARAMETER";
case EGL_BAD_NATIVE_PIXMAP : return "EGL_BAD_NATIVE_PIXMAP";
case EGL_BAD_NATIVE_WINDOW : return "EGL_BAD_NATIVE_WINDOW";
case EGL_CONTEXT_LOST : return "EGL_CONTEXT_LOST";
default : return "UNKNOWN";
}
}

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
Copyright (C) 2017-2021 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
This program is free software; you can redistribute it and/or modify it under
@@ -17,11 +17,13 @@ this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA
*/
#pragma once
#include <common/debug.h>
#include "common/debug.h"
#define EGL_DEBUG_PRINT(type, fmt, ...) do {egl_debug_printf(type " %20s:%-4u | %-30s | " fmt, STRIPPATH(__FILE__), __LINE__, __FUNCTION__, ##__VA_ARGS__);} while (0)
#define EGL_ERROR(fmt, ...) EGL_DEBUG_PRINT("[E]", fmt, ##__VA_ARGS__)
const char * egl_getErrorStr(void);
void egl_debug_printf(char * format, ...);
#define DEBUG_EGL_WARN(fmt, ...) \
DEBUG_WARN(fmt " (%s)", ##__VA_ARGS__, egl_getErrorStr())
#define DEBUG_EGL_ERROR(fmt, ...) \
DEBUG_ERROR(fmt " (%s)", ##__VA_ARGS__, egl_getErrorStr())

View File

@@ -154,8 +154,12 @@ void egl_splash_free(EGL_Splash ** splash)
if (!*splash)
return;
egl_model_free(&(*splash)->bg );
egl_model_free(&(*splash)->logo);
egl_shader_free(&(*splash)->bgShader );
egl_shader_free(&(*splash)->logoShader);
free(*splash);
*splash = NULL;
}
@@ -174,4 +178,4 @@ void egl_splash_render(EGL_Splash * splash, float alpha, float scaleY)
egl_model_render(splash->logo);
glDisable(GL_BLEND);
}
}

View File

@@ -20,8 +20,9 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include "texture.h"
#include "common/debug.h"
#include "common/framebuffer.h"
#include "debug.h"
#include "dynprocs.h"
#include "utils.h"
#include "egldebug.h"
#include <stdlib.h>
#include <string.h>
@@ -141,7 +142,8 @@ static bool egl_texture_map(EGL_Texture * texture, uint8_t i)
if (!texture->buf[i].map)
{
EGL_ERROR("glMapBufferRange failed for %d of %lu bytes", i, texture->pboBufferSize);
DEBUG_EGL_ERROR("glMapBufferRange failed for %d of %lu bytes", i,
texture->pboBufferSize);
return false;
}
@@ -386,13 +388,13 @@ bool egl_texture_update_from_dma(EGL_Texture * texture, const FrameBuffer * fram
if (image == EGL_NO_IMAGE)
{
DEBUG_ERROR("failed to create ELGImage for DMA transfer");
DEBUG_EGL_ERROR("Failed to create ELGImage for DMA transfer");
return false;
}
/* bind the texture and initiate the transfer */
glBindTexture(GL_TEXTURE_2D, texture->tex);
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
g_dynprocs.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
/* wait for completion */
framebuffer_wait(frame, texture->height * texture->stride);
@@ -480,7 +482,7 @@ enum EGL_TexStatus egl_texture_bind(EGL_Texture * texture)
case GL_INVALID_VALUE:
glDeleteSync(texture->buf[b].sync);
texture->buf[b].sync = 0;
EGL_ERROR("glClientWaitSync failed");
DEBUG_EGL_ERROR("glClientWaitSync failed");
return EGL_TEX_STATUS_ERROR;
}
}

View File

@@ -4,7 +4,6 @@ project(renderer_Opengl LANGUAGES C)
find_package(PkgConfig)
pkg_check_modules(RENDERER_OPENGL_PKGCONFIG REQUIRED
gl
glu
)
add_library(renderer_OpenGL STATIC

View File

@@ -27,7 +27,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <SDL2/SDL_ttf.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glx.h>
#include "common/debug.h"
@@ -48,17 +47,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#define FADE_TIME 1000000
static bool opengl_vsync_option_validator(struct Option * opt, const char ** error)
{
if (opt->value.x_bool && getenv("WAYLAND_DISPLAY"))
{
DEBUG_WARN("Cannot disable vsync on Wayland, forcing opengl:vsync=off");
opt->value.x_bool = false;
}
return true;
}
static struct Option opengl_options[] =
{
{
@@ -74,7 +62,6 @@ static struct Option opengl_options[] =
.description = "Enable vsync",
.type = OPTION_TYPE_BOOL,
.value.x_bool = false,
.validator = &opengl_vsync_option_validator
},
{
.module = "opengl",
@@ -331,7 +318,7 @@ void opengl_on_resize(void * opaque, const int width, const int height,
glViewport(0, 0, this->window.x, this->window.y);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, this->window.x, this->window.y, 0);
glOrtho(0, this->window.x, this->window.y, 0, -1, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
@@ -854,7 +841,40 @@ static bool _check_gl_error(unsigned int line, const char * name)
if (error == GL_NO_ERROR)
return false;
const GLubyte * errStr = gluErrorString(error);
const char * errStr;
switch (error)
{
case GL_INVALID_ENUM:
errStr = "GL_INVALID_ENUM";
break;
case GL_INVALID_VALUE:
errStr = "GL_INVALID_VALUE";
break;
case GL_INVALID_OPERATION:
errStr = "GL_INVALID_OPERATION";
break;
case GL_STACK_OVERFLOW:
errStr = "GL_STACK_OVERFLOW";
break;
case GL_STACK_UNDERFLOW:
errStr = "GL_STACK_UNDERFLOW";
break;
case GL_OUT_OF_MEMORY:
errStr = "GL_OUT_OF_MEMORY";
break;
case GL_TABLE_TOO_LARGE:
errStr = "GL_TABLE_TOO_LARGE";
break;
default:
errStr = "unknown error";
}
DEBUG_ERROR("%d: %s = %d (%s)", line, name, error, errStr);
return true;
}
@@ -1282,7 +1302,7 @@ static bool draw_frame(struct Inst * this)
break;
case GL_WAIT_FAILED:
DEBUG_ERROR("Wait failed %s", gluErrorString(glGetError()));
DEBUG_ERROR("Wait failed %d", glGetError());
break;
}

View File

@@ -152,16 +152,11 @@ void app_handleFocusEvent(bool focused)
if (!app_inputEnabled())
return;
if (params.grabKeyboardOnFocus)
{
if (focused)
g_state.ds->grabKeyboard();
else
g_state.ds->ungrabKeyboard();
}
if (!focused)
{
setGrabQuiet(false);
setCursorInView(false);
}
g_cursor.realign = true;
g_state.ds->realignPointer();
@@ -506,7 +501,7 @@ static int cursorThread(void * unused)
g_cursor.guest.y
);
if (params.mouseRedraw)
if (params.mouseRedraw && g_cursor.guest.visible)
lgSignalEvent(e_frame);
}
@@ -1013,6 +1008,9 @@ static void setCursorInView(bool enable)
if (warpSupport && !params.captureInputOnly)
g_state.ds->grabPointer();
if (params.grabKeyboardOnFocus)
g_state.ds->grabKeyboard();
}
else
{
@@ -1022,7 +1020,7 @@ static void setCursorInView(bool enable)
if (warpSupport)
g_state.ds->ungrabPointer();
setGrabQuiet(false);
g_state.ds->ungrabKeyboard();
}
g_cursor.warpState = WARP_STATE_ON;
@@ -1452,6 +1450,9 @@ void app_handleMouseBasic()
int x = (int) round((px - local.x) / g_cursor.dpiScale);
int y = (int) round((py - local.y) / g_cursor.dpiScale);
if (!x && !y)
return;
g_cursor.guest.x += x;
g_cursor.guest.y += y;
@@ -1562,8 +1563,7 @@ static void setGrabQuiet(bool enable)
{
if (params.grabKeyboard)
{
if (!g_state.focused || !params.grabKeyboardOnFocus ||
params.captureInputOnly)
if (!params.grabKeyboardOnFocus || !g_state.focused || params.captureInputOnly)
g_state.ds->ungrabKeyboard();
}
@@ -1609,14 +1609,23 @@ int eventFilter(void * userdata, SDL_Event * event)
return 0;
}
void int_handler(int signal)
void int_handler(int sig)
{
switch(signal)
switch(sig)
{
case SIGINT:
case SIGTERM:
DEBUG_INFO("Caught signal, shutting down...");
g_state.state = APP_STATE_SHUTDOWN;
if (g_state.state != APP_STATE_SHUTDOWN)
{
DEBUG_INFO("Caught signal, shutting down...");
g_state.state = APP_STATE_SHUTDOWN;
}
else
{
DEBUG_INFO("Caught second signal, force quitting...");
signal(sig, SIG_DFL);
raise(sig);
}
break;
}
}
@@ -2037,6 +2046,8 @@ static int lg_run(void)
return -1;
}
lgInit();
// start the renderThread so we don't just display junk
if (!lgCreateThread("renderThread", renderThread, NULL, &t_render))
{
@@ -2079,8 +2090,6 @@ static int lg_run(void)
KVMFR *udata;
int waitCount = 0;
lgInit();
restart:
while(g_state.state == APP_STATE_RUNNING)
{
@@ -2104,7 +2113,8 @@ restart:
{
DEBUG_BREAK();
DEBUG_INFO("Please check the host application is running and is the correct version");
DEBUG_INFO("Check the host log in your guest at %%TEMP%%\\looking-glass-host.txt");
DEBUG_INFO("Check the host log in your guest at: "
"%%ProgramData%%\\Looking Glass (host)\\looking-glass-host.txt");
DEBUG_INFO("Continuing to wait...");
}

View File

@@ -223,6 +223,12 @@ void option_free(void)
state.options = NULL;
state.oCount = 0;
for(int g = 0; g < state.gCount; ++g)
{
struct OptionGroup * group = &state.groups[g];
if (group->options)
free(group->options);
}
free(state.groups);
state.groups = NULL;
state.gCount = 0;

View File

@@ -20,3 +20,7 @@ target_link_libraries(lg_common_platform_code
lg_common
setupapi
)
if (ENABLE_BACKTRACE)
target_link_libraries(lg_common_platform_code dbghelp)
endif()

View File

@@ -18,9 +18,142 @@ Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "common/crash.h"
#include "common/debug.h"
#include "common/version.h"
#ifdef ENABLE_BACKTRACE
#include <stdio.h>
#include <inttypes.h>
#include <windows.h>
#include <dbghelp.h>
static const char * exception_name(DWORD code)
{
switch (code)
{
case EXCEPTION_ACCESS_VIOLATION:
return "ACCESS_VIOLATION";
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
return "ARRAY_BOUNDS_EXCEEDED";
case EXCEPTION_BREAKPOINT:
return "BREAKPOINT";
case EXCEPTION_DATATYPE_MISALIGNMENT:
return "DATATYPE_MISALIGNMENT";
case EXCEPTION_FLT_DENORMAL_OPERAND:
return "FLT_DENORMAL_OPERAND";
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
return "FLT_DIVIDE_BY_ZERO";
case EXCEPTION_FLT_INEXACT_RESULT:
return "FLT_INEXACT_RESULT";
case EXCEPTION_FLT_INVALID_OPERATION:
return "FLT_INVALID_OPERATION";
case EXCEPTION_FLT_OVERFLOW:
return "FLT_OVERFLOW";
case EXCEPTION_FLT_STACK_CHECK:
return "FLT_STACK_CHECK";
case EXCEPTION_FLT_UNDERFLOW:
return "FLT_UNDERFLOW";
case EXCEPTION_ILLEGAL_INSTRUCTION:
return "ILLEGAL_INSTRUCTION";
case EXCEPTION_IN_PAGE_ERROR:
return "IN_PAGE_ERROR";
case EXCEPTION_INT_DIVIDE_BY_ZERO:
return "INT_DIVIDE_BY_ZERO";
case EXCEPTION_INT_OVERFLOW:
return "INT_OVERFLOW";
case EXCEPTION_INVALID_DISPOSITION:
return "INVALID_DISPOSITION";
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
return "NONCONTINUABLE_EXCEPTION";
case EXCEPTION_PRIV_INSTRUCTION:
return "PRIV_INSTRUCTION";
case EXCEPTION_SINGLE_STEP:
return "SINGLE_STEP";
case EXCEPTION_STACK_OVERFLOW:
return "STACK_OVERFLOW";
default:
return "unknown";
}
}
static LONG CALLBACK exception_filter(EXCEPTION_POINTERS * exc)
{
PEXCEPTION_RECORD excInfo = exc->ExceptionRecord;
CONTEXT context;
memcpy(&context, exc->ContextRecord, sizeof context);
DEBUG_ERROR("==== FATAL CRASH (%s) ====", BUILD_VERSION);
DEBUG_ERROR("exception 0x%08lx (%s), address is %p", excInfo->ExceptionCode,
exception_name(excInfo->ExceptionCode), excInfo->ExceptionAddress);
if (!SymInitialize(GetCurrentProcess(), NULL, TRUE))
{
DEBUG_ERROR("Failed to SymInitialize: 0x%08lx, could not generate stack trace", GetLastError());
goto fail;
}
SymSetOptions(SYMOPT_LOAD_LINES);
STACKFRAME64 frame = { 0 };
frame.AddrPC.Offset = context.Rip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Rbp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Rsp;
frame.AddrStack.Mode = AddrModeFlat;
HANDLE hProcess = GetCurrentProcess();
HANDLE hThread = GetCurrentThread();
for (int i = 1; StackWalk64(IMAGE_FILE_MACHINE_AMD64, hProcess, hThread, &frame, &context, NULL,
SymFunctionTableAccess64, SymGetModuleBase64, NULL); ++i)
{
DWORD64 moduleBase = SymGetModuleBase64(hProcess, frame.AddrPC.Offset);
char moduleName[MAX_PATH];
if (moduleBase && GetModuleFileNameA((HMODULE) moduleBase, moduleName, MAX_PATH))
{
DWORD64 disp;
char symbolBuf[sizeof(SYMBOL_INFO) + 255];
PSYMBOL_INFO symbol = (PSYMBOL_INFO) symbolBuf;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
symbol->MaxNameLen = 256;
if (SymFromAddr(hProcess, frame.AddrPC.Offset, &disp, symbol))
{
IMAGEHLP_LINE line = { sizeof(IMAGEHLP_LINE), 0 };
DWORD lineDisp;
if (SymGetLineFromAddr64(hProcess, frame.AddrPC.Offset, &lineDisp, &line))
DEBUG_ERROR("[trace]: %2d: %s:%s+0x%" PRIx64 " (%s:%ld+0x%lx)", i, moduleName, symbol->Name, disp,
line.FileName, line.LineNumber, lineDisp);
else
DEBUG_ERROR("[trace]: %2d: %s:%s+0x%" PRIx64, i, moduleName, symbol->Name, disp);
}
else
DEBUG_ERROR("[trace]: %2d: %s+0x%08" PRIx64, i, moduleName, frame.AddrPC.Offset - moduleBase);
}
else
DEBUG_ERROR("[trace]: %2d: 0x%016" PRIx64, i, frame.AddrPC.Offset);
}
SymCleanup(hProcess);
fail:
fflush(stderr);
return EXCEPTION_CONTINUE_SEARCH;
}
bool installCrashHandler(const char * exe)
{
//TODO
SetUnhandledExceptionFilter(exception_filter);
return true;
}
}
#else
bool installCrashHandler(const char * exe)
{
return true;
}
#endif

View File

@@ -6,12 +6,17 @@ set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/")
include(CheckCCompilerFlag)
include(FeatureSummary)
option(OPTIMIZE_FOR_NATIVE "Build with -march=native" ON)
option(OPTIMIZE_FOR_NATIVE "Build with -march=native" OFF)
if(OPTIMIZE_FOR_NATIVE)
CHECK_C_COMPILER_FLAG("-march=native" COMPILER_SUPPORTS_MARCH_NATIVE)
if(COMPILER_SUPPORTS_MARCH_NATIVE)
add_compile_options("-march=native")
endif()
else()
CHECK_C_COMPILER_FLAG("-march=nehalem" COMPILER_SUPPORTS_MARCH_NEHALEM)
if(COMPILER_SUPPORTS_MARCH_NEHALEM)
add_compile_options("-march=nehalem")
endif()
endif()
option(ENABLE_BACKTRACE "Enable backtrace support on crash" ON)

View File

@@ -54,16 +54,19 @@ The resulting installer will be at
## Where is the log?
It is in your user's temp directory:
The log file for the host application is located at:
%TEMP%\looking-glass-host.txt
%ProgramData%\Looking Glass (host)\looking-glass-host.txt
Or if running as a system service it will be located in:
You can also find out where the file is by right clicking on the tray icon and
selecting "Log File Location".
C:\Windows\Temp\looking-glass-host.txt
The log file for the looking glass service is located at:
You can find out where the file is by right clicking on the tray icon and
selecting "Log File Location"
%ProgramData%\Looking Glass (host)\looking-glass-host-service.txt
This is useful for troubleshooting errors related to the host application not
starting.
### High priority capture using DXGI and Secure Desktop (UAC) capture support

View File

@@ -32,6 +32,7 @@ target_link_libraries(platform_Windows
userenv
wtsapi32
psapi
shlwapi
)
target_include_directories(platform_Windows

View File

@@ -743,7 +743,7 @@ static CaptureResult dxgi_capture(void)
assert(this);
assert(this->initialized);
Texture * tex;
Texture * tex = NULL;
CaptureResult result;
HRESULT status;
DXGI_OUTDUPL_FRAME_INFO frameInfo;

View File

@@ -60,12 +60,16 @@ struct iface
LGEvent * cursorEvents[2];
int mouseX, mouseY, mouseHotX, mouseHotY;
bool mouseVisible;
bool mouseVisible, hasMousePosition;
bool mouseHookCreated;
bool forceCompositionCreated;
};
static struct iface * this = NULL;
static void nvfbc_free();
static bool nvfbc_deinit(void);
static void nvfbc_free(void);
static int pointerThread(void * unused);
static void getDesktopSize(unsigned int * width, unsigned int * height, unsigned int * dpi)
@@ -84,8 +88,9 @@ static void getDesktopSize(unsigned int * width, unsigned int * height, unsigned
static void on_mouseMove(int x, int y)
{
this->mouseX = x;
this->mouseY = y;
this->hasMousePosition = true;
this->mouseX = x;
this->mouseY = y;
lgSignalEvent(this->cursorEvents[0]);
}
@@ -118,36 +123,7 @@ static bool nvfbc_create(
if (!NvFBCInit())
return false;
int bufferLen = GetEnvironmentVariable("NVFBC_PRIV_DATA", NULL, 0);
uint8_t * privData = NULL;
int privDataLen = 0;
if(bufferLen)
{
char * buffer = malloc(bufferLen);
GetEnvironmentVariable("NVFBC_PRIV_DATA", buffer, bufferLen);
privDataLen = (bufferLen - 1) / 2;
privData = (uint8_t *)malloc(privDataLen);
char hex[3] = {0};
for(int i = 0; i < privDataLen; ++i)
{
memcpy(hex, &buffer[i*2], 2);
privData[i] = (uint8_t)strtoul(hex, NULL, 16);
}
free(buffer);
}
this = (struct iface *)calloc(sizeof(struct iface), 1);
if (!NvFBCToSysCreate(privData, privDataLen, &this->nvfbc, &this->maxWidth, &this->maxHeight))
{
free(privData);
nvfbc_free();
return false;
}
free(privData);
this->frameEvent = lgCreateEvent(true, 17);
if (!this->frameEvent)
{
@@ -166,6 +142,35 @@ static bool nvfbc_create(
static bool nvfbc_init(void)
{
this->stop = false;
int bufferLen = GetEnvironmentVariable("NVFBC_PRIV_DATA", NULL, 0);
uint8_t * privData = NULL;
int privDataLen = 0;
if (bufferLen)
{
char * buffer = malloc(bufferLen);
GetEnvironmentVariable("NVFBC_PRIV_DATA", buffer, bufferLen);
privDataLen = (bufferLen - 1) / 2;
privData = (uint8_t *)malloc(privDataLen);
char hex[3] = {0};
for(int i = 0; i < privDataLen; ++i)
{
memcpy(hex, &buffer[i*2], 2);
privData[i] = (uint8_t)strtoul(hex, NULL, 16);
}
free(buffer);
}
if (!NvFBCToSysCreate(privData, privDataLen, &this->nvfbc, &this->maxWidth, &this->maxHeight))
{
free(privData);
return false;
}
free(privData);
getDesktopSize(&this->width, &this->height, &this->dpi);
lgResetEvent(this->frameEvent);
@@ -186,12 +191,21 @@ static bool nvfbc_init(void)
}
this->cursorEvents[0] = lgCreateEvent(true, 10);
mouseHook_install(on_mouseMove);
if (this->seperateCursor)
this->cursorEvents[1] = lgWrapEvent(event);
dwmForceComposition();
if (!this->mouseHookCreated)
{
mouseHook_install(on_mouseMove);
this->mouseHookCreated = true;
}
if (!this->forceCompositionCreated)
{
dwmForceComposition();
this->forceCompositionCreated = true;
}
DEBUG_INFO("Cursor mode : %s", this->seperateCursor ? "decoupled" : "integrated");
@@ -200,6 +214,7 @@ static bool nvfbc_init(void)
if (!lgCreateThread("NvFBCPointer", pointerThread, NULL, &this->pointerThread))
{
DEBUG_ERROR("Failed to create the NvFBCPointer thread");
nvfbc_deinit();
return false;
}
@@ -223,25 +238,32 @@ static void nvfbc_stop(void)
static bool nvfbc_deinit(void)
{
mouseHook_remove();
dwmUnforceComposition();
if (this->cursorEvents[0])
{
lgFreeEvent(this->cursorEvents[0]);
this->cursorEvents[0] = NULL;
}
if (this->nvfbc)
{
NvFBCToSysRelease(&this->nvfbc);
this->nvfbc = NULL;
}
return true;
}
static void nvfbc_free(void)
{
NvFBCToSysRelease(&this->nvfbc);
if (this->frameEvent)
lgFreeEvent(this->frameEvent);
if (this->mouseHookCreated)
mouseHook_remove();
if (this->forceCompositionCreated)
dwmUnforceComposition();
free(this);
this = NULL;
NvFBCFree();
@@ -350,6 +372,8 @@ static CaptureResult nvfbc_getFrame(FrameBuffer * frame)
static int pointerThread(void * unused)
{
lgSignalEvent(this->cursorEvents[1]);
while(!this->stop)
{
LGEvent * events[2];
@@ -362,6 +386,7 @@ static int pointerThread(void * unused)
CaptureResult result;
CapturePointer pointer = { 0 };
bool hotspotUpdated = false;
if (this->seperateCursor && events[1])
{
@@ -383,9 +408,10 @@ static int pointerThread(void * unused)
this->mouseVisible = pointer.visible;
this->mouseHotX = pointer.hx;
this->mouseHotY = pointer.hy;
hotspotUpdated = true;
}
if (events[0])
if (events[0] || (hotspotUpdated && this->hasMousePosition))
{
pointer.positionUpdate = true;
pointer.visible = this->mouseVisible;

View File

@@ -129,6 +129,7 @@ Section "-Install" Section1
SetOutPath $INSTDIR
File ..\..\looking-glass-host.exe
File /nonfatal ..\..\looking-glass-host.pdb
File LICENSE.txt
WriteUninstaller $INSTDIR\uninstaller.exe
@@ -171,7 +172,7 @@ Section /o "Desktop Shortcut" Section3
StrCpy $option_desktop 1
SectionEnd
Section /o "Start Menu Shortcut" Section4
Section "Start Menu Shortcut" Section4
StrCpy $option_startMenu 1
SectionEnd
@@ -179,7 +180,10 @@ Section "-Hidden Start Menu" Section5
SetShellVarContext all
${If} $option_startMenu == 1
CreateShortCut "$SMPROGRAMS\Looking Glass (host).lnk" $INSTDIR\looking-glass-host.exe
CreateDirectory "$APPDATA\Looking Glass (host)"
CreateDirectory "$SMPROGRAMS\Looking Glass (host)"
CreateShortCut "$SMPROGRAMS\Looking Glass (host)\Looking Glass (host).lnk" $INSTDIR\looking-glass-host.exe
CreateShortCut "$SMPROGRAMS\Looking Glass (host)\Looking Glass Logs.lnk" "$APPDATA\Looking Glass (host)"
${EndIf}
${If} $option_desktop == 1

View File

@@ -56,6 +56,13 @@ static bool switchDesktopAndHook(void)
}
CloseDesktop(desk);
POINT position;
GetCursorPos(&position);
mouseHook.x = position.x;
mouseHook.y = position.y;
mouseHook.callback(position.x, position.y);
mouseHook.hook = SetWindowsHookEx(WH_MOUSE_LL, mouseHook_hook, NULL, 0);
if (!mouseHook.hook)
{
@@ -84,11 +91,11 @@ static DWORD WINAPI threadProc(LPVOID lParam) {
return 0;
}
mouseHook.callback = (MouseHookFn)lParam;
if (!switchDesktopAndHook())
return 0;
mouseHook.installed = true;
mouseHook.callback = (MouseHookFn)lParam;
HWINEVENTHOOK eventHook = SetWinEventHook(
EVENT_SYSTEM_DESKTOPSWITCH, EVENT_SYSTEM_DESKTOPSWITCH, NULL,

View File

@@ -23,6 +23,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <windows.h>
#include <shellapi.h>
#include <shlwapi.h>
#include <fcntl.h>
#include "interface/platform.h"
@@ -34,6 +35,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#define ID_MENU_SHOW_LOG 3000
#define ID_MENU_EXIT 3001
#define LOG_NAME "looking-glass-host.txt"
struct AppState
{
@@ -44,6 +46,7 @@ struct AppState
char ** argv;
char executable[MAX_PATH + 1];
char systemLogDir[MAX_PATH];
HWND messageWnd;
NOTIFYICONDATA iconData;
UINT trayRestartMsg;
@@ -166,6 +169,29 @@ static BOOL WINAPI CtrlHandler(DWORD dwCtrlType)
return FALSE;
}
const char *getSystemLogDirectory(void)
{
return app.systemLogDir;
}
static void populateSystemLogDirectory()
{
char programData[MAX_PATH];
if (GetEnvironmentVariableA("ProgramData", programData, sizeof(programData)) &&
PathIsDirectoryA(programData))
{
if (!PathCombineA(app.systemLogDir, programData, "Looking Glass (host)"))
goto fail;
if (!PathIsDirectoryA(app.systemLogDir) && !CreateDirectoryA(app.systemLogDir, NULL))
goto fail;
return;
}
fail:
strcpy(app.systemLogDir, "");
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
// convert the command line to the standard argc and argv
@@ -180,6 +206,8 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
LocalFree(wargv);
GetModuleFileName(NULL, app.executable, sizeof(app.executable));
populateSystemLogDirectory();
if (HandleService(app.argc, app.argv))
return LG_HOST_EXIT_FAILED;
@@ -201,11 +229,9 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
int result = 0;
app.hInst = hInstance;
char tempPath[MAX_PATH+1];
GetTempPathA(sizeof(tempPath), tempPath);
int len = snprintf(NULL, 0, "%slooking-glass-host.txt", tempPath);
char * logFilePath = malloc(len + 1);
sprintf(logFilePath, "%slooking-glass-host.txt", tempPath);
char logFilePath[MAX_PATH];
if (!PathCombineA(logFilePath, app.systemLogDir, LOG_NAME))
strcpy(logFilePath, LOG_NAME);
struct Option options[] =
{
@@ -220,7 +246,6 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
};
option_register(options);
free(logFilePath);
// setup a handler for ctrl+c
SetConsoleCtrlHandler(CtrlHandler, TRUE);

View File

@@ -30,4 +30,5 @@ struct MSG_CALL_FUNCTION
LPARAM lParam;
};
const char *getSystemLogDirectory(void);
LRESULT sendAppMessage(UINT Msg, WPARAM wParam, LPARAM lParam);

View File

@@ -20,6 +20,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include "interface/platform.h"
#include "common/ivshmem.h"
#include "service.h"
#include "platform.h"
#include <stdio.h>
#include <stdbool.h>
@@ -29,6 +30,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <time.h>
#include <windows.h>
#include <shlwapi.h>
#include <winsvc.h>
#include <psapi.h>
#include <sddl.h>
@@ -37,6 +39,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#define SVCNAME "Looking Glass (host)"
#define SVC_ERROR ((DWORD)0xC0020001L)
#define LOG_NAME "looking-glass-host-service.txt"
/*
* Windows 10 provides this API via kernel32.dll as well as advapi32.dll and
@@ -112,12 +115,11 @@ static bool setupAPI(void)
static void setupLogging(void)
{
char tempPath[MAX_PATH+1];
GetTempPathA(sizeof(tempPath), tempPath);
int len = snprintf(NULL, 0, "%slooking-glass-host-service.txt", tempPath);
char * logFilePath = malloc(len + 1);
sprintf(logFilePath, "%slooking-glass-host-service.txt", tempPath);
char logFilePath[MAX_PATH];
if (!PathCombineA(logFilePath, getSystemLogDirectory(), LOG_NAME))
strcpy(logFilePath, LOG_NAME);
service.logFile = fopen(logFilePath, "a+");
setbuf(service.logFile, NULL);
doLog("Startup\n");
}
@@ -644,7 +646,7 @@ VOID WINAPI SvcMain(DWORD dwArgc, LPTSTR *lpszArgv)
ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0);
while(1)
{
ULONGLONG launchTime;
ULONGLONG launchTime = 0ULL;
DWORD interactiveSession = WTSGetActiveConsoleSessionId();
if (interactiveSession != 0 && interactiveSession != 0xFFFFFFFF)

View File

@@ -467,7 +467,7 @@ int app_main(int argc, char * argv[])
.magic = KVMFR_MAGIC,
.version = KVMFR_VERSION,
};
strncpy(udata.hostver, BUILD_VERSION, sizeof(udata.hostver));
strncpy(udata.hostver, BUILD_VERSION, sizeof(udata.hostver)-1);
LGMP_STATUS status;
if ((status = lgmpHostInit(shmDev.mem, shmDev.size, &app.lgmp,