mirror of
https://github.com/gnif/LookingGlass.git
synced 2025-11-17 07:28:44 +00:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
789ee70674 | ||
|
|
3c0616bab7 | ||
|
|
3ce3b573a3 | ||
|
|
ce459c24ce | ||
|
|
7d0b9711bd | ||
|
|
e477663a7e | ||
|
|
eb01efe0cb | ||
|
|
8db4b65dee | ||
|
|
501b270890 | ||
|
|
fd8f8b2b28 | ||
|
|
7083b73720 | ||
|
|
511adbba68 | ||
|
|
78b8e2a73c | ||
|
|
041b95507d | ||
|
|
bfb47a0ae4 | ||
|
|
59efa6f0ad | ||
|
|
0acce0737f | ||
|
|
e96a80dd20 | ||
|
|
c626385845 | ||
|
|
54d0dc351b | ||
|
|
1effd5fddc |
@@ -198,7 +198,7 @@ static struct Option options[] =
|
||||
.name = "minimizeOnFocusLoss",
|
||||
.description = "Minimize window on focus loss",
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = true,
|
||||
.value.x_bool = false,
|
||||
},
|
||||
{
|
||||
.module = "win",
|
||||
|
||||
@@ -1052,6 +1052,9 @@ static void lg_shutdown(void)
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
// initialize for DEBUG_* macros
|
||||
debug_init();
|
||||
|
||||
if (getuid() == 0)
|
||||
{
|
||||
DEBUG_ERROR("Do not run looking glass as root!");
|
||||
|
||||
@@ -30,6 +30,20 @@
|
||||
#include <inttypes.h>
|
||||
#include "time.h"
|
||||
|
||||
enum DebugLevel
|
||||
{
|
||||
DEBUG_LEVEL_NONE,
|
||||
DEBUG_LEVEL_INFO,
|
||||
DEBUG_LEVEL_WARN,
|
||||
DEBUG_LEVEL_ERROR,
|
||||
DEBUG_LEVEL_FIXME,
|
||||
DEBUG_LEVEL_FATAL
|
||||
};
|
||||
|
||||
extern const char ** debug_lookup;
|
||||
|
||||
void debug_init(void);
|
||||
|
||||
#ifdef ENABLE_BACKTRACE
|
||||
void printBacktrace(void);
|
||||
#define DEBUG_PRINT_BACKTRACE() printBacktrace()
|
||||
@@ -65,19 +79,20 @@ void printBacktrace(void);
|
||||
sizeof(s) > 20 && (s)[sizeof(s)-21] == DIRECTORY_SEPARATOR ? (s) + sizeof(s) - 20 : \
|
||||
sizeof(s) > 21 && (s)[sizeof(s)-22] == DIRECTORY_SEPARATOR ? (s) + sizeof(s) - 21 : (s))
|
||||
|
||||
#define DEBUG_PRINT(type, fmt, ...) do { \
|
||||
fprintf(stderr, "%12" PRId64 " " type " %20s:%-4u | %-30s | " fmt "\n", \
|
||||
microtime(), STRIPPATH(__FILE__), __LINE__, __FUNCTION__, ##__VA_ARGS__);\
|
||||
#define DEBUG_PRINT(level, fmt, ...) do { \
|
||||
fprintf(stderr, "%s%12" PRId64 "%20s:%-4u | %-30s | " fmt "%s\n", \
|
||||
debug_lookup[level], microtime(), STRIPPATH(__FILE__), \
|
||||
__LINE__, __FUNCTION__, ##__VA_ARGS__, debug_lookup[DEBUG_LEVEL_NONE]); \
|
||||
} while (0)
|
||||
|
||||
#define DEBUG_BREAK() DEBUG_PRINT("[ ]", "================================================================================")
|
||||
#define DEBUG_INFO(fmt, ...) DEBUG_PRINT("[I]", fmt, ##__VA_ARGS__)
|
||||
#define DEBUG_WARN(fmt, ...) DEBUG_PRINT("[W]", fmt, ##__VA_ARGS__)
|
||||
#define DEBUG_ERROR(fmt, ...) DEBUG_PRINT("[E]", fmt, ##__VA_ARGS__)
|
||||
#define DEBUG_FIXME(fmt, ...) DEBUG_PRINT("[F]", fmt, ##__VA_ARGS__)
|
||||
#define DEBUG_BREAK() DEBUG_PRINT(DEBUG_LEVEL_INFO, "================================================================================")
|
||||
#define DEBUG_INFO(fmt, ...) DEBUG_PRINT(DEBUG_LEVEL_INFO, fmt, ##__VA_ARGS__)
|
||||
#define DEBUG_WARN(fmt, ...) DEBUG_PRINT(DEBUG_LEVEL_WARN, fmt, ##__VA_ARGS__)
|
||||
#define DEBUG_ERROR(fmt, ...) DEBUG_PRINT(DEBUG_LEVEL_ERROR, fmt, ##__VA_ARGS__)
|
||||
#define DEBUG_FIXME(fmt, ...) DEBUG_PRINT(DEBUG_LEVEL_FIXME, fmt, ##__VA_ARGS__)
|
||||
#define DEBUG_FATAL(fmt, ...) do { \
|
||||
DEBUG_BREAK(); \
|
||||
DEBUG_PRINT("[!]", fmt, ##__VA_ARGS__); \
|
||||
DEBUG_PRINT(DEBUG_LEVEL_FATAL, fmt, ##__VA_ARGS__); \
|
||||
DEBUG_PRINT_BACKTRACE(); \
|
||||
abort(); \
|
||||
} while(0)
|
||||
|
||||
@@ -31,7 +31,6 @@ typedef struct LGEvent LGEvent;
|
||||
LGEvent * lgCreateEvent(bool autoReset, unsigned int msSpinTime);
|
||||
void lgFreeEvent (LGEvent * handle);
|
||||
bool lgWaitEvent (LGEvent * handle, unsigned int timeout);
|
||||
bool lgWaitEvents (LGEvent * handles[], int count, bool waitAll, unsigned int timeout);
|
||||
bool lgSignalEvent(LGEvent * handle);
|
||||
bool lgResetEvent (LGEvent * handle);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ include_directories(
|
||||
)
|
||||
|
||||
add_library(lg_common_platform_code STATIC
|
||||
debug.c
|
||||
crash.c
|
||||
sysinfo.c
|
||||
thread.c
|
||||
|
||||
56
common/src/platform/linux/debug.c
Normal file
56
common/src/platform/linux/debug.c
Normal file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright (C) 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "common/debug.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#define COLOR_RESET "\033[0m"
|
||||
#define COLOR_YELLOW "\033[0;33m"
|
||||
#define COLOR_RED "\033[0;31m"
|
||||
#define COLOR_CYAN "\033[0;36m"
|
||||
#define COLOR_WHITE "\033[0;37m"
|
||||
|
||||
const char ** debug_lookup = NULL;
|
||||
|
||||
void debug_init(void)
|
||||
{
|
||||
static const char * colorLookup[] =
|
||||
{
|
||||
COLOR_RESET , // DEBUG_LEVEL_NONE
|
||||
COLOR_RESET "[I] ", // DEBUG_LEVEL_INFO
|
||||
COLOR_YELLOW "[W] ", // DEBUG_LEVEL_WARN
|
||||
COLOR_RED "[E] ", // DEBUG_LEVEL_ERROR
|
||||
COLOR_CYAN "[F] ", // DEBUG_LEVEL_FIXME
|
||||
COLOR_WHITE "[!] " // DEBUG_LEVEL_FATAL
|
||||
};
|
||||
|
||||
static const char * plainLookup[] =
|
||||
{
|
||||
"" , // DEBUG_LEVEL_NONE
|
||||
"[I] ", // DEBUG_LEVEL_INFO
|
||||
"[W] ", // DEBUG_LEVEL_WARN
|
||||
"[E] ", // DEBUG_LEVEL_ERROR
|
||||
"[F] ", // DEBUG_LEVEL_FIXME
|
||||
"[!] " // DEBUG_LEVEL_FATAL
|
||||
};
|
||||
|
||||
debug_lookup = (isatty(STDERR_FILENO) == 1) ? colorLookup : plainLookup;
|
||||
}
|
||||
@@ -6,6 +6,7 @@ include_directories(
|
||||
)
|
||||
|
||||
add_library(lg_common_platform_code STATIC
|
||||
debug.c
|
||||
crash.c
|
||||
dpi.c
|
||||
sysinfo.c
|
||||
|
||||
38
common/src/platform/windows/debug.c
Normal file
38
common/src/platform/windows/debug.c
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright (C) 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "common/debug.h"
|
||||
|
||||
const char ** debug_lookup = NULL;
|
||||
|
||||
void debug_init(void)
|
||||
{
|
||||
static const char * plainLookup[] =
|
||||
{
|
||||
"" , // DEBUG_LEVEL_NONE
|
||||
"[I] ", // DEBUG_LEVEL_INFO
|
||||
"[W] ", // DEBUG_LEVEL_WARN
|
||||
"[E] ", // DEBUG_LEVEL_ERROR
|
||||
"[F] ", // DEBUG_LEVEL_FIXME
|
||||
"[!] " // DEBUG_LEVEL_FATAL
|
||||
};
|
||||
|
||||
debug_lookup = plainLookup;
|
||||
}
|
||||
@@ -23,126 +23,38 @@
|
||||
#include "common/time.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
struct LGEvent
|
||||
{
|
||||
volatile int lock;
|
||||
bool reset;
|
||||
HANDLE handle;
|
||||
bool wrapped;
|
||||
unsigned int msSpinTime;
|
||||
volatile bool signaled;
|
||||
};
|
||||
#include <stdatomic.h>
|
||||
|
||||
LGEvent * lgCreateEvent(bool autoReset, unsigned int msSpinTime)
|
||||
{
|
||||
LGEvent * event = (LGEvent *)malloc(sizeof(LGEvent));
|
||||
if (!event)
|
||||
{
|
||||
DEBUG_ERROR("out of ram");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
event->lock = 0;
|
||||
event->reset = autoReset;
|
||||
event->handle = CreateEvent(NULL, autoReset ? FALSE : TRUE, FALSE, NULL);
|
||||
event->wrapped = false;
|
||||
event->msSpinTime = msSpinTime;
|
||||
event->signaled = false;
|
||||
|
||||
if (!event->handle)
|
||||
HANDLE handle = CreateEvent(NULL, autoReset ? FALSE : TRUE, FALSE, NULL);
|
||||
if (!handle)
|
||||
{
|
||||
DEBUG_WINERROR("Failed to create the event", GetLastError());
|
||||
free(event);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return event;
|
||||
return (LGEvent *)handle;
|
||||
}
|
||||
|
||||
LGEvent * lgWrapEvent(void * handle)
|
||||
{
|
||||
LGEvent * event = (LGEvent *)malloc(sizeof(LGEvent));
|
||||
if (!event)
|
||||
{
|
||||
DEBUG_ERROR("out of ram");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
event->lock = 0;
|
||||
event->reset = false;
|
||||
event->handle = (HANDLE)handle;
|
||||
event->wrapped = true;
|
||||
event->msSpinTime = 0;
|
||||
event->signaled = false;
|
||||
return event;
|
||||
return (LGEvent *)handle;
|
||||
}
|
||||
|
||||
void lgFreeEvent(LGEvent * event)
|
||||
{
|
||||
CloseHandle(event->handle);
|
||||
CloseHandle((HANDLE)event);
|
||||
}
|
||||
|
||||
bool lgWaitEvent(LGEvent * event, unsigned int timeout)
|
||||
{
|
||||
// wrapped events can't be enahnced
|
||||
if (!event->wrapped)
|
||||
{
|
||||
if (event->signaled)
|
||||
{
|
||||
if (event->reset)
|
||||
event->signaled = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (timeout == 0)
|
||||
{
|
||||
bool ret = event->signaled;
|
||||
if (event->reset)
|
||||
event->signaled = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (event->msSpinTime)
|
||||
{
|
||||
unsigned int spinTime = event->msSpinTime;
|
||||
if (timeout != TIMEOUT_INFINITE)
|
||||
{
|
||||
if (timeout > event->msSpinTime)
|
||||
timeout -= event->msSpinTime;
|
||||
else
|
||||
{
|
||||
spinTime -= timeout;
|
||||
timeout = 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t now = microtime();
|
||||
uint64_t end = now + spinTime * 1000;
|
||||
while(!event->signaled)
|
||||
{
|
||||
now = microtime();
|
||||
if (now >= end)
|
||||
break;
|
||||
}
|
||||
|
||||
if (event->signaled)
|
||||
{
|
||||
if (event->reset)
|
||||
event->signaled = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const DWORD to = (timeout == TIMEOUT_INFINITE) ? INFINITE : (DWORD)timeout;
|
||||
while(true)
|
||||
do
|
||||
{
|
||||
switch(WaitForSingleObject(event->handle, to))
|
||||
switch(WaitForSingleObject((HANDLE)event, to))
|
||||
{
|
||||
case WAIT_OBJECT_0:
|
||||
if (!event->reset)
|
||||
event->signaled = true;
|
||||
return true;
|
||||
|
||||
case WAIT_ABANDONED:
|
||||
@@ -151,7 +63,6 @@ bool lgWaitEvent(LGEvent * event, unsigned int timeout)
|
||||
case WAIT_TIMEOUT:
|
||||
if (timeout == TIMEOUT_INFINITE)
|
||||
continue;
|
||||
|
||||
return false;
|
||||
|
||||
case WAIT_FAILED:
|
||||
@@ -160,63 +71,18 @@ bool lgWaitEvent(LGEvent * event, unsigned int timeout)
|
||||
}
|
||||
|
||||
DEBUG_ERROR("Unknown wait event return code");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
while(0);
|
||||
|
||||
bool lgWaitEvents(LGEvent * events[], int count, bool waitAll, unsigned int timeout)
|
||||
{
|
||||
const DWORD to = (timeout == TIMEOUT_INFINITE) ? INFINITE : (DWORD)timeout;
|
||||
|
||||
HANDLE * handles = (HANDLE *)malloc(sizeof(HANDLE) * count);
|
||||
for(int i = 0; i < count; ++i)
|
||||
handles[i] = events[i]->handle;
|
||||
|
||||
while(true)
|
||||
{
|
||||
DWORD result = WaitForMultipleObjects(count, handles, waitAll, to);
|
||||
if (result >= WAIT_OBJECT_0 && result < count)
|
||||
{
|
||||
// null non signaled events from the handle list
|
||||
for(int i = 0; i < count; ++i)
|
||||
if (i != result && !lgWaitEvent(events[i], 0))
|
||||
events[i] = NULL;
|
||||
free(handles);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (result >= WAIT_ABANDONED_0 && result - WAIT_ABANDONED_0 < count)
|
||||
continue;
|
||||
|
||||
switch(result)
|
||||
{
|
||||
case WAIT_TIMEOUT:
|
||||
if (timeout == TIMEOUT_INFINITE)
|
||||
continue;
|
||||
|
||||
free(handles);
|
||||
return false;
|
||||
|
||||
case WAIT_FAILED:
|
||||
DEBUG_WINERROR("Wait for event failed", GetLastError());
|
||||
free(handles);
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_ERROR("Unknown wait event return code");
|
||||
free(handles);
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool lgSignalEvent(LGEvent * event)
|
||||
{
|
||||
event->signaled = true;
|
||||
return SetEvent(event->handle);
|
||||
return SetEvent((HANDLE)event);
|
||||
}
|
||||
|
||||
bool lgResetEvent(LGEvent * event)
|
||||
{
|
||||
event->signaled = false;
|
||||
return ResetEvent(event->handle);
|
||||
return ResetEvent((HANDLE)event);
|
||||
}
|
||||
|
||||
36
doc/faq.rst
36
doc/faq.rst
@@ -31,13 +31,6 @@ There are several reasons why this can happen, the most common are your
|
||||
capture resolution, or refresh rate. The windows capture methods currently
|
||||
struggle to capture high resolutions under certain circumstances.
|
||||
|
||||
Another cause can be how the game or application you are running is
|
||||
configured. Because of the way windows integrate with the WDM (Windows
|
||||
Desktop Manager), running applications in "Full Screen" mode may—in some
|
||||
cases—cause a large performance penalty. Try switching to windowed
|
||||
full-screen mode, the difference in performance can be like night and
|
||||
day.
|
||||
|
||||
Some titles do some strange things at early initialization that cause
|
||||
capture performance issues. One such title is the Unigine Valley
|
||||
benchmark where the capture rate is limited to 1/2 the actual rate. For
|
||||
@@ -137,6 +130,16 @@ NvFBC (NVIDIA Capture API) doesn't work
|
||||
NvFBC is only supported on professional-grade GPUs, and will not function on
|
||||
consumer-grade cards like those from the GeForce series.
|
||||
|
||||
If you have a supported card, you can enable NVFBC by adding the following
|
||||
to the host ini file, found at
|
||||
``%ProgramFiles%\Looking Glass (host)\looking-glass-host.ini``
|
||||
(create one if it doesn't exist):
|
||||
|
||||
.. code:: INI
|
||||
|
||||
[app]
|
||||
capture=nvfbc
|
||||
|
||||
.. _the_screen_stops_updating_when_left_idle_for_a_time:
|
||||
|
||||
The screen stops updating when left idle for a time
|
||||
@@ -157,8 +160,9 @@ The log file for the host application is located at::
|
||||
|
||||
%ProgramData%\Looking Glass (host)\looking-glass-host.txt
|
||||
|
||||
You can also find out where the file is by right clicking on the tray
|
||||
icon and selecting "Log File Location".
|
||||
You can also open the log file by right clicking on the Looking Glass
|
||||
system tray icon, then clicking *Open Log File*. This opens the log
|
||||
file in Notepad.
|
||||
|
||||
The log file for the looking glass service is located at::
|
||||
|
||||
@@ -204,8 +208,9 @@ This is intentional for several reasons.
|
||||
position as NVIDIA decided to not provide this as part of the cursor
|
||||
updates.
|
||||
2. NvFBC requires administrator level access to enable the interface in
|
||||
the first place. (WIP)
|
||||
3. DXGI performance can be improved if we have this. (WIP)
|
||||
the first place.
|
||||
3. General capture performance is boosted by taking advantage of high priority
|
||||
scheduling with SYSTEM level privileges.
|
||||
|
||||
NvFBC (NVIDIA Frame Buffer Capture)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -216,8 +221,6 @@ Why can't I compile NvFBC support into the host?
|
||||
You must download and install the NVidia Capture SDK. Please note that
|
||||
by doing so you will be agreeing to NVIDIA's SDK License agreement.
|
||||
|
||||
*-Geoff*
|
||||
|
||||
.. _a_note_about_ivshmem_and_scream_audio:
|
||||
|
||||
Why doesn't Looking Glass work with Scream over IVSHMEM?
|
||||
@@ -235,9 +238,10 @@ a highly optimized memory copy anyway so there is no need to make another one.
|
||||
|
||||
If you insist on using IVSHMEM for Scream—despite its inferiority to the
|
||||
default network implementation—the Windows Host Application can be told
|
||||
what device to use. Create a ``looking-glass-host.ini`` file in the same
|
||||
directory as the looking-glass-host.exe file. In it, you can use the
|
||||
``os:shmDevice`` option like so:
|
||||
what device to use. Edit the file
|
||||
``%ProgramFiles%\Looking Glass (host)\looking-glass-host.ini``,
|
||||
(create one if it doesn't exist)
|
||||
then, you can add the ``os:shmDevice`` option like so:
|
||||
|
||||
.. code:: INI
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ More information:
|
||||
* `Forum <https://forum.level1techs.com/c/software/lookingglass/142>`_
|
||||
* `Community wiki <https://looking-glass.io/wiki/>`_
|
||||
* IRC - #LookingGlass on `libera.chat <https://libera.chat/>`_
|
||||
* `Trello <https://trello.com/b/tI1Xbwsg/looking-glass>`_
|
||||
|
||||
Donate:
|
||||
|
||||
@@ -40,3 +39,4 @@ Donate:
|
||||
* `Patreon <https://www.patreon.com/gnif>`_
|
||||
* `PayPal <https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=ESQ72XUPGKXRY>`_
|
||||
* BTC - 14ZFcYjsKPiVreHqcaekvHGL846u3ZuT13
|
||||
* ETH - 0x6f8aEe454384122bF9ed28f025FBCe2Bce98db85
|
||||
|
||||
@@ -76,7 +76,7 @@ To install the module into DKMS, run
|
||||
|
||||
.. code:: bash
|
||||
|
||||
dkms install .
|
||||
dkms install "."
|
||||
|
||||
.. _module_dkms_loading:
|
||||
|
||||
|
||||
@@ -91,9 +91,10 @@ typedef void (*CapturePostPointerBuffer)(CapturePointer pointer);
|
||||
|
||||
typedef struct CaptureInterface
|
||||
{
|
||||
const char *shortName;
|
||||
const char * (*getName )();
|
||||
void (*initOptions )();
|
||||
const char * shortName;
|
||||
const bool asyncCapture;
|
||||
const char * (*getName )();
|
||||
void (*initOptions )();
|
||||
|
||||
bool(*create)(
|
||||
CaptureGetPointerBuffer getPointerBufferFn,
|
||||
|
||||
@@ -236,6 +236,7 @@ static CaptureResult xcb_getFrame(FrameBuffer * frame, const unsigned int height
|
||||
struct CaptureInterface Capture_XCB =
|
||||
{
|
||||
.shortName = "XCB",
|
||||
.asyncCapture = true,
|
||||
.getName = xcb_getName,
|
||||
.create = xcb_create,
|
||||
.init = xcb_init,
|
||||
|
||||
@@ -41,6 +41,9 @@ struct app app = { 0 };
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
// initialize for DEBUG_* macros
|
||||
debug_init();
|
||||
|
||||
app.executable = argv[0];
|
||||
|
||||
struct passwd * pw = getpwuid(getuid());
|
||||
|
||||
@@ -37,18 +37,6 @@
|
||||
|
||||
#include "dxgi_extra.h"
|
||||
|
||||
typedef enum _D3DKMT_SCHEDULINGPRIORITYCLASS
|
||||
{
|
||||
D3DKMT_SCHEDULINGPRIORITYCLASS_IDLE,
|
||||
D3DKMT_SCHEDULINGPRIORITYCLASS_BELOW_NORMAL,
|
||||
D3DKMT_SCHEDULINGPRIORITYCLASS_NORMAL,
|
||||
D3DKMT_SCHEDULINGPRIORITYCLASS_ABOVE_NORMAL,
|
||||
D3DKMT_SCHEDULINGPRIORITYCLASS_HIGH,
|
||||
D3DKMT_SCHEDULINGPRIORITYCLASS_REALTIME
|
||||
}
|
||||
D3DKMT_SCHEDULINGPRIORITYCLASS;
|
||||
typedef NTSTATUS WINAPI (*PD3DKMTSetProcessSchedulingPriorityClass)(HANDLE, D3DKMT_SCHEDULINGPRIORITYCLASS);
|
||||
|
||||
#define LOCKED(x) INTERLOCKED_SECTION(this->deviceContextLock, x)
|
||||
|
||||
enum TextureState
|
||||
@@ -154,7 +142,7 @@ static void dxgi_initOptions(void)
|
||||
{
|
||||
.module = "dxgi",
|
||||
.name = "useAcquireLock",
|
||||
.description = "Enable locking around `AcquireFrame` (EXPERIMENTAL, leave enabled if you're not sure!)",
|
||||
.description = "Enable locking around `AcquireNextFrame` (EXPERIMENTAL, leave enabled if you're not sure!)",
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = true
|
||||
},
|
||||
@@ -242,7 +230,7 @@ static bool dxgi_init(void)
|
||||
const char * optAdapter = option_get_string("dxgi", "adapter");
|
||||
const char * optOutput = option_get_string("dxgi", "output" );
|
||||
|
||||
for(int i = 0; IDXGIFactory1_EnumAdapters1(this->factory, i, &this->adapter) != DXGI_ERROR_NOT_FOUND; ++i)
|
||||
for (int i = 0; IDXGIFactory1_EnumAdapters1(this->factory, i, &this->adapter) != DXGI_ERROR_NOT_FOUND; ++i)
|
||||
{
|
||||
if (optAdapter)
|
||||
{
|
||||
@@ -271,7 +259,7 @@ static bool dxgi_init(void)
|
||||
DEBUG_INFO("Adapter matched, trying: %ls", adapterDesc.Description);
|
||||
}
|
||||
|
||||
for(int n = 0; IDXGIAdapter1_EnumOutputs(this->adapter, n, &this->output) != DXGI_ERROR_NOT_FOUND; ++n)
|
||||
for (int n = 0; IDXGIAdapter1_EnumOutputs(this->adapter, n, &this->output) != DXGI_ERROR_NOT_FOUND; ++n)
|
||||
{
|
||||
IDXGIOutput_GetDesc(this->output, &outputDesc);
|
||||
if (optOutput)
|
||||
@@ -314,6 +302,16 @@ static bool dxgi_init(void)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
DXGI_ADAPTER_DESC1 adapterDesc;
|
||||
IDXGIAdapter1_GetDesc1(this->adapter, &adapterDesc);
|
||||
DEBUG_INFO("Device Name : %ls" , outputDesc.DeviceName);
|
||||
DEBUG_INFO("Device Descripion: %ls" , adapterDesc.Description);
|
||||
DEBUG_INFO("Device Vendor ID : 0x%x" , adapterDesc.VendorId);
|
||||
DEBUG_INFO("Device Device ID : 0x%x" , adapterDesc.DeviceId);
|
||||
DEBUG_INFO("Device Video Mem : %u MiB" , (unsigned)(adapterDesc.DedicatedVideoMemory / 1048576));
|
||||
DEBUG_INFO("Device Sys Mem : %u MiB" , (unsigned)(adapterDesc.DedicatedSystemMemory / 1048576));
|
||||
DEBUG_INFO("Shared Sys Mem : %u MiB" , (unsigned)(adapterDesc.SharedSystemMemory / 1048576));
|
||||
|
||||
static const D3D_FEATURE_LEVEL win8[] =
|
||||
{
|
||||
D3D_FEATURE_LEVEL_11_1,
|
||||
@@ -380,9 +378,6 @@ static bool dxgi_init(void)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
DXGI_ADAPTER_DESC1 adapterDesc;
|
||||
IDXGIAdapter1_GetDesc1(this->adapter, &adapterDesc);
|
||||
|
||||
switch(outputDesc.Rotation)
|
||||
{
|
||||
case DXGI_MODE_ROTATION_ROTATE90:
|
||||
@@ -423,49 +418,10 @@ static bool dxgi_init(void)
|
||||
this->dpi = monitor_dpi(outputDesc.Monitor);
|
||||
++this->formatVer;
|
||||
|
||||
DEBUG_INFO("Device Descripion: %ls" , adapterDesc.Description);
|
||||
DEBUG_INFO("Device Vendor ID : 0x%x" , adapterDesc.VendorId);
|
||||
DEBUG_INFO("Device Device ID : 0x%x" , adapterDesc.DeviceId);
|
||||
DEBUG_INFO("Device Video Mem : %u MiB" , (unsigned)(adapterDesc.DedicatedVideoMemory / 1048576));
|
||||
DEBUG_INFO("Device Sys Mem : %u MiB" , (unsigned)(adapterDesc.DedicatedSystemMemory / 1048576));
|
||||
DEBUG_INFO("Shared Sys Mem : %u MiB" , (unsigned)(adapterDesc.SharedSystemMemory / 1048576));
|
||||
DEBUG_INFO("Feature Level : 0x%x" , this->featureLevel);
|
||||
DEBUG_INFO("Capture Size : %u x %u", this->width, this->height);
|
||||
DEBUG_INFO("AcquireLock : %s" , this->useAcquireLock ? "enabled" : "disabled");
|
||||
|
||||
// bump up our priority
|
||||
{
|
||||
HMODULE gdi32 = GetModuleHandleA("GDI32");
|
||||
if (gdi32)
|
||||
{
|
||||
PD3DKMTSetProcessSchedulingPriorityClass fn =
|
||||
(PD3DKMTSetProcessSchedulingPriorityClass)GetProcAddress(gdi32, "D3DKMTSetProcessSchedulingPriorityClass");
|
||||
|
||||
if (fn)
|
||||
{
|
||||
status = fn(GetCurrentProcess(), D3DKMT_SCHEDULINGPRIORITYCLASS_REALTIME);
|
||||
if (FAILED(status))
|
||||
{
|
||||
DEBUG_WARN("Failed to set realtime GPU priority.");
|
||||
DEBUG_INFO("This is not a failure, please do not report this as an issue.");
|
||||
DEBUG_INFO("To fix this, install and run the Looking Glass host as a service.");
|
||||
DEBUG_INFO("looking-glass-host.exe InstallService");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IDXGIDevice * dxgi;
|
||||
status = ID3D11Device_QueryInterface(this->device, &IID_IDXGIDevice, (void **)&dxgi);
|
||||
if (FAILED(status))
|
||||
{
|
||||
DEBUG_WINERROR("failed to query DXGI interface from device", status);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
IDXGIDevice_SetGPUThreadPriority(dxgi, 7);
|
||||
IDXGIDevice_Release(dxgi);
|
||||
}
|
||||
|
||||
// try to reduce the latency
|
||||
{
|
||||
IDXGIDevice1 * dxgi;
|
||||
@@ -587,7 +543,7 @@ static bool dxgi_init(void)
|
||||
texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
texDesc.MiscFlags = 0;
|
||||
|
||||
for(int i = 0; i < this->maxTextures; ++i)
|
||||
for (int i = 0; i < this->maxTextures; ++i)
|
||||
{
|
||||
status = ID3D11Device_CreateTexture2D(this->device, &texDesc, NULL, &this->texture[i].tex);
|
||||
if (FAILED(status))
|
||||
@@ -628,7 +584,7 @@ static bool dxgi_deinit(void)
|
||||
{
|
||||
assert(this);
|
||||
|
||||
for(int i = 0; i < this->maxTextures; ++i)
|
||||
for (int i = 0; i < this->maxTextures; ++i)
|
||||
{
|
||||
this->texture[i].state = TEXTURE_STATE_UNUSED;
|
||||
|
||||
@@ -809,7 +765,7 @@ static CaptureResult dxgi_capture(void)
|
||||
uint32_t bufferSize;
|
||||
if (frameInfo.PointerShapeBufferSize > 0)
|
||||
{
|
||||
if(!this->getPointerBufferFn(&pointerShape, &bufferSize))
|
||||
if (!this->getPointerBufferFn(&pointerShape, &bufferSize))
|
||||
DEBUG_WARN("Failed to obtain a buffer for the pointer shape");
|
||||
else
|
||||
copyPointer = true;
|
||||
@@ -926,13 +882,13 @@ static CaptureResult dxgi_waitFrame(CaptureFrame * frame, const size_t maxFrameS
|
||||
assert(this->initialized);
|
||||
|
||||
// NOTE: the event may be signaled when there are no frames available
|
||||
if(atomic_load_explicit(&this->texReady, memory_order_acquire) == 0)
|
||||
if (atomic_load_explicit(&this->texReady, memory_order_acquire) == 0)
|
||||
{
|
||||
if (!lgWaitEvent(this->frameEvent, 1000))
|
||||
return CAPTURE_RESULT_TIMEOUT;
|
||||
|
||||
// the count will still be zero if we are stopping
|
||||
if(atomic_load_explicit(&this->texReady, memory_order_acquire) == 0)
|
||||
if (atomic_load_explicit(&this->texReady, memory_order_acquire) == 0)
|
||||
return CAPTURE_RESULT_TIMEOUT;
|
||||
}
|
||||
|
||||
@@ -1041,6 +997,7 @@ static CaptureResult dxgi_releaseFrame(void)
|
||||
struct CaptureInterface Capture_DXGI =
|
||||
{
|
||||
.shortName = "DXGI",
|
||||
.asyncCapture = true,
|
||||
.getName = dxgi_getName,
|
||||
.initOptions = dxgi_initOptions,
|
||||
.create = dxgi_create,
|
||||
|
||||
@@ -57,8 +57,7 @@ struct iface
|
||||
|
||||
NvFBCFrameGrabInfo grabInfo;
|
||||
|
||||
LGEvent * frameEvent;
|
||||
LGEvent * cursorEvents[2];
|
||||
LGEvent * cursorEvent;
|
||||
|
||||
int mouseX, mouseY, mouseHotX, mouseHotY;
|
||||
bool mouseVisible, hasMousePosition;
|
||||
@@ -92,8 +91,16 @@ static void on_mouseMove(int x, int y)
|
||||
this->hasMousePosition = true;
|
||||
this->mouseX = x;
|
||||
this->mouseY = y;
|
||||
if (this->cursorEvents[0])
|
||||
lgSignalEvent(this->cursorEvents[0]);
|
||||
|
||||
const CapturePointer pointer =
|
||||
{
|
||||
.positionUpdate = true,
|
||||
.visible = this->mouseVisible,
|
||||
.x = x - this->mouseHotX,
|
||||
.y = y - this->mouseHotY
|
||||
};
|
||||
|
||||
this->postPointerBufferFn(pointer);
|
||||
}
|
||||
|
||||
static const char * nvfbc_getName(void)
|
||||
@@ -126,13 +133,6 @@ static bool nvfbc_create(
|
||||
return false;
|
||||
|
||||
this = (struct iface *)calloc(sizeof(struct iface), 1);
|
||||
this->frameEvent = lgCreateEvent(true, 17);
|
||||
if (!this->frameEvent)
|
||||
{
|
||||
DEBUG_ERROR("failed to create the frame event");
|
||||
nvfbc_free();
|
||||
return false;
|
||||
}
|
||||
|
||||
this->seperateCursor = option_get_bool("nvfbc", "decoupleCursor");
|
||||
this->getPointerBufferFn = getPointerBufferFn;
|
||||
@@ -157,7 +157,7 @@ static bool nvfbc_init(void)
|
||||
privDataLen = (bufferLen - 1) / 2;
|
||||
privData = (uint8_t *)malloc(privDataLen);
|
||||
char hex[3] = {0};
|
||||
for(int i = 0; i < privDataLen; ++i)
|
||||
for (int i = 0; i < privDataLen; ++i)
|
||||
{
|
||||
memcpy(hex, &buffer[i*2], 2);
|
||||
privData[i] = (uint8_t)strtoul(hex, NULL, 16);
|
||||
@@ -178,7 +178,6 @@ static bool nvfbc_init(void)
|
||||
free(privData);
|
||||
|
||||
getDesktopSize(&this->width, &this->height, &this->dpi);
|
||||
lgResetEvent(this->frameEvent);
|
||||
|
||||
HANDLE event;
|
||||
if (!NvFBCToSysSetup(
|
||||
@@ -196,10 +195,8 @@ static bool nvfbc_init(void)
|
||||
return false;
|
||||
}
|
||||
|
||||
this->cursorEvents[0] = lgCreateEvent(true, 10);
|
||||
|
||||
if (this->seperateCursor)
|
||||
this->cursorEvents[1] = lgWrapEvent(event);
|
||||
this->cursorEvent = lgWrapEvent(event);
|
||||
|
||||
if (!this->mouseHookCreated)
|
||||
{
|
||||
@@ -232,8 +229,7 @@ static void nvfbc_stop(void)
|
||||
{
|
||||
this->stop = true;
|
||||
|
||||
lgSignalEvent(this->cursorEvents[0]);
|
||||
lgSignalEvent(this->frameEvent);
|
||||
lgSignalEvent(this->cursorEvent);
|
||||
|
||||
if (this->pointerThread)
|
||||
{
|
||||
@@ -244,12 +240,7 @@ static void nvfbc_stop(void)
|
||||
|
||||
static bool nvfbc_deinit(void)
|
||||
{
|
||||
if (this->cursorEvents[0])
|
||||
{
|
||||
lgFreeEvent(this->cursorEvents[0]);
|
||||
this->cursorEvents[0] = NULL;
|
||||
}
|
||||
|
||||
this->cursorEvent = NULL;
|
||||
if (this->nvfbc)
|
||||
{
|
||||
NvFBCToSysRelease(&this->nvfbc);
|
||||
@@ -261,9 +252,6 @@ static bool nvfbc_deinit(void)
|
||||
|
||||
static void nvfbc_free(void)
|
||||
{
|
||||
if (this->frameEvent)
|
||||
lgFreeEvent(this->frameEvent);
|
||||
|
||||
if (this->mouseHookCreated)
|
||||
mouseHook_remove();
|
||||
|
||||
@@ -299,28 +287,25 @@ static CaptureResult nvfbc_capture(void)
|
||||
bool changed = false;
|
||||
const unsigned int h = (this->height + 127) / 128;
|
||||
const unsigned int w = (this->width + 127) / 128;
|
||||
for(unsigned int y = 0; y < h; ++y)
|
||||
for(unsigned int x = 0; x < w; ++x)
|
||||
for (unsigned int y = 0; y < h; ++y)
|
||||
for (unsigned int x = 0; x < w; ++x)
|
||||
if (this->diffMap[(y*w)+x])
|
||||
{
|
||||
changed = true;
|
||||
break;
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
if (!changed)
|
||||
return CAPTURE_RESULT_TIMEOUT;
|
||||
|
||||
memcpy(&this->grabInfo, &grabInfo, sizeof(grabInfo));
|
||||
lgSignalEvent(this->frameEvent);
|
||||
return CAPTURE_RESULT_OK;
|
||||
}
|
||||
|
||||
static CaptureResult nvfbc_waitFrame(CaptureFrame * frame,
|
||||
const size_t maxFrameSize)
|
||||
{
|
||||
if (!lgWaitEvent(this->frameEvent, 1000))
|
||||
return CAPTURE_RESULT_TIMEOUT;
|
||||
|
||||
if (this->stop)
|
||||
return CAPTURE_RESULT_REINIT;
|
||||
|
||||
@@ -378,13 +363,9 @@ static CaptureResult nvfbc_getFrame(FrameBuffer * frame,
|
||||
|
||||
static int pointerThread(void * unused)
|
||||
{
|
||||
lgSignalEvent(this->cursorEvents[1]);
|
||||
|
||||
while(!this->stop)
|
||||
{
|
||||
LGEvent * events[2];
|
||||
memcpy(&events, &this->cursorEvents, sizeof(LGEvent *) * 2);
|
||||
if (!lgWaitEvents(events, this->seperateCursor ? 2 : 1, false, 1000))
|
||||
if (!lgWaitEvent(this->cursorEvent, 1000))
|
||||
continue;
|
||||
|
||||
if (this->stop)
|
||||
@@ -392,39 +373,31 @@ static int pointerThread(void * unused)
|
||||
|
||||
CaptureResult result;
|
||||
CapturePointer pointer = { 0 };
|
||||
bool hotspotUpdated = false;
|
||||
|
||||
if (this->seperateCursor && events[1])
|
||||
void * data;
|
||||
uint32_t size;
|
||||
if (!this->getPointerBufferFn(&data, &size))
|
||||
{
|
||||
void * data;
|
||||
uint32_t size;
|
||||
if (!this->getPointerBufferFn(&data, &size))
|
||||
{
|
||||
DEBUG_WARN("failed to get a pointer buffer");
|
||||
continue;
|
||||
}
|
||||
|
||||
result = NvFBCToSysGetCursor(this->nvfbc, &pointer, data, size);
|
||||
if (result != CAPTURE_RESULT_OK)
|
||||
{
|
||||
DEBUG_WARN("NvFBCToSysGetCursor failed");
|
||||
continue;
|
||||
}
|
||||
|
||||
this->mouseVisible = pointer.visible;
|
||||
this->mouseHotX = pointer.hx;
|
||||
this->mouseHotY = pointer.hy;
|
||||
hotspotUpdated = true;
|
||||
DEBUG_WARN("failed to get a pointer buffer");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (events[0] || (hotspotUpdated && this->hasMousePosition))
|
||||
result = NvFBCToSysGetCursor(this->nvfbc, &pointer, data, size);
|
||||
if (result != CAPTURE_RESULT_OK)
|
||||
{
|
||||
pointer.positionUpdate = true;
|
||||
pointer.visible = this->mouseVisible;
|
||||
pointer.x = this->mouseX - this->mouseHotX;
|
||||
pointer.y = this->mouseY - this->mouseHotY;
|
||||
DEBUG_WARN("NvFBCToSysGetCursor failed");
|
||||
continue;
|
||||
}
|
||||
|
||||
this->mouseVisible = pointer.visible;
|
||||
this->mouseHotX = pointer.hx;
|
||||
this->mouseHotY = pointer.hy;
|
||||
|
||||
pointer.positionUpdate = true;
|
||||
pointer.visible = this->mouseVisible;
|
||||
pointer.x = this->mouseX - pointer.hx;
|
||||
pointer.y = this->mouseY - pointer.hy;
|
||||
|
||||
this->postPointerBufferFn(pointer);
|
||||
}
|
||||
|
||||
@@ -434,6 +407,7 @@ static int pointerThread(void * unused)
|
||||
struct CaptureInterface Capture_NVFBC =
|
||||
{
|
||||
.shortName = "NvFBC",
|
||||
.asyncCapture = false,
|
||||
.getName = nvfbc_getName,
|
||||
.initOptions = nvfbc_initOptions,
|
||||
|
||||
|
||||
@@ -253,6 +253,11 @@ CaptureResult NvFBCToSysCapture(
|
||||
handle->retry = 0;
|
||||
break;
|
||||
|
||||
case NVFBC_ERROR_PROTECTED_CONTENT:
|
||||
DEBUG_WARN("Protected content is playing, can't capture");
|
||||
Sleep(100);
|
||||
return CAPTURE_RESULT_TIMEOUT;
|
||||
|
||||
case NVFBC_ERROR_INVALID_PARAM:
|
||||
if (handle->retry < 2)
|
||||
{
|
||||
|
||||
@@ -314,6 +314,9 @@ fail:
|
||||
|
||||
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
|
||||
{
|
||||
// initialize for DEBUG_* macros
|
||||
debug_init();
|
||||
|
||||
// convert the command line to the standard argc and argv
|
||||
LPWSTR * wargv = CommandLineToArgvW(GetCommandLineW(), &app.argc);
|
||||
app.argv = malloc(sizeof(char *) * app.argc);
|
||||
@@ -464,6 +467,41 @@ finish:
|
||||
return result;
|
||||
}
|
||||
|
||||
void boostPriority(void)
|
||||
{
|
||||
typedef enum _D3DKMT_SCHEDULINGPRIORITYCLASS
|
||||
{
|
||||
D3DKMT_SCHEDULINGPRIORITYCLASS_IDLE,
|
||||
D3DKMT_SCHEDULINGPRIORITYCLASS_BELOW_NORMAL,
|
||||
D3DKMT_SCHEDULINGPRIORITYCLASS_NORMAL,
|
||||
D3DKMT_SCHEDULINGPRIORITYCLASS_ABOVE_NORMAL,
|
||||
D3DKMT_SCHEDULINGPRIORITYCLASS_HIGH,
|
||||
D3DKMT_SCHEDULINGPRIORITYCLASS_REALTIME
|
||||
}
|
||||
D3DKMT_SCHEDULINGPRIORITYCLASS;
|
||||
typedef NTSTATUS WINAPI (*PD3DKMTSetProcessSchedulingPriorityClass)
|
||||
(HANDLE, D3DKMT_SCHEDULINGPRIORITYCLASS);
|
||||
|
||||
HMODULE gdi32 = GetModuleHandleA("GDI32");
|
||||
if (!gdi32)
|
||||
return;
|
||||
|
||||
PD3DKMTSetProcessSchedulingPriorityClass fn =
|
||||
(PD3DKMTSetProcessSchedulingPriorityClass)
|
||||
GetProcAddress(gdi32, "D3DKMTSetProcessSchedulingPriorityClass");
|
||||
|
||||
if (!fn)
|
||||
return;
|
||||
|
||||
if (FAILED(fn(GetCurrentProcess(), D3DKMT_SCHEDULINGPRIORITYCLASS_REALTIME)))
|
||||
{
|
||||
DEBUG_WARN("Failed to set realtime GPU priority.");
|
||||
DEBUG_INFO("This is not a failure, please do not report this as an issue.");
|
||||
DEBUG_INFO("To fix this, install and run the Looking Glass host as a service.");
|
||||
DEBUG_INFO("looking-glass-host.exe InstallService");
|
||||
}
|
||||
}
|
||||
|
||||
bool app_init(void)
|
||||
{
|
||||
const char * logFile = option_get_string("os", "logFile" );
|
||||
@@ -487,6 +525,9 @@ bool app_init(void)
|
||||
// get the performance frequency for spinlocks
|
||||
QueryPerformanceFrequency(&app.perfFreq);
|
||||
|
||||
// try to boost the scheduler priority
|
||||
boostPriority();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
331
host/src/app.c
331
host/src/app.c
@@ -79,17 +79,21 @@ struct app
|
||||
PLGMPHost lgmp;
|
||||
|
||||
PLGMPHostQueue pointerQueue;
|
||||
PLGMPMemory pointerMemory[POINTER_SHAPE_BUFFERS];
|
||||
PLGMPMemory pointerMemory[LGMP_Q_POINTER_LEN];
|
||||
PLGMPMemory pointerShapeMemory[POINTER_SHAPE_BUFFERS];
|
||||
LG_Lock pointerLock;
|
||||
CapturePointer pointerInfo;
|
||||
PLGMPMemory pointerShape;
|
||||
bool pointerShapeValid;
|
||||
unsigned int pointerIndex;
|
||||
unsigned int pointerShapeIndex;
|
||||
|
||||
long pageSize;
|
||||
size_t maxFrameSize;
|
||||
PLGMPHostQueue frameQueue;
|
||||
PLGMPMemory frameMemory[LGMP_Q_FRAME_LEN];
|
||||
unsigned int frameIndex;
|
||||
bool frameValid;
|
||||
|
||||
CaptureInterface * iface;
|
||||
|
||||
@@ -138,118 +142,127 @@ static bool lgmpTimer(void * opaque)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sendFrame(void)
|
||||
{
|
||||
CaptureFrame frame = { 0 };
|
||||
bool repeatFrame = false;
|
||||
|
||||
//wait until there is room in the queue
|
||||
while(app.state == APP_STATE_RUNNING &&
|
||||
lgmpHostQueuePending(app.frameQueue) == LGMP_Q_FRAME_LEN)
|
||||
{
|
||||
usleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (app.state != APP_STATE_RUNNING)
|
||||
return false;
|
||||
|
||||
switch(app.iface->waitFrame(&frame, app.maxFrameSize))
|
||||
{
|
||||
case CAPTURE_RESULT_OK:
|
||||
break;
|
||||
|
||||
case CAPTURE_RESULT_REINIT:
|
||||
{
|
||||
app.state = APP_STATE_RESTART;
|
||||
DEBUG_INFO("Frame thread reinit");
|
||||
return false;
|
||||
}
|
||||
|
||||
case CAPTURE_RESULT_ERROR:
|
||||
{
|
||||
DEBUG_ERROR("Failed to get the frame");
|
||||
return false;
|
||||
}
|
||||
|
||||
case CAPTURE_RESULT_TIMEOUT:
|
||||
{
|
||||
if (app.frameValid && lgmpHostQueueNewSubs(app.frameQueue) > 0)
|
||||
{
|
||||
// resend the last frame
|
||||
repeatFrame = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
LGMP_STATUS status;
|
||||
|
||||
// if we are repeating a frame just send the last frame again
|
||||
if (repeatFrame)
|
||||
{
|
||||
if ((status = lgmpHostQueuePost(app.frameQueue, 0, app.frameMemory[app.frameIndex])) != LGMP_OK)
|
||||
DEBUG_ERROR("%s", lgmpStatusString(status));
|
||||
return true;
|
||||
}
|
||||
|
||||
// we increment the index first so that if we need to repeat a frame
|
||||
// the index still points to the latest valid frame
|
||||
if (++app.frameIndex == LGMP_Q_FRAME_LEN)
|
||||
app.frameIndex = 0;
|
||||
|
||||
KVMFRFrame * fi = lgmpHostMemPtr(app.frameMemory[app.frameIndex]);
|
||||
switch(frame.format)
|
||||
{
|
||||
case CAPTURE_FMT_BGRA : fi->type = FRAME_TYPE_BGRA ; break;
|
||||
case CAPTURE_FMT_RGBA : fi->type = FRAME_TYPE_RGBA ; break;
|
||||
case CAPTURE_FMT_RGBA10 : fi->type = FRAME_TYPE_RGBA10 ; break;
|
||||
case CAPTURE_FMT_RGBA16F: fi->type = FRAME_TYPE_RGBA16F; break;
|
||||
default:
|
||||
DEBUG_ERROR("Unsupported frame format %d, skipping frame", frame.format);
|
||||
return true;
|
||||
}
|
||||
|
||||
switch(frame.rotation)
|
||||
{
|
||||
case CAPTURE_ROT_0 : fi->rotation = FRAME_ROT_0 ; break;
|
||||
case CAPTURE_ROT_90 : fi->rotation = FRAME_ROT_90 ; break;
|
||||
case CAPTURE_ROT_180: fi->rotation = FRAME_ROT_180; break;
|
||||
case CAPTURE_ROT_270: fi->rotation = FRAME_ROT_270; break;
|
||||
default:
|
||||
DEBUG_WARN("Unsupported frame rotation %d", frame.rotation);
|
||||
fi->rotation = FRAME_ROT_0;
|
||||
break;
|
||||
}
|
||||
|
||||
fi->formatVer = frame.formatVer;
|
||||
fi->width = frame.width;
|
||||
fi->height = frame.height;
|
||||
fi->realHeight = frame.realHeight;
|
||||
fi->stride = frame.stride;
|
||||
fi->pitch = frame.pitch;
|
||||
fi->offset = app.pageSize - FrameBufferStructSize;
|
||||
fi->mouseScalePercent = app.iface->getMouseScale();
|
||||
fi->blockScreensaver = os_blockScreensaver();
|
||||
app.frameValid = true;
|
||||
|
||||
// put the framebuffer on the border of the next page
|
||||
// this is to allow for aligned DMA transfers by the receiver
|
||||
FrameBuffer * fb = (FrameBuffer *)(((uint8_t*)fi) + fi->offset);
|
||||
framebuffer_prepare(fb);
|
||||
|
||||
/* we post and then get the frame, this is intentional! */
|
||||
if ((status = lgmpHostQueuePost(app.frameQueue, 0, app.frameMemory[app.frameIndex])) != LGMP_OK)
|
||||
{
|
||||
DEBUG_ERROR("%s", lgmpStatusString(status));
|
||||
return true;
|
||||
}
|
||||
|
||||
app.iface->getFrame(fb, frame.height);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int frameThread(void * opaque)
|
||||
{
|
||||
DEBUG_INFO("Frame thread started");
|
||||
|
||||
bool frameValid = false;
|
||||
bool repeatFrame = false;
|
||||
CaptureFrame frame = { 0 };
|
||||
const long pageSize = sysinfo_getPageSize();
|
||||
|
||||
while(app.state == APP_STATE_RUNNING)
|
||||
{
|
||||
//wait until there is room in the queue
|
||||
if(lgmpHostQueuePending(app.frameQueue) == LGMP_Q_FRAME_LEN)
|
||||
{
|
||||
usleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch(app.iface->waitFrame(&frame, app.maxFrameSize))
|
||||
{
|
||||
case CAPTURE_RESULT_OK:
|
||||
repeatFrame = false;
|
||||
break;
|
||||
|
||||
case CAPTURE_RESULT_REINIT:
|
||||
{
|
||||
app.state = APP_STATE_RESTART;
|
||||
DEBUG_INFO("Frame thread reinit");
|
||||
return 0;
|
||||
}
|
||||
|
||||
case CAPTURE_RESULT_ERROR:
|
||||
{
|
||||
DEBUG_ERROR("Failed to get the frame");
|
||||
return 0;
|
||||
}
|
||||
|
||||
case CAPTURE_RESULT_TIMEOUT:
|
||||
{
|
||||
if (frameValid && lgmpHostQueueNewSubs(app.frameQueue) > 0)
|
||||
{
|
||||
// resend the last frame
|
||||
repeatFrame = true;
|
||||
break;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
LGMP_STATUS status;
|
||||
|
||||
// if we are repeating a frame just send the last frame again
|
||||
if (repeatFrame)
|
||||
{
|
||||
if ((status = lgmpHostQueuePost(app.frameQueue, 0, app.frameMemory[app.frameIndex])) != LGMP_OK)
|
||||
DEBUG_ERROR("%s", lgmpStatusString(status));
|
||||
continue;
|
||||
}
|
||||
|
||||
// we increment the index first so that if we need to repeat a frame
|
||||
// the index still points to the latest valid frame
|
||||
if (++app.frameIndex == LGMP_Q_FRAME_LEN)
|
||||
app.frameIndex = 0;
|
||||
|
||||
KVMFRFrame * fi = lgmpHostMemPtr(app.frameMemory[app.frameIndex]);
|
||||
switch(frame.format)
|
||||
{
|
||||
case CAPTURE_FMT_BGRA : fi->type = FRAME_TYPE_BGRA ; break;
|
||||
case CAPTURE_FMT_RGBA : fi->type = FRAME_TYPE_RGBA ; break;
|
||||
case CAPTURE_FMT_RGBA10 : fi->type = FRAME_TYPE_RGBA10 ; break;
|
||||
case CAPTURE_FMT_RGBA16F: fi->type = FRAME_TYPE_RGBA16F; break;
|
||||
default:
|
||||
DEBUG_ERROR("Unsupported frame format %d, skipping frame", frame.format);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch(frame.rotation)
|
||||
{
|
||||
case CAPTURE_ROT_0 : fi->rotation = FRAME_ROT_0 ; break;
|
||||
case CAPTURE_ROT_90 : fi->rotation = FRAME_ROT_90 ; break;
|
||||
case CAPTURE_ROT_180: fi->rotation = FRAME_ROT_180; break;
|
||||
case CAPTURE_ROT_270: fi->rotation = FRAME_ROT_270; break;
|
||||
default:
|
||||
DEBUG_WARN("Unsupported frame rotation %d", frame.rotation);
|
||||
fi->rotation = FRAME_ROT_0;
|
||||
break;
|
||||
}
|
||||
|
||||
fi->formatVer = frame.formatVer;
|
||||
fi->width = frame.width;
|
||||
fi->height = frame.height;
|
||||
fi->realHeight = frame.realHeight;
|
||||
fi->stride = frame.stride;
|
||||
fi->pitch = frame.pitch;
|
||||
fi->offset = pageSize - FrameBufferStructSize;
|
||||
fi->mouseScalePercent = app.iface->getMouseScale();
|
||||
fi->blockScreensaver = os_blockScreensaver();
|
||||
frameValid = true;
|
||||
|
||||
// put the framebuffer on the border of the next page
|
||||
// this is to allow for aligned DMA transfers by the receiver
|
||||
FrameBuffer * fb = (FrameBuffer *)(((uint8_t*)fi) + fi->offset);
|
||||
framebuffer_prepare(fb);
|
||||
|
||||
/* we post and then get the frame, this is intentional! */
|
||||
if ((status = lgmpHostQueuePost(app.frameQueue, 0, app.frameMemory[app.frameIndex])) != LGMP_OK)
|
||||
{
|
||||
DEBUG_ERROR("%s", lgmpStatusString(status));
|
||||
continue;
|
||||
}
|
||||
app.iface->getFrame(fb, frame.height);
|
||||
if (!sendFrame())
|
||||
break;
|
||||
}
|
||||
DEBUG_INFO("Frame thread stopped");
|
||||
return 0;
|
||||
@@ -258,6 +271,9 @@ static int frameThread(void * opaque)
|
||||
bool startThreads(void)
|
||||
{
|
||||
app.state = APP_STATE_RUNNING;
|
||||
if (!app.iface->asyncCapture)
|
||||
return true;
|
||||
|
||||
if (!lgCreateThread("FrameThread", frameThread, NULL, &app.frameThread))
|
||||
{
|
||||
DEBUG_ERROR("Failed to create the frame thread");
|
||||
@@ -269,20 +285,25 @@ bool startThreads(void)
|
||||
|
||||
bool stopThreads(void)
|
||||
{
|
||||
bool ok = true;
|
||||
|
||||
app.iface->stop();
|
||||
if (app.state != APP_STATE_SHUTDOWN)
|
||||
app.state = APP_STATE_IDLE;
|
||||
|
||||
if (app.frameThread && !lgJoinThread(app.frameThread, NULL))
|
||||
{
|
||||
DEBUG_WARN("Failed to join the frame thread");
|
||||
ok = false;
|
||||
}
|
||||
app.frameThread = NULL;
|
||||
if (!app.iface->asyncCapture)
|
||||
return true;
|
||||
|
||||
return ok;
|
||||
if (app.frameThread)
|
||||
{
|
||||
if (!lgJoinThread(app.frameThread, NULL))
|
||||
{
|
||||
DEBUG_WARN("Failed to join the frame thread");
|
||||
app.frameThread = NULL;
|
||||
return false;
|
||||
}
|
||||
app.frameThread = NULL;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool captureStart(void)
|
||||
@@ -315,8 +336,8 @@ static bool captureStop(void)
|
||||
|
||||
bool captureGetPointerBuffer(void ** data, uint32_t * size)
|
||||
{
|
||||
PLGMPMemory mem = app.pointerMemory[app.pointerIndex];
|
||||
*data = ((uint8_t*)lgmpHostMemPtr(mem)) + sizeof(KVMFRCursor);
|
||||
PLGMPMemory mem = app.pointerShapeMemory[app.pointerShapeIndex];
|
||||
*data = (uint8_t*)lgmpHostMemPtr(mem) + sizeof(KVMFRCursor);
|
||||
*size = MAX_POINTER_SIZE - sizeof(KVMFRCursor);
|
||||
return true;
|
||||
}
|
||||
@@ -342,8 +363,18 @@ static void sendPointer(bool newClient)
|
||||
// new clients need the last known shape and current position
|
||||
if (newClient)
|
||||
{
|
||||
PLGMPMemory mem;
|
||||
if (app.pointerShapeValid)
|
||||
mem = app.pointerShape;
|
||||
else
|
||||
{
|
||||
mem = app.pointerMemory[app.pointerIndex];
|
||||
if (++app.pointerIndex == LGMP_Q_POINTER_LEN)
|
||||
app.pointerIndex = 0;
|
||||
}
|
||||
|
||||
// update the saved details with the current cursor position
|
||||
KVMFRCursor *cursor = lgmpHostMemPtr(app.pointerShape);
|
||||
KVMFRCursor *cursor = lgmpHostMemPtr(mem);
|
||||
cursor->x = app.pointerInfo.x;
|
||||
cursor->y = app.pointerInfo.y;
|
||||
|
||||
@@ -351,12 +382,24 @@ static void sendPointer(bool newClient)
|
||||
(app.pointerShapeValid ? CURSOR_FLAG_SHAPE : 0) |
|
||||
(app.pointerInfo.visible ? CURSOR_FLAG_VISIBLE : 0);
|
||||
|
||||
postPointer(flags, app.pointerShape);
|
||||
postPointer(flags, mem);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t flags = 0;
|
||||
PLGMPMemory mem = app.pointerMemory[app.pointerIndex];
|
||||
PLGMPMemory mem;
|
||||
if (app.pointerInfo.shapeUpdate)
|
||||
{
|
||||
mem = app.pointerShapeMemory[app.pointerShapeIndex];
|
||||
if (++app.pointerShapeIndex == POINTER_SHAPE_BUFFERS)
|
||||
app.pointerShapeIndex = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
mem = app.pointerMemory[app.pointerIndex];
|
||||
if (++app.pointerIndex == LGMP_Q_POINTER_LEN)
|
||||
app.pointerIndex = 0;
|
||||
}
|
||||
KVMFRCursor *cursor = lgmpHostMemPtr(mem);
|
||||
|
||||
if (app.pointerInfo.positionUpdate || newClient)
|
||||
@@ -390,16 +433,9 @@ static void sendPointer(bool newClient)
|
||||
app.pointerShapeValid = true;
|
||||
flags |= CURSOR_FLAG_SHAPE;
|
||||
|
||||
// retain this memory for new clients
|
||||
app.pointerMemory[app.pointerIndex] = app.pointerShape;
|
||||
app.pointerShape = mem;
|
||||
}
|
||||
|
||||
// only advance if the pointer shape was not swapped out of the list
|
||||
if ((flags & CURSOR_FLAG_SHAPE) == 0)
|
||||
if (++app.pointerIndex == POINTER_SHAPE_BUFFERS)
|
||||
app.pointerIndex = 0;
|
||||
|
||||
postPointer(flags, mem);
|
||||
}
|
||||
|
||||
@@ -525,34 +561,41 @@ int app_main(int argc, char * argv[])
|
||||
goto fail_lgmp;
|
||||
}
|
||||
|
||||
for(int i = 0; i < POINTER_SHAPE_BUFFERS; ++i)
|
||||
for(int i = 0; i < LGMP_Q_POINTER_LEN; ++i)
|
||||
{
|
||||
if ((status = lgmpHostMemAlloc(app.lgmp, MAX_POINTER_SIZE, &app.pointerMemory[i])) != LGMP_OK)
|
||||
if ((status = lgmpHostMemAlloc(app.lgmp, sizeof(KVMFRCursor), &app.pointerMemory[i])) != LGMP_OK)
|
||||
{
|
||||
DEBUG_ERROR("lgmpHostMemAlloc Failed (Pointer): %s", lgmpStatusString(status));
|
||||
exitcode = LG_HOST_EXIT_FATAL;
|
||||
goto fail_lgmp;
|
||||
}
|
||||
memset(lgmpHostMemPtr(app.pointerMemory[i]), 0, MAX_POINTER_SIZE);
|
||||
memset(lgmpHostMemPtr(app.pointerMemory[i]), 0, sizeof(KVMFRCursor));
|
||||
}
|
||||
|
||||
app.pointerShapeValid = false;
|
||||
if ((status = lgmpHostMemAlloc(app.lgmp, MAX_POINTER_SIZE, &app.pointerShape)) != LGMP_OK)
|
||||
for(int i = 0; i < POINTER_SHAPE_BUFFERS; ++i)
|
||||
{
|
||||
DEBUG_ERROR("lgmpHostMemAlloc Failed (Pointer Shape): %s", lgmpStatusString(status));
|
||||
exitcode = LG_HOST_EXIT_FATAL;
|
||||
goto fail_lgmp;
|
||||
if ((status = lgmpHostMemAlloc(app.lgmp, MAX_POINTER_SIZE, &app.pointerShapeMemory[i])) != LGMP_OK)
|
||||
{
|
||||
DEBUG_ERROR("lgmpHostMemAlloc Failed (Pointer Shapes): %s", lgmpStatusString(status));
|
||||
exitcode = LG_HOST_EXIT_FATAL;
|
||||
goto fail_lgmp;
|
||||
}
|
||||
memset(lgmpHostMemPtr(app.pointerShapeMemory[i]), 0, MAX_POINTER_SIZE);
|
||||
}
|
||||
|
||||
const long sz = sysinfo_getPageSize();
|
||||
app.pageSize = sysinfo_getPageSize();
|
||||
app.frameValid = false;
|
||||
app.pointerShapeValid = false;
|
||||
|
||||
app.maxFrameSize = lgmpHostMemAvail(app.lgmp);
|
||||
app.maxFrameSize = (app.maxFrameSize - (sz - 1)) & ~(sz - 1);
|
||||
app.maxFrameSize = (app.maxFrameSize - (app.pageSize - 1)) & ~(app.pageSize - 1);
|
||||
app.maxFrameSize /= LGMP_Q_FRAME_LEN;
|
||||
DEBUG_INFO("Max Frame Size : %u MiB", (unsigned int)(app.maxFrameSize / 1048576LL));
|
||||
|
||||
for(int i = 0; i < LGMP_Q_FRAME_LEN; ++i)
|
||||
{
|
||||
if ((status = lgmpHostMemAllocAligned(app.lgmp, app.maxFrameSize, sz, &app.frameMemory[i])) != LGMP_OK)
|
||||
if ((status = lgmpHostMemAllocAligned(app.lgmp, app.maxFrameSize,
|
||||
app.pageSize, &app.frameMemory[i])) != LGMP_OK)
|
||||
{
|
||||
DEBUG_ERROR("lgmpHostMemAlloc Failed (Frame): %s", lgmpStatusString(status));
|
||||
exitcode = LG_HOST_EXIT_FATAL;
|
||||
@@ -594,6 +637,8 @@ int app_main(int argc, char * argv[])
|
||||
}
|
||||
|
||||
DEBUG_INFO("Using : %s", iface->getName());
|
||||
DEBUG_INFO("Capture Method : %s", iface->asyncCapture ?
|
||||
"Asynchronous" : "Synchronous");
|
||||
|
||||
app.state = APP_STATE_RUNNING;
|
||||
app.iface = iface;
|
||||
@@ -688,6 +733,9 @@ int app_main(int argc, char * argv[])
|
||||
exitcode = LG_HOST_EXIT_FAILED;
|
||||
goto fail_capture;
|
||||
}
|
||||
|
||||
if (!iface->asyncCapture)
|
||||
sendFrame();
|
||||
}
|
||||
|
||||
if (app.state != APP_STATE_SHUTDOWN)
|
||||
@@ -728,9 +776,10 @@ fail_timer:
|
||||
fail_lgmp:
|
||||
for(int i = 0; i < LGMP_Q_FRAME_LEN; ++i)
|
||||
lgmpHostMemFree(&app.frameMemory[i]);
|
||||
for(int i = 0; i < POINTER_SHAPE_BUFFERS; ++i)
|
||||
for(int i = 0; i < LGMP_Q_POINTER_LEN; ++i)
|
||||
lgmpHostMemFree(&app.pointerMemory[i]);
|
||||
lgmpHostMemFree(&app.pointerShape);
|
||||
for(int i = 0; i < POINTER_SHAPE_BUFFERS; ++i)
|
||||
lgmpHostMemFree(&app.pointerShapeMemory[i]);
|
||||
lgmpHostFree(&app.lgmp);
|
||||
|
||||
fail_ivshmem:
|
||||
|
||||
Reference in New Issue
Block a user