2021-06-06 01:26:18 +00:00
|
|
|
/**
|
|
|
|
* Looking Glass
|
2021-08-04 09:48:32 +00:00
|
|
|
* Copyright © 2017-2021 The Looking Glass Authors
|
2021-06-06 01:26:18 +00:00
|
|
|
* 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
|
|
|
|
*/
|
2017-10-31 08:07:16 +00:00
|
|
|
|
2019-03-30 01:26:06 +00:00
|
|
|
#include "main.h"
|
2019-03-30 04:52:00 +00:00
|
|
|
#include "config.h"
|
2019-03-30 01:26:06 +00:00
|
|
|
|
2017-11-25 07:20:30 +00:00
|
|
|
#include <getopt.h>
|
2017-12-17 09:09:47 +00:00
|
|
|
#include <signal.h>
|
2017-10-19 04:15:49 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2017-12-28 04:30:03 +00:00
|
|
|
#include <sys/mman.h>
|
2019-03-30 04:52:00 +00:00
|
|
|
#include <sys/stat.h>
|
2017-12-28 04:30:03 +00:00
|
|
|
#include <fcntl.h>
|
2017-10-19 04:15:49 +00:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdbool.h>
|
2021-06-12 09:06:32 +00:00
|
|
|
#include <math.h>
|
2020-08-09 05:14:17 +00:00
|
|
|
#include <stdatomic.h>
|
2021-01-18 02:54:21 +00:00
|
|
|
#include <linux/input.h>
|
2017-10-19 04:15:49 +00:00
|
|
|
|
2021-08-13 08:20:57 +00:00
|
|
|
#include "common/array.h"
|
2019-04-11 01:12:59 +00:00
|
|
|
#include "common/debug.h"
|
2019-04-11 06:41:52 +00:00
|
|
|
#include "common/crash.h"
|
2019-04-11 01:12:59 +00:00
|
|
|
#include "common/KVMFR.h"
|
2019-05-23 19:29:38 +00:00
|
|
|
#include "common/stringutils.h"
|
2020-01-02 12:59:06 +00:00
|
|
|
#include "common/thread.h"
|
2020-01-17 03:35:08 +00:00
|
|
|
#include "common/locking.h"
|
2020-01-02 13:08:43 +00:00
|
|
|
#include "common/event.h"
|
2020-01-03 04:17:14 +00:00
|
|
|
#include "common/ivshmem.h"
|
2020-01-03 04:53:44 +00:00
|
|
|
#include "common/time.h"
|
2020-10-08 15:17:20 +00:00
|
|
|
#include "common/version.h"
|
2021-07-31 11:05:48 +00:00
|
|
|
#include "common/paths.h"
|
2021-08-31 00:12:35 +00:00
|
|
|
#include "common/cpuinfo.h"
|
2020-01-03 04:17:14 +00:00
|
|
|
|
2021-01-25 08:58:36 +00:00
|
|
|
#include "core.h"
|
2021-01-21 04:39:15 +00:00
|
|
|
#include "app.h"
|
2021-01-25 16:34:22 +00:00
|
|
|
#include "keybind.h"
|
2021-01-25 08:58:36 +00:00
|
|
|
#include "clipboard.h"
|
2021-07-30 04:37:57 +00:00
|
|
|
#include "kb.h"
|
2019-02-24 18:43:18 +00:00
|
|
|
#include "ll.h"
|
2021-01-26 10:46:30 +00:00
|
|
|
#include "egl_dynprocs.h"
|
2021-09-30 10:14:25 +00:00
|
|
|
#include "gl_dynprocs.h"
|
2021-07-22 07:27:30 +00:00
|
|
|
#include "overlays.h"
|
2021-07-23 04:01:10 +00:00
|
|
|
#include "overlay_utils.h"
|
|
|
|
#include "util.h"
|
2020-01-03 04:53:44 +00:00
|
|
|
|
2019-03-30 01:26:06 +00:00
|
|
|
// forwards
|
|
|
|
static int renderThread(void * unused);
|
2017-11-25 06:51:34 +00:00
|
|
|
|
2020-01-02 13:08:43 +00:00
|
|
|
static LGEvent *e_startup = NULL;
|
|
|
|
static LGThread *t_spice = NULL;
|
|
|
|
static LGThread *t_render = NULL;
|
2019-10-26 01:03:10 +00:00
|
|
|
|
2021-08-04 20:40:06 +00:00
|
|
|
struct AppState g_state = { 0 };
|
2021-01-04 01:04:43 +00:00
|
|
|
struct CursorState g_cursor;
|
2019-05-21 05:03:59 +00:00
|
|
|
|
|
|
|
// this structure is initialized in config.c
|
2021-01-25 08:58:36 +00:00
|
|
|
struct AppParams g_params = { 0 };
|
2021-01-09 07:41:02 +00:00
|
|
|
|
2021-01-14 06:05:26 +00:00
|
|
|
static void lgInit(void)
|
2020-12-03 13:32:28 +00:00
|
|
|
{
|
2021-01-04 01:06:54 +00:00
|
|
|
g_state.state = APP_STATE_RUNNING;
|
2021-01-21 04:39:15 +00:00
|
|
|
g_state.formatValid = false;
|
2021-01-04 01:06:54 +00:00
|
|
|
g_state.resizeDone = true;
|
2020-12-03 13:32:28 +00:00
|
|
|
|
2021-01-21 04:39:15 +00:00
|
|
|
if (g_cursor.grab)
|
2021-01-25 08:58:36 +00:00
|
|
|
core_setGrab(false);
|
2021-01-21 04:39:15 +00:00
|
|
|
|
2021-01-08 09:49:20 +00:00
|
|
|
g_cursor.useScale = false;
|
|
|
|
g_cursor.scale.x = 1.0;
|
|
|
|
g_cursor.scale.y = 1.0;
|
2021-01-18 19:00:59 +00:00
|
|
|
g_cursor.draw = false;
|
2021-01-04 04:40:02 +00:00
|
|
|
g_cursor.inView = false;
|
2021-01-04 01:04:43 +00:00
|
|
|
g_cursor.guest.valid = false;
|
2021-01-21 04:39:15 +00:00
|
|
|
|
|
|
|
// if spice is not in use, hide the local cursor
|
2021-08-04 20:11:12 +00:00
|
|
|
if ((!g_params.useSpiceInput && g_params.hideMouse) || !g_params.showCursorDot)
|
2021-07-29 20:31:07 +00:00
|
|
|
g_state.ds->setPointer(LG_POINTER_NONE);
|
2021-01-21 04:39:15 +00:00
|
|
|
else
|
2021-07-29 20:31:07 +00:00
|
|
|
g_state.ds->setPointer(LG_POINTER_SQUARE);
|
2020-12-03 13:32:28 +00:00
|
|
|
}
|
|
|
|
|
2021-07-19 00:58:40 +00:00
|
|
|
static bool fpsTimerFn(void * unused)
|
|
|
|
{
|
2021-07-20 05:16:01 +00:00
|
|
|
static uint64_t last;
|
|
|
|
if (!last)
|
|
|
|
{
|
2021-07-20 05:37:51 +00:00
|
|
|
last = nanotime();
|
2021-07-20 05:16:01 +00:00
|
|
|
return true;
|
|
|
|
}
|
2021-07-19 00:58:40 +00:00
|
|
|
|
2021-07-20 05:16:01 +00:00
|
|
|
const uint64_t renderCount = atomic_exchange_explicit(&g_state.renderCount, 0,
|
|
|
|
memory_order_acquire);
|
2021-07-19 00:58:40 +00:00
|
|
|
|
2021-07-21 06:47:37 +00:00
|
|
|
float fps, ups;
|
|
|
|
if (renderCount > 0)
|
|
|
|
{
|
|
|
|
const uint64_t frameCount = atomic_exchange_explicit(&g_state.frameCount, 0,
|
|
|
|
memory_order_acquire);
|
2021-07-20 05:16:01 +00:00
|
|
|
|
2021-07-21 06:47:37 +00:00
|
|
|
const uint64_t time = nanotime();
|
|
|
|
const uint64_t elapsedNs = time - last;
|
|
|
|
const float elapsedMs = (float)elapsedNs / 1e6f;
|
2021-07-19 00:58:40 +00:00
|
|
|
|
2021-07-21 06:47:37 +00:00
|
|
|
last = time;
|
|
|
|
fps = 1e3f / (elapsedMs / (float)renderCount);
|
|
|
|
ups = 1e3f / (elapsedMs / (float)frameCount);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
last = nanotime();
|
|
|
|
fps = 0.0f;
|
|
|
|
ups = 0.0f;
|
|
|
|
}
|
2021-07-20 05:37:51 +00:00
|
|
|
|
2021-07-21 06:47:37 +00:00
|
|
|
atomic_store_explicit(&g_state.fps, fps, memory_order_relaxed);
|
|
|
|
atomic_store_explicit(&g_state.ups, ups, memory_order_relaxed);
|
2021-07-19 00:58:40 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-08-06 15:44:36 +00:00
|
|
|
static void preSwapCallback(void * udata)
|
|
|
|
{
|
|
|
|
const uint64_t * renderStart = (const uint64_t *)udata;
|
|
|
|
ringbuffer_push(g_state.renderDuration,
|
|
|
|
&(float) {(nanotime() - *renderStart) * 1e-6f});
|
|
|
|
}
|
|
|
|
|
2019-03-28 10:23:24 +00:00
|
|
|
static int renderThread(void * unused)
|
2017-10-19 04:15:49 +00:00
|
|
|
{
|
2021-08-08 05:43:42 +00:00
|
|
|
if (!RENDERER(renderStartup, g_state.useDMA))
|
2019-05-27 08:38:36 +00:00
|
|
|
{
|
2021-08-14 02:04:54 +00:00
|
|
|
DEBUG_ERROR("EGL render failed to start");
|
2021-01-04 01:06:54 +00:00
|
|
|
g_state.state = APP_STATE_SHUTDOWN;
|
2018-07-28 04:49:37 +00:00
|
|
|
|
2019-12-09 16:30:04 +00:00
|
|
|
/* unblock threads waiting on the condition */
|
2020-01-02 13:08:43 +00:00
|
|
|
lgSignalEvent(e_startup);
|
2019-03-28 10:23:24 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-08-08 05:32:01 +00:00
|
|
|
if (g_state.lgr->ops.supports && !RENDERER(supports, LG_SUPPORTS_DMABUF))
|
2021-08-02 17:58:30 +00:00
|
|
|
g_state.useDMA = false;
|
|
|
|
|
2021-07-19 00:58:40 +00:00
|
|
|
/* start up the fps timer */
|
|
|
|
LGTimer * fpsTimer;
|
2021-07-21 06:56:49 +00:00
|
|
|
if (!lgCreateTimer(500, fpsTimerFn, NULL, &fpsTimer))
|
2021-07-19 00:58:40 +00:00
|
|
|
{
|
|
|
|
DEBUG_ERROR("Failed to create the fps timer");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-07-04 11:54:44 +00:00
|
|
|
LG_LOCK_INIT(g_state.lgrLock);
|
|
|
|
|
2019-12-09 16:30:04 +00:00
|
|
|
/* signal to other threads that the renderer is ready */
|
2020-01-02 13:08:43 +00:00
|
|
|
lgSignalEvent(e_startup);
|
2019-12-09 16:30:04 +00:00
|
|
|
|
2018-12-12 14:21:56 +00:00
|
|
|
struct timespec time;
|
2020-10-29 15:36:12 +00:00
|
|
|
clock_gettime(CLOCK_MONOTONIC, &time);
|
|
|
|
|
2021-01-04 01:06:54 +00:00
|
|
|
while(g_state.state != APP_STATE_SHUTDOWN)
|
2017-12-19 13:53:45 +00:00
|
|
|
{
|
2021-08-03 20:49:35 +00:00
|
|
|
bool forceRender = false;
|
|
|
|
if (g_state.jitRender)
|
|
|
|
forceRender = g_state.ds->waitFrame();
|
|
|
|
|
2021-08-01 08:04:43 +00:00
|
|
|
app_handleRenderEvent(microtime());
|
2021-08-01 07:38:09 +00:00
|
|
|
if (g_state.jitRender)
|
|
|
|
{
|
2021-08-01 09:32:01 +00:00
|
|
|
const uint64_t pending =
|
|
|
|
atomic_load_explicit(&g_state.pendingCount, memory_order_acquire);
|
2021-08-01 11:13:59 +00:00
|
|
|
if (!lgResetEvent(g_state.frameEvent)
|
2021-08-03 20:49:35 +00:00
|
|
|
&& !forceRender
|
2021-08-01 11:13:59 +00:00
|
|
|
&& !pending
|
|
|
|
&& !app_overlayNeedsRender()
|
2021-08-08 05:43:42 +00:00
|
|
|
&& !RENDERER(needsRender))
|
2021-08-01 08:07:25 +00:00
|
|
|
{
|
|
|
|
if (g_state.ds->skipFrame)
|
|
|
|
g_state.ds->skipFrame();
|
2021-08-01 07:38:09 +00:00
|
|
|
continue;
|
2021-08-01 08:07:25 +00:00
|
|
|
}
|
2021-08-01 09:32:01 +00:00
|
|
|
|
|
|
|
if (pending > 0)
|
|
|
|
atomic_fetch_sub(&g_state.pendingCount, 1);
|
2021-08-01 07:38:09 +00:00
|
|
|
}
|
2021-07-31 08:02:24 +00:00
|
|
|
else if (g_params.fpsMin != 0)
|
2020-05-22 08:39:19 +00:00
|
|
|
{
|
2021-07-21 06:56:49 +00:00
|
|
|
float ups = atomic_load_explicit(&g_state.ups, memory_order_relaxed);
|
|
|
|
|
2021-07-25 05:29:29 +00:00
|
|
|
if (!lgWaitEventAbs(g_state.frameEvent, &time) || ups > g_params.fpsMin)
|
2021-07-21 06:47:37 +00:00
|
|
|
{
|
|
|
|
/* only update the time if we woke up early */
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &time);
|
2021-07-31 06:19:55 +00:00
|
|
|
tsAdd(&time, g_state.overlayInput ?
|
|
|
|
g_state.overlayFrameTime : g_state.frameTime);
|
2021-07-21 06:47:37 +00:00
|
|
|
}
|
2020-05-22 08:39:19 +00:00
|
|
|
}
|
|
|
|
|
2021-01-04 02:40:03 +00:00
|
|
|
int resize = atomic_load(&g_state.lgrResize);
|
|
|
|
if (resize)
|
2018-07-28 04:49:37 +00:00
|
|
|
{
|
2021-07-22 22:57:35 +00:00
|
|
|
g_state.io->DisplaySize = (ImVec2) {
|
2021-07-30 01:46:50 +00:00
|
|
|
.x = g_state.windowW,
|
|
|
|
.y = g_state.windowH,
|
2021-07-22 22:57:35 +00:00
|
|
|
};
|
2021-07-30 01:46:50 +00:00
|
|
|
g_state.io->DisplayFramebufferScale = (ImVec2) {
|
|
|
|
.x = g_state.windowScale,
|
|
|
|
.y = g_state.windowScale,
|
|
|
|
};
|
|
|
|
g_state.io->FontGlobalScale = 1.0f / g_state.windowScale;
|
2021-07-23 04:01:10 +00:00
|
|
|
|
|
|
|
ImFontAtlas_Clear(g_state.io->Fonts);
|
|
|
|
ImFontAtlas_AddFontFromFileTTF(g_state.io->Fonts, g_state.fontName,
|
2021-07-23 04:22:04 +00:00
|
|
|
g_params.uiSize * g_state.windowScale, NULL, NULL);
|
2021-07-23 09:58:33 +00:00
|
|
|
g_state.fontLarge = ImFontAtlas_AddFontFromFileTTF(g_state.io->Fonts,
|
2021-07-25 06:48:50 +00:00
|
|
|
g_state.fontName, 1.3f * g_params.uiSize * g_state.windowScale, NULL, NULL);
|
2021-07-26 07:05:52 +00:00
|
|
|
if (!ImFontAtlas_Build(g_state.io->Fonts))
|
|
|
|
DEBUG_FATAL("Failed to build font atlas: %s (%s)", g_params.uiFont, g_state.fontName);
|
2021-07-23 04:01:10 +00:00
|
|
|
|
2021-01-04 01:06:54 +00:00
|
|
|
if (g_state.lgr)
|
2021-08-08 05:43:42 +00:00
|
|
|
RENDERER(onResize, g_state.windowW, g_state.windowH,
|
2021-02-21 05:08:52 +00:00
|
|
|
g_state.windowScale, g_state.dstRect, g_params.winRotate);
|
2021-01-04 02:40:03 +00:00
|
|
|
atomic_compare_exchange_weak(&g_state.lgrResize, &resize, 0);
|
2018-07-28 04:49:37 +00:00
|
|
|
}
|
|
|
|
|
2021-07-21 07:26:48 +00:00
|
|
|
static uint64_t lastFrameCount = 0;
|
|
|
|
const uint64_t frameCount =
|
|
|
|
atomic_load_explicit(&g_state.frameCount, memory_order_relaxed);
|
|
|
|
const bool newFrame = frameCount != lastFrameCount;
|
|
|
|
lastFrameCount = frameCount;
|
|
|
|
|
2021-07-25 05:29:29 +00:00
|
|
|
const bool invalidate = atomic_exchange(&g_state.invalidateWindow, false);
|
|
|
|
|
2021-08-03 10:34:55 +00:00
|
|
|
const uint64_t renderStart = nanotime();
|
2021-07-04 11:54:44 +00:00
|
|
|
LG_LOCK(g_state.lgrLock);
|
2021-08-08 05:32:01 +00:00
|
|
|
if (!RENDERER(render, g_params.winRotate, newFrame, invalidate,
|
|
|
|
preSwapCallback, (void *)&renderStart))
|
2021-07-04 11:54:44 +00:00
|
|
|
{
|
|
|
|
LG_UNLOCK(g_state.lgrLock);
|
2018-05-28 05:30:04 +00:00
|
|
|
break;
|
2021-07-04 11:54:44 +00:00
|
|
|
}
|
|
|
|
LG_UNLOCK(g_state.lgrLock);
|
2018-05-23 23:01:53 +00:00
|
|
|
|
2021-07-19 00:58:40 +00:00
|
|
|
const uint64_t t = nanotime();
|
|
|
|
const uint64_t delta = t - g_state.lastRenderTime;
|
|
|
|
|
2021-07-08 15:53:49 +00:00
|
|
|
g_state.lastRenderTime = t;
|
2021-07-20 05:16:01 +00:00
|
|
|
atomic_fetch_add_explicit(&g_state.renderCount, 1, memory_order_relaxed);
|
2021-07-10 04:20:28 +00:00
|
|
|
|
|
|
|
if (g_state.lastRenderTimeValid)
|
|
|
|
{
|
2021-07-19 00:58:40 +00:00
|
|
|
const float fdelta = (float)delta / 1e6f;
|
2021-07-10 04:20:28 +00:00
|
|
|
ringbuffer_push(g_state.renderTimings, &fdelta);
|
|
|
|
}
|
|
|
|
g_state.lastRenderTimeValid = true;
|
2021-07-08 15:53:49 +00:00
|
|
|
|
2021-02-25 23:21:56 +00:00
|
|
|
const uint64_t now = microtime();
|
|
|
|
if (!g_state.resizeDone && g_state.resizeTimeout < now)
|
2020-01-03 04:53:44 +00:00
|
|
|
{
|
2021-05-05 13:17:15 +00:00
|
|
|
if (g_params.autoResize)
|
|
|
|
{
|
|
|
|
g_state.ds->setWindowSize(
|
|
|
|
g_state.dstRect.w,
|
|
|
|
g_state.dstRect.h
|
|
|
|
);
|
|
|
|
}
|
2021-01-04 01:06:54 +00:00
|
|
|
g_state.resizeDone = true;
|
2020-01-03 04:53:44 +00:00
|
|
|
}
|
2017-12-19 13:53:45 +00:00
|
|
|
}
|
2018-01-24 12:46:11 +00:00
|
|
|
|
2021-01-04 01:06:54 +00:00
|
|
|
g_state.state = APP_STATE_SHUTDOWN;
|
2019-08-30 01:50:43 +00:00
|
|
|
|
2021-07-19 00:58:40 +00:00
|
|
|
lgTimerDestroy(fpsTimer);
|
|
|
|
|
2021-10-06 09:04:06 +00:00
|
|
|
core_stopCursorThread();
|
2021-01-25 16:34:22 +00:00
|
|
|
core_stopFrameThread();
|
2019-08-30 01:36:28 +00:00
|
|
|
|
2021-08-08 05:32:01 +00:00
|
|
|
RENDERER(deinitialize);
|
2021-01-04 01:06:54 +00:00
|
|
|
g_state.lgr = NULL;
|
2021-07-04 11:54:44 +00:00
|
|
|
LG_LOCK_FREE(g_state.lgrLock);
|
|
|
|
|
2018-01-24 12:46:11 +00:00
|
|
|
return 0;
|
2017-12-19 13:53:45 +00:00
|
|
|
}
|
2017-12-15 08:13:36 +00:00
|
|
|
|
2021-10-06 09:04:06 +00:00
|
|
|
int main_cursorThread(void * unused)
|
2017-12-19 13:53:45 +00:00
|
|
|
{
|
2020-01-09 09:32:42 +00:00
|
|
|
LGMP_STATUS status;
|
2021-11-01 02:44:46 +00:00
|
|
|
LG_RendererCursor cursorType = LG_CURSOR_COLOR;
|
|
|
|
KVMFRCursor * cursor = NULL;
|
2021-11-01 14:01:17 +00:00
|
|
|
int cursorSize = 0;
|
2017-12-15 01:02:08 +00:00
|
|
|
|
2020-01-02 13:08:43 +00:00
|
|
|
lgWaitEvent(e_startup, TIMEOUT_INFINITE);
|
2020-01-09 09:32:42 +00:00
|
|
|
|
|
|
|
// subscribe to the pointer queue
|
2021-01-04 01:06:54 +00:00
|
|
|
while(g_state.state == APP_STATE_RUNNING)
|
2017-12-06 15:37:46 +00:00
|
|
|
{
|
2021-08-05 12:19:35 +00:00
|
|
|
status = lgmpClientSubscribe(g_state.lgmp, LGMP_Q_POINTER,
|
|
|
|
&g_state.pointerQueue);
|
2020-01-09 09:32:42 +00:00
|
|
|
if (status == LGMP_OK)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (status == LGMP_ERR_NO_SUCH_QUEUE)
|
2017-12-20 14:56:59 +00:00
|
|
|
{
|
2020-01-09 09:32:42 +00:00
|
|
|
usleep(1000);
|
2017-12-20 14:56:59 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-01-09 09:32:42 +00:00
|
|
|
DEBUG_ERROR("lgmpClientSubscribe Failed: %s", lgmpStatusString(status));
|
2021-01-04 01:06:54 +00:00
|
|
|
g_state.state = APP_STATE_SHUTDOWN;
|
2020-01-09 09:32:42 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-10-06 09:04:06 +00:00
|
|
|
while(g_state.state == APP_STATE_RUNNING && !g_state.stopVideo)
|
2020-01-09 09:32:42 +00:00
|
|
|
{
|
|
|
|
LGMPMessage msg;
|
2021-08-05 12:19:35 +00:00
|
|
|
if ((status = lgmpClientProcess(g_state.pointerQueue, &msg)) != LGMP_OK)
|
2019-01-11 12:59:46 +00:00
|
|
|
{
|
2020-01-09 09:32:42 +00:00
|
|
|
if (status == LGMP_ERR_QUEUE_EMPTY)
|
|
|
|
{
|
2021-01-08 10:46:34 +00:00
|
|
|
if (g_cursor.redraw && g_cursor.guest.valid)
|
2020-01-10 19:03:16 +00:00
|
|
|
{
|
2021-01-04 01:04:43 +00:00
|
|
|
g_cursor.redraw = false;
|
2021-08-08 05:43:42 +00:00
|
|
|
RENDERER(onMouseEvent,
|
2021-01-25 08:58:36 +00:00
|
|
|
g_cursor.guest.visible && (g_cursor.draw || !g_params.useSpiceInput),
|
2021-01-04 01:04:43 +00:00
|
|
|
g_cursor.guest.x,
|
2021-10-22 12:19:56 +00:00
|
|
|
g_cursor.guest.y,
|
|
|
|
g_cursor.guest.hx,
|
|
|
|
g_cursor.guest.hy
|
2020-01-10 19:03:16 +00:00
|
|
|
);
|
2020-08-20 06:05:55 +00:00
|
|
|
|
2021-07-17 01:46:30 +00:00
|
|
|
if (!g_state.stopVideo)
|
2021-07-25 05:29:29 +00:00
|
|
|
lgSignalEvent(g_state.frameEvent);
|
2020-01-10 19:03:16 +00:00
|
|
|
}
|
|
|
|
|
2021-09-02 21:41:33 +00:00
|
|
|
struct timespec req =
|
2021-01-07 21:27:12 +00:00
|
|
|
{
|
|
|
|
.tv_sec = 0,
|
2021-01-25 08:58:36 +00:00
|
|
|
.tv_nsec = g_params.cursorPollInterval * 1000L
|
2021-01-07 21:27:12 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct timespec rem;
|
|
|
|
while(nanosleep(&req, &rem) < 0)
|
2021-09-02 21:41:33 +00:00
|
|
|
{
|
2021-01-07 21:27:12 +00:00
|
|
|
if (errno != -EINTR)
|
|
|
|
{
|
|
|
|
DEBUG_ERROR("nanosleep failed");
|
|
|
|
break;
|
|
|
|
}
|
2021-09-02 21:41:33 +00:00
|
|
|
req = rem;
|
|
|
|
}
|
2021-01-07 21:27:12 +00:00
|
|
|
|
2020-01-09 09:32:42 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-08-11 04:30:44 +00:00
|
|
|
if (status == LGMP_ERR_INVALID_SESSION)
|
2021-01-04 01:06:54 +00:00
|
|
|
g_state.state = APP_STATE_RESTART;
|
2020-08-11 04:45:43 +00:00
|
|
|
else
|
2020-08-11 05:14:58 +00:00
|
|
|
{
|
2020-08-11 04:45:43 +00:00
|
|
|
DEBUG_ERROR("lgmpClientProcess Failed: %s", lgmpStatusString(status));
|
2021-01-04 01:06:54 +00:00
|
|
|
g_state.state = APP_STATE_SHUTDOWN;
|
2020-08-11 05:14:58 +00:00
|
|
|
}
|
2020-01-09 09:32:42 +00:00
|
|
|
break;
|
2019-01-11 12:59:46 +00:00
|
|
|
}
|
|
|
|
|
2021-11-01 14:01:17 +00:00
|
|
|
if (cursor && msg.size > cursorSize)
|
|
|
|
{
|
|
|
|
free(cursor);
|
|
|
|
cursor = NULL;
|
|
|
|
}
|
|
|
|
|
2021-07-12 10:52:24 +00:00
|
|
|
/* copy and release the message ASAP */
|
2021-11-01 02:44:46 +00:00
|
|
|
if (!cursor)
|
|
|
|
{
|
|
|
|
cursor = malloc(msg.size);
|
|
|
|
if (!cursor)
|
|
|
|
{
|
|
|
|
DEBUG_ERROR("failed to allocate %d bytes for cursor", msg.size);
|
|
|
|
g_state.state = APP_STATE_SHUTDOWN;
|
|
|
|
break;
|
|
|
|
}
|
2021-11-01 14:01:17 +00:00
|
|
|
cursorSize = msg.size;
|
2021-11-01 02:44:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(cursor, msg.mem, msg.size);
|
2021-08-05 12:19:35 +00:00
|
|
|
lgmpClientMessageDone(g_state.pointerQueue);
|
2020-01-10 18:22:12 +00:00
|
|
|
|
2021-01-04 01:04:43 +00:00
|
|
|
g_cursor.guest.visible =
|
2020-01-26 15:07:32 +00:00
|
|
|
msg.udata & CURSOR_FLAG_VISIBLE;
|
|
|
|
|
2020-01-26 06:30:16 +00:00
|
|
|
if (msg.udata & CURSOR_FLAG_SHAPE)
|
2019-01-11 12:59:46 +00:00
|
|
|
{
|
2020-01-09 10:18:35 +00:00
|
|
|
switch(cursor->type)
|
|
|
|
{
|
|
|
|
case CURSOR_TYPE_COLOR : cursorType = LG_CURSOR_COLOR ; break;
|
|
|
|
case CURSOR_TYPE_MONOCHROME : cursorType = LG_CURSOR_MONOCHROME ; break;
|
|
|
|
case CURSOR_TYPE_MASKED_COLOR: cursorType = LG_CURSOR_MASKED_COLOR; break;
|
|
|
|
default:
|
|
|
|
DEBUG_ERROR("Invalid cursor type");
|
2021-08-05 12:19:35 +00:00
|
|
|
lgmpClientMessageDone(g_state.pointerQueue);
|
2020-01-09 10:18:35 +00:00
|
|
|
continue;
|
|
|
|
}
|
2019-01-11 12:59:46 +00:00
|
|
|
|
2021-01-04 01:04:43 +00:00
|
|
|
g_cursor.guest.hx = cursor->hx;
|
|
|
|
g_cursor.guest.hy = cursor->hy;
|
2020-08-20 03:46:18 +00:00
|
|
|
|
2020-01-09 10:18:35 +00:00
|
|
|
const uint8_t * data = (const uint8_t *)(cursor + 1);
|
2021-08-08 05:43:42 +00:00
|
|
|
if (!RENDERER(onMouseShape,
|
2020-01-09 10:18:35 +00:00
|
|
|
cursorType,
|
|
|
|
cursor->width,
|
|
|
|
cursor->height,
|
|
|
|
cursor->pitch,
|
|
|
|
data)
|
|
|
|
)
|
|
|
|
{
|
|
|
|
DEBUG_ERROR("Failed to update mouse shape");
|
2020-01-09 09:32:42 +00:00
|
|
|
continue;
|
2020-01-09 10:18:35 +00:00
|
|
|
}
|
2020-01-09 09:32:42 +00:00
|
|
|
}
|
2017-11-25 09:21:57 +00:00
|
|
|
|
2020-08-20 04:52:24 +00:00
|
|
|
if (msg.udata & CURSOR_FLAG_POSITION)
|
|
|
|
{
|
2021-01-08 12:12:15 +00:00
|
|
|
bool valid = g_cursor.guest.valid;
|
2021-01-08 08:41:10 +00:00
|
|
|
g_cursor.guest.x = cursor->x;
|
|
|
|
g_cursor.guest.y = cursor->y;
|
2021-01-04 01:04:43 +00:00
|
|
|
g_cursor.guest.valid = true;
|
2021-01-08 12:12:15 +00:00
|
|
|
|
2021-01-09 07:01:08 +00:00
|
|
|
// if the state just became valid
|
2021-01-28 18:13:26 +00:00
|
|
|
if (valid != true && core_inputEnabled())
|
[client] spice/wayland: improve cursor tracking logic
One of the major issues with the old tracking code is a data race
between the cursor thread updating g_cursor.guest and the
app_handleMouseBasic function. Specifically, the latter may have
sent mouse input via spice that has not been processed by the guest
and updated g_cursor.guest, but the guest may overwrite g_cursor.guest
to a previous state before the input is processed. This causes some
movements to be doubled. Eventually, the cursor positions will
synchronize, but this nevertheless causes a lot of jitter.
In this commit, we introduce a new field g_cursor.projected, which
is unambiguously the position of the cursor after taking into account
all the input already sent via spice. This is synced up to the guest
cursor upon entering the window and when the host restarts. Afterwards,
all mouse movements will be based on this position. This eliminates
all cursor jitter as far as I could tell.
Also, the cursor is now synced to the host position when exiting
capture mode.
A downside of this commit is that if the 1:1 movement patch is not
correctly applied, the cursor position would be wildly off instead
of simply jittering, but that is an unsupported configuration and
should not matter.
Also unsupported is when an application in guest moves the cursor
programmatically and bypassing spice. When using those applications,
capture mode must be on. Before this commit, we try to move the guest
cursor back to where it should be, but it's inherently fragile and
may lead to scenarios such as wild movements in first-person shooters.
2021-01-21 02:05:50 +00:00
|
|
|
{
|
2021-01-25 08:58:36 +00:00
|
|
|
core_alignToGuest();
|
[client] spice/wayland: improve cursor tracking logic
One of the major issues with the old tracking code is a data race
between the cursor thread updating g_cursor.guest and the
app_handleMouseBasic function. Specifically, the latter may have
sent mouse input via spice that has not been processed by the guest
and updated g_cursor.guest, but the guest may overwrite g_cursor.guest
to a previous state before the input is processed. This causes some
movements to be doubled. Eventually, the cursor positions will
synchronize, but this nevertheless causes a lot of jitter.
In this commit, we introduce a new field g_cursor.projected, which
is unambiguously the position of the cursor after taking into account
all the input already sent via spice. This is synced up to the guest
cursor upon entering the window and when the host restarts. Afterwards,
all mouse movements will be based on this position. This eliminates
all cursor jitter as far as I could tell.
Also, the cursor is now synced to the host position when exiting
capture mode.
A downside of this commit is that if the 1:1 movement patch is not
correctly applied, the cursor position would be wildly off instead
of simply jittering, but that is an unsupported configuration and
should not matter.
Also unsupported is when an application in guest moves the cursor
programmatically and bypassing spice. When using those applications,
capture mode must be on. Before this commit, we try to move the guest
cursor back to where it should be, but it's inherently fragile and
may lead to scenarios such as wild movements in first-person shooters.
2021-01-21 02:05:50 +00:00
|
|
|
app_resyncMouseBasic();
|
|
|
|
}
|
2021-05-03 20:35:36 +00:00
|
|
|
|
|
|
|
// tell the DS there was an update
|
|
|
|
core_handleGuestMouseUpdate();
|
2020-08-20 04:52:24 +00:00
|
|
|
}
|
|
|
|
|
2021-01-04 01:04:43 +00:00
|
|
|
g_cursor.redraw = false;
|
2020-01-26 06:30:16 +00:00
|
|
|
|
2021-08-08 05:43:42 +00:00
|
|
|
RENDERER(onMouseEvent,
|
2021-01-25 08:58:36 +00:00
|
|
|
g_cursor.guest.visible && (g_cursor.draw || !g_params.useSpiceInput),
|
2021-01-04 01:04:43 +00:00
|
|
|
g_cursor.guest.x,
|
2021-10-22 12:19:56 +00:00
|
|
|
g_cursor.guest.y,
|
|
|
|
g_cursor.guest.hx,
|
|
|
|
g_cursor.guest.hy
|
2020-01-26 15:11:21 +00:00
|
|
|
);
|
2020-08-20 05:50:33 +00:00
|
|
|
|
2021-07-17 01:46:30 +00:00
|
|
|
if (g_params.mouseRedraw && g_cursor.guest.visible && !g_state.stopVideo)
|
2021-07-25 05:29:29 +00:00
|
|
|
lgSignalEvent(g_state.frameEvent);
|
2017-12-19 13:53:45 +00:00
|
|
|
}
|
2017-12-15 07:47:44 +00:00
|
|
|
|
2021-08-05 12:19:35 +00:00
|
|
|
lgmpClientUnsubscribe(&g_state.pointerQueue);
|
2021-11-01 02:44:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
if (cursor)
|
|
|
|
{
|
|
|
|
free(cursor);
|
|
|
|
cursor = NULL;
|
|
|
|
}
|
|
|
|
|
2017-12-19 13:53:45 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2017-12-15 08:13:36 +00:00
|
|
|
|
2021-01-25 16:34:22 +00:00
|
|
|
int main_frameThread(void * unused)
|
2017-12-19 13:53:45 +00:00
|
|
|
{
|
2020-10-29 11:57:04 +00:00
|
|
|
struct DMAFrameInfo
|
|
|
|
{
|
|
|
|
KVMFRFrame * frame;
|
|
|
|
size_t dataSize;
|
|
|
|
int fd;
|
|
|
|
};
|
|
|
|
|
2020-01-10 00:00:46 +00:00
|
|
|
LGMP_STATUS status;
|
|
|
|
PLGMPClientQueue queue;
|
2017-12-15 08:13:36 +00:00
|
|
|
|
2021-07-25 03:44:39 +00:00
|
|
|
uint32_t frameSerial = 0;
|
|
|
|
uint32_t formatVer = 0;
|
|
|
|
size_t dataSize = 0;
|
2020-10-12 07:52:37 +00:00
|
|
|
LG_RendererFormat lgrFormat;
|
|
|
|
|
2020-10-30 07:48:41 +00:00
|
|
|
struct DMAFrameInfo dmaInfo[LGMP_Q_FRAME_LEN] = {0};
|
2021-08-02 13:37:33 +00:00
|
|
|
if (g_state.useDMA)
|
2020-10-29 11:57:04 +00:00
|
|
|
DEBUG_INFO("Using DMA buffer support");
|
|
|
|
|
2020-01-02 13:08:43 +00:00
|
|
|
lgWaitEvent(e_startup, TIMEOUT_INFINITE);
|
2021-01-04 01:06:54 +00:00
|
|
|
if (g_state.state != APP_STATE_RUNNING)
|
2020-01-27 01:25:47 +00:00
|
|
|
return 0;
|
2020-01-09 09:32:42 +00:00
|
|
|
|
|
|
|
// subscribe to the frame queue
|
2021-01-04 01:06:54 +00:00
|
|
|
while(g_state.state == APP_STATE_RUNNING)
|
2017-12-19 13:53:45 +00:00
|
|
|
{
|
2021-01-04 01:06:54 +00:00
|
|
|
status = lgmpClientSubscribe(g_state.lgmp, LGMP_Q_FRAME, &queue);
|
2020-01-09 09:32:42 +00:00
|
|
|
if (status == LGMP_OK)
|
|
|
|
break;
|
2018-01-03 22:25:17 +00:00
|
|
|
|
2020-01-09 09:32:42 +00:00
|
|
|
if (status == LGMP_ERR_NO_SUCH_QUEUE)
|
|
|
|
{
|
|
|
|
usleep(1000);
|
2017-12-19 13:53:45 +00:00
|
|
|
continue;
|
|
|
|
}
|
2017-10-19 04:15:49 +00:00
|
|
|
|
2020-01-09 09:32:42 +00:00
|
|
|
DEBUG_ERROR("lgmpClientSubscribe Failed: %s", lgmpStatusString(status));
|
2021-01-04 01:06:54 +00:00
|
|
|
g_state.state = APP_STATE_SHUTDOWN;
|
2020-01-09 09:32:42 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-01-04 01:06:54 +00:00
|
|
|
while(g_state.state == APP_STATE_RUNNING && !g_state.stopVideo)
|
2020-01-09 09:32:42 +00:00
|
|
|
{
|
|
|
|
LGMPMessage msg;
|
|
|
|
if ((status = lgmpClientProcess(queue, &msg)) != LGMP_OK)
|
|
|
|
{
|
|
|
|
if (status == LGMP_ERR_QUEUE_EMPTY)
|
|
|
|
{
|
2021-09-02 21:41:33 +00:00
|
|
|
struct timespec req =
|
2021-01-07 21:27:12 +00:00
|
|
|
{
|
|
|
|
.tv_sec = 0,
|
2021-01-25 08:58:36 +00:00
|
|
|
.tv_nsec = g_params.framePollInterval * 1000L
|
2021-01-07 21:27:12 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct timespec rem;
|
|
|
|
while(nanosleep(&req, &rem) < 0)
|
2021-09-02 21:41:33 +00:00
|
|
|
{
|
2021-01-07 21:27:12 +00:00
|
|
|
if (errno != -EINTR)
|
|
|
|
{
|
|
|
|
DEBUG_ERROR("nanosleep failed");
|
|
|
|
break;
|
|
|
|
}
|
2021-09-02 21:41:33 +00:00
|
|
|
req = rem;
|
|
|
|
}
|
2021-01-07 21:27:12 +00:00
|
|
|
|
2020-01-09 09:32:42 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-08-11 04:30:44 +00:00
|
|
|
if (status == LGMP_ERR_INVALID_SESSION)
|
2021-01-04 01:06:54 +00:00
|
|
|
g_state.state = APP_STATE_RESTART;
|
2020-08-11 04:45:43 +00:00
|
|
|
else
|
2020-08-11 05:14:58 +00:00
|
|
|
{
|
2020-08-11 04:45:43 +00:00
|
|
|
DEBUG_ERROR("lgmpClientProcess Failed: %s", lgmpStatusString(status));
|
2021-01-04 01:06:54 +00:00
|
|
|
g_state.state = APP_STATE_SHUTDOWN;
|
2020-08-11 05:14:58 +00:00
|
|
|
}
|
2020-01-09 09:32:42 +00:00
|
|
|
break;
|
2017-11-25 09:21:57 +00:00
|
|
|
}
|
2017-12-12 15:22:47 +00:00
|
|
|
|
2021-01-26 10:46:30 +00:00
|
|
|
KVMFRFrame * frame = (KVMFRFrame *)msg.mem;
|
2021-07-25 03:44:39 +00:00
|
|
|
|
|
|
|
// ignore any repeated frames, this happens when a new client connects to
|
|
|
|
// the same host application.
|
2021-07-25 05:09:48 +00:00
|
|
|
if (frame->frameSerial == frameSerial && g_state.formatValid)
|
2021-07-25 03:44:39 +00:00
|
|
|
{
|
|
|
|
lgmpClientMessageDone(queue);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
frameSerial = frame->frameSerial;
|
|
|
|
|
2020-10-29 11:57:04 +00:00
|
|
|
struct DMAFrameInfo *dma = NULL;
|
2020-01-09 09:32:42 +00:00
|
|
|
|
2021-01-21 04:39:15 +00:00
|
|
|
if (!g_state.formatValid || frame->formatVer != formatVer)
|
2017-12-12 15:22:47 +00:00
|
|
|
{
|
2020-10-12 07:52:37 +00:00
|
|
|
// setup the renderer format with the frame format details
|
|
|
|
lgrFormat.type = frame->type;
|
|
|
|
lgrFormat.width = frame->width;
|
|
|
|
lgrFormat.height = frame->height;
|
|
|
|
lgrFormat.stride = frame->stride;
|
|
|
|
lgrFormat.pitch = frame->pitch;
|
|
|
|
|
2021-06-12 09:06:32 +00:00
|
|
|
if (frame->height != frame->realHeight)
|
|
|
|
{
|
|
|
|
const float needed =
|
|
|
|
((frame->realHeight * frame->pitch * 2) / 1048576.0f) + 10.0f;
|
|
|
|
const int size = (int)powf(2.0f, ceilf(logf(needed) / logf(2.0f)));
|
|
|
|
|
|
|
|
DEBUG_BREAK();
|
|
|
|
DEBUG_WARN("IVSHMEM too small, screen truncated");
|
|
|
|
DEBUG_WARN("Recommend increase size to %d MiB", size);
|
|
|
|
DEBUG_BREAK();
|
|
|
|
|
|
|
|
app_alert(LG_ALERT_ERROR,
|
|
|
|
"IVSHMEM too small, screen truncated\n"
|
|
|
|
"Recommend increasing size to %d MiB",
|
|
|
|
size);
|
|
|
|
}
|
|
|
|
|
2021-01-18 15:44:56 +00:00
|
|
|
switch(frame->rotation)
|
|
|
|
{
|
|
|
|
case FRAME_ROT_0 : lgrFormat.rotate = LG_ROTATE_0 ; break;
|
|
|
|
case FRAME_ROT_90 : lgrFormat.rotate = LG_ROTATE_90 ; break;
|
|
|
|
case FRAME_ROT_180: lgrFormat.rotate = LG_ROTATE_180; break;
|
|
|
|
case FRAME_ROT_270: lgrFormat.rotate = LG_ROTATE_270; break;
|
|
|
|
}
|
|
|
|
g_state.rotate = lgrFormat.rotate;
|
|
|
|
|
2020-10-29 11:57:04 +00:00
|
|
|
bool error = false;
|
2020-10-12 07:52:37 +00:00
|
|
|
switch(frame->type)
|
|
|
|
{
|
|
|
|
case FRAME_TYPE_RGBA:
|
|
|
|
case FRAME_TYPE_BGRA:
|
|
|
|
case FRAME_TYPE_RGBA10:
|
|
|
|
dataSize = lgrFormat.height * lgrFormat.pitch;
|
|
|
|
lgrFormat.bpp = 32;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FRAME_TYPE_RGBA16F:
|
|
|
|
dataSize = lgrFormat.height * lgrFormat.pitch;
|
|
|
|
lgrFormat.bpp = 64;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
DEBUG_ERROR("Unsupported frameType");
|
|
|
|
error = true;
|
|
|
|
break;
|
|
|
|
}
|
2018-07-27 22:41:15 +00:00
|
|
|
|
2020-10-12 07:52:37 +00:00
|
|
|
if (error)
|
|
|
|
{
|
|
|
|
lgmpClientMessageDone(queue);
|
2021-01-04 01:06:54 +00:00
|
|
|
g_state.state = APP_STATE_SHUTDOWN;
|
2017-12-19 13:53:45 +00:00
|
|
|
break;
|
2020-10-12 07:52:37 +00:00
|
|
|
}
|
2017-12-12 16:08:13 +00:00
|
|
|
|
2021-01-21 04:39:15 +00:00
|
|
|
g_state.formatValid = true;
|
|
|
|
formatVer = frame->formatVer;
|
2020-10-12 08:43:29 +00:00
|
|
|
|
2021-01-18 15:44:56 +00:00
|
|
|
DEBUG_INFO("Format: %s %ux%u stride:%u pitch:%u rotation:%d",
|
2020-10-12 08:43:29 +00:00
|
|
|
FrameTypeStr[frame->type],
|
|
|
|
frame->width, frame->height,
|
2021-01-18 15:44:56 +00:00
|
|
|
frame->stride, frame->pitch,
|
|
|
|
frame->rotation);
|
2021-01-15 01:40:59 +00:00
|
|
|
|
2021-07-04 11:54:44 +00:00
|
|
|
LG_LOCK(g_state.lgrLock);
|
2021-08-08 05:43:42 +00:00
|
|
|
if (!RENDERER(onFrameFormat, lgrFormat))
|
2020-10-12 08:43:29 +00:00
|
|
|
{
|
|
|
|
DEBUG_ERROR("renderer failed to configure format");
|
2021-01-04 01:06:54 +00:00
|
|
|
g_state.state = APP_STATE_SHUTDOWN;
|
2021-07-04 11:54:44 +00:00
|
|
|
LG_UNLOCK(g_state.lgrLock);
|
2020-10-12 08:43:29 +00:00
|
|
|
break;
|
|
|
|
}
|
2021-07-04 11:54:44 +00:00
|
|
|
LG_UNLOCK(g_state.lgrLock);
|
2021-01-04 08:16:18 +00:00
|
|
|
|
|
|
|
g_state.srcSize.x = lgrFormat.width;
|
|
|
|
g_state.srcSize.y = lgrFormat.height;
|
|
|
|
g_state.haveSrcSize = true;
|
2021-01-25 08:58:36 +00:00
|
|
|
if (g_params.autoResize)
|
2021-01-26 10:46:30 +00:00
|
|
|
g_state.ds->setWindowSize(lgrFormat.width, lgrFormat.height);
|
2021-01-04 08:16:18 +00:00
|
|
|
|
2021-01-25 08:58:36 +00:00
|
|
|
core_updatePositionInfo();
|
2017-12-19 13:53:45 +00:00
|
|
|
}
|
2017-12-15 08:13:36 +00:00
|
|
|
|
2021-08-02 13:37:33 +00:00
|
|
|
if (g_state.useDMA)
|
2020-10-29 11:57:04 +00:00
|
|
|
{
|
|
|
|
/* find the existing dma buffer if it exists */
|
2021-08-13 08:20:57 +00:00
|
|
|
for(int i = 0; i < ARRAY_LENGTH(dmaInfo); ++i)
|
2020-10-29 11:57:04 +00:00
|
|
|
{
|
|
|
|
if (dmaInfo[i].frame == frame)
|
|
|
|
{
|
|
|
|
dma = &dmaInfo[i];
|
|
|
|
/* if it's too small close it */
|
|
|
|
if (dma->dataSize < dataSize)
|
|
|
|
{
|
|
|
|
close(dma->fd);
|
|
|
|
dma->fd = -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* otherwise find a free buffer for use */
|
|
|
|
if (!dma)
|
2021-08-13 08:20:57 +00:00
|
|
|
for(int i = 0; i < ARRAY_LENGTH(dmaInfo); ++i)
|
2020-10-29 11:57:04 +00:00
|
|
|
{
|
|
|
|
if (!dmaInfo[i].frame)
|
|
|
|
{
|
|
|
|
dma = &dmaInfo[i];
|
|
|
|
dma->frame = frame;
|
|
|
|
dma->fd = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* open the buffer */
|
|
|
|
if (dma->fd == -1)
|
|
|
|
{
|
2021-01-04 01:06:54 +00:00
|
|
|
const uintptr_t pos = (uintptr_t)msg.mem - (uintptr_t)g_state.shm.mem;
|
2020-10-29 11:57:04 +00:00
|
|
|
const uintptr_t offset = (uintptr_t)frame->offset + FrameBufferStructSize;
|
|
|
|
|
|
|
|
dma->dataSize = dataSize;
|
2021-01-04 01:06:54 +00:00
|
|
|
dma->fd = ivshmemGetDMABuf(&g_state.shm, pos + offset, dataSize);
|
2020-10-29 11:57:04 +00:00
|
|
|
if (dma->fd < 0)
|
|
|
|
{
|
|
|
|
DEBUG_ERROR("Failed to get the DMA buffer for the frame");
|
2021-01-04 01:06:54 +00:00
|
|
|
g_state.state = APP_STATE_SHUTDOWN;
|
2020-10-29 11:57:04 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-13 08:30:49 +00:00
|
|
|
FrameBuffer * fb = (FrameBuffer *)(((uint8_t*)frame) + frame->offset);
|
2021-08-08 05:43:42 +00:00
|
|
|
if (!RENDERER(onFrame, fb, g_state.useDMA ? dma->fd : -1,
|
2021-08-08 05:32:01 +00:00
|
|
|
frame->damageRects, frame->damageRectsCount))
|
2017-12-19 13:53:45 +00:00
|
|
|
{
|
2020-10-12 08:43:29 +00:00
|
|
|
lgmpClientMessageDone(queue);
|
|
|
|
DEBUG_ERROR("renderer on frame returned failure");
|
2021-01-04 01:06:54 +00:00
|
|
|
g_state.state = APP_STATE_SHUTDOWN;
|
2017-12-19 13:53:45 +00:00
|
|
|
break;
|
2017-12-12 15:22:47 +00:00
|
|
|
}
|
2020-05-22 08:00:18 +00:00
|
|
|
|
2021-02-03 23:01:53 +00:00
|
|
|
if (g_params.autoScreensaver && g_state.autoIdleInhibitState != frame->blockScreensaver)
|
|
|
|
{
|
|
|
|
if (frame->blockScreensaver)
|
|
|
|
g_state.ds->inhibitIdle();
|
|
|
|
else
|
|
|
|
g_state.ds->uninhibitIdle();
|
|
|
|
g_state.autoIdleInhibitState = frame->blockScreensaver;
|
|
|
|
}
|
|
|
|
|
2021-07-08 15:53:49 +00:00
|
|
|
const uint64_t t = nanotime();
|
|
|
|
const uint64_t delta = t - g_state.lastFrameTime;
|
|
|
|
g_state.lastFrameTime = t;
|
2021-07-10 04:20:28 +00:00
|
|
|
|
|
|
|
if (g_state.lastFrameTimeValid)
|
2021-08-03 21:39:44 +00:00
|
|
|
ringbuffer_push(g_state.uploadTimings, &(float) { delta * 1e-6f });
|
2021-07-10 04:20:28 +00:00
|
|
|
g_state.lastFrameTimeValid = true;
|
2021-07-08 15:53:49 +00:00
|
|
|
|
2021-07-19 00:58:40 +00:00
|
|
|
atomic_fetch_add_explicit(&g_state.frameCount, 1, memory_order_relaxed);
|
2021-08-01 09:32:01 +00:00
|
|
|
if (g_state.jitRender)
|
|
|
|
{
|
|
|
|
if (atomic_load_explicit(&g_state.pendingCount, memory_order_acquire) < 10)
|
|
|
|
atomic_fetch_add_explicit(&g_state.pendingCount, 1,
|
|
|
|
memory_order_release);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
lgSignalEvent(g_state.frameEvent);
|
|
|
|
|
2020-08-09 05:14:17 +00:00
|
|
|
lgmpClientMessageDone(queue);
|
2017-10-19 04:15:49 +00:00
|
|
|
}
|
|
|
|
|
2020-01-09 09:32:42 +00:00
|
|
|
lgmpClientUnsubscribe(&queue);
|
2021-08-08 05:43:42 +00:00
|
|
|
RENDERER(onRestart);
|
2020-10-14 08:40:20 +00:00
|
|
|
|
2021-08-02 13:37:33 +00:00
|
|
|
if (g_state.useDMA)
|
2020-10-29 11:57:04 +00:00
|
|
|
{
|
2021-08-13 08:20:57 +00:00
|
|
|
for(int i = 0; i < ARRAY_LENGTH(dmaInfo); ++i)
|
2020-10-29 11:57:04 +00:00
|
|
|
if (dmaInfo[i].fd >= 0)
|
|
|
|
close(dmaInfo[i].fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-10-19 04:15:49 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int spiceThread(void * arg)
|
|
|
|
{
|
2021-01-04 01:06:54 +00:00
|
|
|
while(g_state.state != APP_STATE_SHUTDOWN)
|
2021-12-28 11:03:48 +00:00
|
|
|
if (!purespice_process(100))
|
2017-10-19 04:15:49 +00:00
|
|
|
{
|
2021-01-04 01:06:54 +00:00
|
|
|
if (g_state.state != APP_STATE_SHUTDOWN)
|
2017-10-19 07:50:42 +00:00
|
|
|
{
|
2021-01-04 01:06:54 +00:00
|
|
|
g_state.state = APP_STATE_SHUTDOWN;
|
2017-10-19 07:50:42 +00:00
|
|
|
DEBUG_ERROR("failed to process spice messages");
|
|
|
|
}
|
2017-10-19 04:15:49 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-12-25 04:02:35 +00:00
|
|
|
if (g_state.audioDev)
|
|
|
|
{
|
|
|
|
g_state.audioDev->free();
|
|
|
|
g_state.audioDev = NULL;
|
|
|
|
}
|
|
|
|
|
2021-01-04 01:06:54 +00:00
|
|
|
g_state.state = APP_STATE_SHUTDOWN;
|
2017-10-19 04:15:49 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-01-25 16:34:22 +00:00
|
|
|
void intHandler(int sig)
|
2017-12-17 09:09:47 +00:00
|
|
|
{
|
2021-01-24 22:10:52 +00:00
|
|
|
switch(sig)
|
2017-12-17 09:09:47 +00:00
|
|
|
{
|
|
|
|
case SIGINT:
|
2019-04-13 23:15:03 +00:00
|
|
|
case SIGTERM:
|
2021-01-24 22:10:52 +00:00
|
|
|
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);
|
|
|
|
}
|
2017-12-17 09:09:47 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-27 08:38:34 +00:00
|
|
|
static bool tryRenderer(const int index, const LG_RendererParams lgrParams,
|
|
|
|
bool * needsOpenGL)
|
2017-12-17 12:15:18 +00:00
|
|
|
{
|
2021-08-08 04:43:04 +00:00
|
|
|
const LG_RendererOps *r = LG_Renderers[index];
|
2017-12-17 12:15:18 +00:00
|
|
|
|
|
|
|
if (!IS_LG_RENDERER_VALID(r))
|
|
|
|
{
|
|
|
|
DEBUG_ERROR("FIXME: Renderer %d is invalid, skipping", index);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// create the renderer
|
2021-08-08 05:32:01 +00:00
|
|
|
g_state.lgr = NULL;
|
|
|
|
*needsOpenGL = false;
|
|
|
|
if (!r->create(&g_state.lgr, lgrParams, needsOpenGL))
|
|
|
|
{
|
|
|
|
g_state.lgr = NULL;
|
2017-12-17 12:15:18 +00:00
|
|
|
return false;
|
2021-08-08 05:32:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// init the ops member
|
|
|
|
memcpy(&g_state.lgr->ops, r, sizeof(*r));
|
2017-12-17 12:15:18 +00:00
|
|
|
|
|
|
|
// initialize the renderer
|
2021-08-08 05:32:01 +00:00
|
|
|
if (!r->initialize(g_state.lgr))
|
2017-12-17 12:15:18 +00:00
|
|
|
{
|
2021-08-08 05:32:01 +00:00
|
|
|
r->deinitialize(g_state.lgr);
|
|
|
|
g_state.lgr = NULL;
|
2017-12-17 12:15:18 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-08-08 05:43:42 +00:00
|
|
|
DEBUG_INFO("Using Renderer: %s", r->getName());
|
2017-12-17 12:15:18 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-08-05 11:43:14 +00:00
|
|
|
static void reportBadVersion()
|
|
|
|
{
|
|
|
|
DEBUG_BREAK();
|
|
|
|
DEBUG_ERROR("The host application is not compatible with this client");
|
|
|
|
DEBUG_ERROR("This is not a Looking Glass error, do not report this");
|
|
|
|
DEBUG_ERROR("Please install the matching host application for this client");
|
|
|
|
}
|
|
|
|
|
2021-12-24 07:43:20 +00:00
|
|
|
void audioStart(int channels, int sampleRate, PSAudioFormat format,
|
|
|
|
uint32_t time)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* we probe here so that the audiodev is operating in the context of the SPICE
|
|
|
|
* thread/loop to avoid any audio API threading issues
|
|
|
|
*/
|
|
|
|
static int probed = false;
|
|
|
|
if (!probed)
|
|
|
|
{
|
|
|
|
probed = true;
|
|
|
|
|
|
|
|
// search for the best audiodev to use
|
|
|
|
for(int i = 0; i < LG_AUDIODEV_COUNT; ++i)
|
|
|
|
if (LG_AudioDevs[i]->init())
|
|
|
|
{
|
|
|
|
g_state.audioDev = LG_AudioDevs[i];
|
|
|
|
DEBUG_INFO("Using AudioDev: %s", g_state.audioDev->name);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!g_state.audioDev)
|
|
|
|
DEBUG_WARN("Failed to initialize an audio backend");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_state.audioDev)
|
|
|
|
{
|
|
|
|
static int lastChannels = 0;
|
|
|
|
static int lastSampleRate = 0;
|
|
|
|
|
|
|
|
if (g_state.audioStarted)
|
|
|
|
{
|
|
|
|
if (channels != lastChannels || sampleRate != lastSampleRate)
|
|
|
|
g_state.audioDev->stop();
|
|
|
|
else
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
lastChannels = channels;
|
|
|
|
lastSampleRate = sampleRate;
|
|
|
|
g_state.audioStarted = true;
|
|
|
|
|
|
|
|
DEBUG_INFO("%d channels @ %dHz", channels, sampleRate);
|
|
|
|
g_state.audioDev->start(channels, sampleRate);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-25 07:55:09 +00:00
|
|
|
static void audioStop(void)
|
2021-12-24 07:43:20 +00:00
|
|
|
{
|
|
|
|
if (g_state.audioDev)
|
|
|
|
g_state.audioDev->stop();
|
|
|
|
g_state.audioStarted = false;
|
|
|
|
}
|
|
|
|
|
2021-12-25 07:55:09 +00:00
|
|
|
static void audioVolume(int channels, const uint16_t volume[])
|
|
|
|
{
|
|
|
|
if (g_state.audioDev && g_state.audioDev->volume)
|
|
|
|
g_state.audioDev->volume(channels, volume);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void audioMute(bool mute)
|
|
|
|
{
|
|
|
|
if (g_state.audioDev && g_state.audioDev->mute)
|
|
|
|
g_state.audioDev->mute(mute);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void audioData(uint8_t * data, size_t size)
|
2021-12-24 07:43:20 +00:00
|
|
|
{
|
|
|
|
if (g_state.audioDev)
|
|
|
|
g_state.audioDev->play(data, size);
|
|
|
|
}
|
|
|
|
|
2021-01-14 06:05:26 +00:00
|
|
|
static int lg_run(void)
|
2017-10-19 04:15:49 +00:00
|
|
|
{
|
2021-01-25 08:58:36 +00:00
|
|
|
g_cursor.sens = g_params.mouseSens;
|
2021-01-04 01:04:43 +00:00
|
|
|
if (g_cursor.sens < -9) g_cursor.sens = -9;
|
|
|
|
else if (g_cursor.sens > 9) g_cursor.sens = 9;
|
2019-05-23 19:29:38 +00:00
|
|
|
|
2021-07-08 04:05:42 +00:00
|
|
|
/* setup imgui */
|
|
|
|
igCreateContext(NULL);
|
2021-07-23 04:01:10 +00:00
|
|
|
g_state.io = igGetIO();
|
|
|
|
g_state.style = igGetStyle();
|
|
|
|
|
2021-08-30 23:01:30 +00:00
|
|
|
g_state.style->Colors[ImGuiCol_ModalWindowDimBg] = (ImVec4) { 0.0f, 0.0f, 0.0f, 0.4f };
|
|
|
|
|
2021-07-31 11:14:23 +00:00
|
|
|
alloc_sprintf(&g_state.imGuiIni, "%s/imgui.ini", lgConfigDir());
|
|
|
|
g_state.io->IniFilename = g_state.imGuiIni;
|
2021-07-29 21:36:28 +00:00
|
|
|
g_state.io->BackendFlags |= ImGuiBackendFlags_HasMouseCursors;
|
|
|
|
|
2021-07-23 04:01:10 +00:00
|
|
|
g_state.windowScale = 1.0;
|
2021-08-08 06:21:48 +00:00
|
|
|
if (util_initUIFonts())
|
|
|
|
{
|
|
|
|
g_state.fontName = util_getUIFont(g_params.uiFont);
|
|
|
|
DEBUG_INFO("Using font: %s", g_state.fontName);
|
|
|
|
}
|
2021-07-23 04:01:10 +00:00
|
|
|
|
2021-08-04 20:40:06 +00:00
|
|
|
app_initOverlays();
|
2021-07-18 02:31:11 +00:00
|
|
|
|
2021-07-08 15:53:49 +00:00
|
|
|
// initialize metrics ringbuffers
|
2021-08-03 10:34:55 +00:00
|
|
|
g_state.renderTimings = ringbuffer_new(256, sizeof(float));
|
2021-08-03 21:39:44 +00:00
|
|
|
g_state.uploadTimings = ringbuffer_new(256, sizeof(float));
|
2021-08-03 10:34:55 +00:00
|
|
|
g_state.renderDuration = ringbuffer_new(256, sizeof(float));
|
|
|
|
overlayGraph_register("FRAME" , g_state.renderTimings , 0.0f, 50.0f);
|
2021-08-03 21:39:44 +00:00
|
|
|
overlayGraph_register("UPLOAD", g_state.uploadTimings , 0.0f, 50.0f);
|
2021-08-06 15:31:21 +00:00
|
|
|
overlayGraph_register("RENDER", g_state.renderDuration, 0.0f, 10.0f);
|
2021-07-18 02:31:11 +00:00
|
|
|
|
2021-07-30 04:37:57 +00:00
|
|
|
initImGuiKeyMap(g_state.io->KeyMap);
|
|
|
|
|
2021-01-16 09:29:54 +00:00
|
|
|
// search for the best displayserver ops to use
|
|
|
|
for(int i = 0; i < LG_DISPLAYSERVER_COUNT; ++i)
|
2021-01-26 10:46:30 +00:00
|
|
|
if (LG_DisplayServers[i]->probe())
|
2021-01-16 09:29:54 +00:00
|
|
|
{
|
|
|
|
g_state.ds = LG_DisplayServers[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-08-13 23:51:36 +00:00
|
|
|
DEBUG_ASSERT(g_state.ds);
|
2021-01-27 08:20:13 +00:00
|
|
|
ASSERT_LG_DS_VALID(g_state.ds);
|
2021-01-16 09:29:54 +00:00
|
|
|
|
2021-08-01 00:07:05 +00:00
|
|
|
if (g_params.jitRender)
|
|
|
|
{
|
2021-08-01 07:06:03 +00:00
|
|
|
if (g_state.ds->waitFrame)
|
2021-08-01 00:07:05 +00:00
|
|
|
g_state.jitRender = true;
|
|
|
|
else
|
|
|
|
DEBUG_WARN("JIT render not supported on display server backend, disabled");
|
|
|
|
}
|
|
|
|
|
2021-01-16 09:29:54 +00:00
|
|
|
// init the subsystem
|
|
|
|
if (!g_state.ds->earlyInit())
|
2019-02-22 11:40:57 +00:00
|
|
|
{
|
2021-01-16 09:29:54 +00:00
|
|
|
DEBUG_ERROR("Subsystem early init failed");
|
|
|
|
return -1;
|
2018-07-28 20:37:48 +00:00
|
|
|
}
|
|
|
|
|
2021-01-26 10:46:30 +00:00
|
|
|
// override the SIGINIT handler so that we can tell the difference between
|
2017-12-17 09:09:47 +00:00
|
|
|
// SIGINT and the user sending a close event, such as ALT+F4
|
2021-01-25 16:34:22 +00:00
|
|
|
signal(SIGINT , intHandler);
|
|
|
|
signal(SIGTERM, intHandler);
|
2017-12-17 09:09:47 +00:00
|
|
|
|
2019-10-26 01:03:10 +00:00
|
|
|
// try map the shared memory
|
2021-01-04 01:06:54 +00:00
|
|
|
if (!ivshmemOpen(&g_state.shm))
|
2019-10-26 01:03:10 +00:00
|
|
|
{
|
|
|
|
DEBUG_ERROR("Failed to map memory");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// try to connect to the spice server
|
2021-12-24 07:43:20 +00:00
|
|
|
if (g_params.useSpiceInput ||
|
|
|
|
g_params.useSpiceClipboard ||
|
|
|
|
g_params.useSpiceAudio)
|
2019-10-26 01:03:10 +00:00
|
|
|
{
|
2021-06-02 12:09:09 +00:00
|
|
|
if (g_params.useSpiceClipboard)
|
2021-12-28 11:03:48 +00:00
|
|
|
purespice_setClipboardCb(
|
2021-06-02 12:09:09 +00:00
|
|
|
cb_spiceNotice,
|
|
|
|
cb_spiceData,
|
|
|
|
cb_spiceRelease,
|
|
|
|
cb_spiceRequest);
|
2019-10-26 01:03:10 +00:00
|
|
|
|
2021-12-24 07:43:20 +00:00
|
|
|
if (g_params.useSpiceAudio)
|
2021-12-28 11:03:48 +00:00
|
|
|
purespice_setAudioCb(
|
2021-12-24 07:43:20 +00:00
|
|
|
audioStart,
|
2021-12-25 07:55:09 +00:00
|
|
|
audioVolume,
|
|
|
|
audioMute,
|
2021-12-24 07:43:20 +00:00
|
|
|
audioStop,
|
|
|
|
audioData);
|
|
|
|
|
2021-12-29 05:02:22 +00:00
|
|
|
const struct PSConfig config =
|
|
|
|
{
|
|
|
|
.host = g_params.spiceHost,
|
|
|
|
.port = g_params.spicePort,
|
|
|
|
.password = "",
|
|
|
|
.playback = g_params.useSpiceAudio,
|
|
|
|
.log.info = debug_info,
|
|
|
|
.log.warn = debug_warn,
|
|
|
|
.log.error = debug_error
|
|
|
|
};
|
|
|
|
|
|
|
|
if (!purespice_connect(&config))
|
2019-10-26 01:03:10 +00:00
|
|
|
{
|
|
|
|
DEBUG_ERROR("Failed to connect to spice server");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2021-12-28 11:03:48 +00:00
|
|
|
while(g_state.state != APP_STATE_SHUTDOWN && !purespice_ready())
|
|
|
|
if (!purespice_process(1000))
|
2019-10-26 01:03:10 +00:00
|
|
|
{
|
2021-01-04 01:06:54 +00:00
|
|
|
g_state.state = APP_STATE_SHUTDOWN;
|
2019-10-26 01:03:10 +00:00
|
|
|
DEBUG_ERROR("Failed to process spice messages");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2021-12-28 11:03:48 +00:00
|
|
|
purespice_mouseMode(true);
|
2020-01-02 12:59:06 +00:00
|
|
|
if (!lgCreateThread("spiceThread", spiceThread, NULL, &t_spice))
|
2019-10-26 01:03:10 +00:00
|
|
|
{
|
|
|
|
DEBUG_ERROR("spice create thread failed");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// select and init a renderer
|
2021-01-27 08:38:34 +00:00
|
|
|
bool needsOpenGL;
|
2017-12-14 06:42:16 +00:00
|
|
|
LG_RendererParams lgrParams;
|
2021-01-25 08:58:36 +00:00
|
|
|
lgrParams.quickSplash = g_params.quickSplash;
|
2017-12-14 06:42:16 +00:00
|
|
|
|
2021-01-25 08:58:36 +00:00
|
|
|
if (g_params.forceRenderer)
|
2017-12-14 06:42:16 +00:00
|
|
|
{
|
2017-12-17 12:15:18 +00:00
|
|
|
DEBUG_INFO("Trying forced renderer");
|
2021-01-27 08:38:34 +00:00
|
|
|
if (!tryRenderer(g_params.forceRendererIndex, lgrParams, &needsOpenGL))
|
2017-12-14 06:42:16 +00:00
|
|
|
{
|
2017-12-17 12:15:18 +00:00
|
|
|
DEBUG_ERROR("Forced renderer failed to iniailize");
|
|
|
|
return -1;
|
2017-12-14 06:42:16 +00:00
|
|
|
}
|
2017-12-17 12:15:18 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// probe for a a suitable renderer
|
|
|
|
for(unsigned int i = 0; i < LG_RENDERER_COUNT; ++i)
|
2017-12-14 06:42:16 +00:00
|
|
|
{
|
2021-01-27 08:38:34 +00:00
|
|
|
if (tryRenderer(i, lgrParams, &needsOpenGL))
|
2017-12-17 12:15:18 +00:00
|
|
|
break;
|
2017-12-14 06:42:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-04 01:06:54 +00:00
|
|
|
if (!g_state.lgr)
|
2017-12-14 06:42:16 +00:00
|
|
|
{
|
|
|
|
DEBUG_INFO("Unable to find a suitable renderer");
|
|
|
|
return -1;
|
|
|
|
}
|
2017-12-13 23:30:55 +00:00
|
|
|
|
2021-08-02 17:58:30 +00:00
|
|
|
g_state.useDMA =
|
|
|
|
g_params.allowDMA &&
|
|
|
|
ivshmemHasDMA(&g_state.shm);
|
|
|
|
|
2021-01-27 10:27:26 +00:00
|
|
|
// initialize the window dimensions at init for renderers
|
|
|
|
g_state.windowW = g_params.w;
|
|
|
|
g_state.windowH = g_params.h;
|
|
|
|
g_state.windowCX = g_params.w / 2;
|
|
|
|
g_state.windowCY = g_params.h / 2;
|
|
|
|
core_updatePositionInfo();
|
|
|
|
|
2021-01-26 10:46:30 +00:00
|
|
|
const LG_DSInitParams params =
|
2021-01-15 09:30:03 +00:00
|
|
|
{
|
2021-01-26 10:46:30 +00:00
|
|
|
.title = g_params.windowTitle,
|
|
|
|
.x = g_params.x,
|
|
|
|
.y = g_params.y,
|
|
|
|
.w = g_params.w,
|
|
|
|
.h = g_params.h,
|
|
|
|
.center = g_params.center,
|
|
|
|
.fullscreen = g_params.fullscreen,
|
|
|
|
.resizable = g_params.allowResize,
|
|
|
|
.borderless = g_params.borderless,
|
|
|
|
.maximize = g_params.maximize,
|
2021-08-03 20:05:42 +00:00
|
|
|
.opengl = needsOpenGL,
|
|
|
|
.jitRender = g_params.jitRender
|
2021-01-26 10:46:30 +00:00
|
|
|
};
|
2020-11-27 23:19:17 +00:00
|
|
|
|
2021-01-27 08:27:19 +00:00
|
|
|
g_state.dsInitialized = g_state.ds->init(params);
|
2021-01-27 08:40:05 +00:00
|
|
|
if (!g_state.dsInitialized)
|
2021-01-27 08:27:19 +00:00
|
|
|
{
|
|
|
|
DEBUG_ERROR("Failed to initialize the displayserver backend");
|
|
|
|
return -1;
|
|
|
|
}
|
2018-10-03 14:09:00 +00:00
|
|
|
|
2021-01-25 08:58:36 +00:00
|
|
|
if (g_params.noScreensaver)
|
2021-01-18 21:08:38 +00:00
|
|
|
g_state.ds->inhibitIdle();
|
|
|
|
|
2019-02-20 18:58:07 +00:00
|
|
|
// ensure renderer viewport is aware of the current window size
|
2021-01-25 08:58:36 +00:00
|
|
|
core_updatePositionInfo();
|
2019-02-20 18:58:07 +00:00
|
|
|
|
2021-01-25 08:58:36 +00:00
|
|
|
if (g_params.fpsMin <= 0)
|
2020-08-20 05:50:33 +00:00
|
|
|
{
|
2020-10-26 07:09:45 +00:00
|
|
|
// default 30 fps
|
2021-01-04 01:06:54 +00:00
|
|
|
g_state.frameTime = 1000000000ULL / 30ULL;
|
2020-08-20 05:50:33 +00:00
|
|
|
}
|
2019-08-30 01:36:28 +00:00
|
|
|
else
|
2019-07-09 01:13:24 +00:00
|
|
|
{
|
2021-01-25 08:58:36 +00:00
|
|
|
DEBUG_INFO("Using the FPS minimum from args: %d", g_params.fpsMin);
|
|
|
|
g_state.frameTime = 1000000000ULL / (unsigned long long)g_params.fpsMin;
|
2019-07-09 01:13:24 +00:00
|
|
|
}
|
2019-08-30 01:36:28 +00:00
|
|
|
|
2021-07-31 06:19:55 +00:00
|
|
|
// when the overlay is shown we should run at a minimum of 60 fps for
|
|
|
|
// interactivity.
|
|
|
|
g_state.overlayFrameTime = min(g_state.frameTime, 1000000000ULL / 60ULL);
|
|
|
|
|
2021-01-25 16:34:22 +00:00
|
|
|
keybind_register();
|
2019-03-28 15:06:37 +00:00
|
|
|
|
2019-12-09 16:30:04 +00:00
|
|
|
// setup the startup condition
|
2020-01-02 13:08:43 +00:00
|
|
|
if (!(e_startup = lgCreateEvent(false, 0)))
|
|
|
|
{
|
|
|
|
DEBUG_ERROR("failed to create the startup event");
|
|
|
|
return -1;
|
|
|
|
}
|
2019-12-09 16:30:04 +00:00
|
|
|
|
2020-05-21 03:41:59 +00:00
|
|
|
// setup the new frame event
|
2021-08-01 07:38:09 +00:00
|
|
|
if (!(g_state.frameEvent = lgCreateEvent(!g_state.jitRender, 0)))
|
2020-05-21 03:41:59 +00:00
|
|
|
{
|
|
|
|
DEBUG_ERROR("failed to create the frame event");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2021-08-01 00:07:05 +00:00
|
|
|
if (g_state.jitRender)
|
|
|
|
DEBUG_INFO("Using JIT render mode");
|
|
|
|
|
2021-01-25 04:25:52 +00:00
|
|
|
lgInit();
|
|
|
|
|
2019-10-26 01:03:10 +00:00
|
|
|
// start the renderThread so we don't just display junk
|
2020-01-02 12:59:06 +00:00
|
|
|
if (!lgCreateThread("renderThread", renderThread, NULL, &t_render))
|
2017-10-19 04:15:49 +00:00
|
|
|
{
|
2019-10-26 01:03:10 +00:00
|
|
|
DEBUG_ERROR("render create thread failed");
|
|
|
|
return -1;
|
|
|
|
}
|
2017-11-25 06:51:34 +00:00
|
|
|
|
2020-05-17 01:25:27 +00:00
|
|
|
// wait for startup to complete so that any error messages below are output at
|
|
|
|
// the end of the output
|
|
|
|
lgWaitEvent(e_startup, TIMEOUT_INFINITE);
|
|
|
|
|
2021-01-15 09:30:03 +00:00
|
|
|
g_state.ds->startup();
|
|
|
|
g_state.cbAvailable = g_state.ds->cbInit && g_state.ds->cbInit();
|
|
|
|
if (g_state.cbAvailable)
|
2021-01-12 01:55:42 +00:00
|
|
|
g_state.cbRequestList = ll_new();
|
|
|
|
|
2020-01-09 09:32:42 +00:00
|
|
|
LGMP_STATUS status;
|
2020-05-20 23:28:41 +00:00
|
|
|
|
2021-01-04 01:06:54 +00:00
|
|
|
while(g_state.state == APP_STATE_RUNNING)
|
2020-05-20 23:28:41 +00:00
|
|
|
{
|
2021-01-04 01:06:54 +00:00
|
|
|
if ((status = lgmpClientInit(g_state.shm.mem, g_state.shm.size, &g_state.lgmp)) == LGMP_OK)
|
2020-05-20 23:28:41 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
DEBUG_ERROR("lgmpClientInit Failed: %s", lgmpStatusString(status));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-05-20 23:32:08 +00:00
|
|
|
/* this short timeout is to allow the LGMP host to update the timestamp before
|
|
|
|
* we start checking for a valid session */
|
2021-01-26 10:46:30 +00:00
|
|
|
g_state.ds->wait(200);
|
2020-05-20 23:32:08 +00:00
|
|
|
|
2021-01-25 08:58:36 +00:00
|
|
|
if (g_params.captureOnStart)
|
|
|
|
core_setGrab(true);
|
2021-01-08 12:12:15 +00:00
|
|
|
|
2020-05-17 01:54:07 +00:00
|
|
|
uint32_t udataSize;
|
|
|
|
KVMFR *udata;
|
2020-08-11 04:45:43 +00:00
|
|
|
int waitCount = 0;
|
2020-05-17 01:13:08 +00:00
|
|
|
|
2020-08-11 04:45:43 +00:00
|
|
|
restart:
|
2021-01-04 01:06:54 +00:00
|
|
|
while(g_state.state == APP_STATE_RUNNING)
|
2020-05-17 01:54:07 +00:00
|
|
|
{
|
2021-01-04 01:06:54 +00:00
|
|
|
if ((status = lgmpClientSessionInit(g_state.lgmp, &udataSize, (uint8_t **)&udata)) == LGMP_OK)
|
2020-01-09 09:32:42 +00:00
|
|
|
break;
|
2018-05-28 05:30:04 +00:00
|
|
|
|
2021-08-05 11:43:14 +00:00
|
|
|
if (status == LGMP_ERR_INVALID_VERSION)
|
|
|
|
{
|
|
|
|
reportBadVersion();
|
|
|
|
DEBUG_INFO("Waiting for you to upgrade the host application");
|
|
|
|
while (g_state.state == APP_STATE_RUNNING &&
|
|
|
|
lgmpClientSessionInit(g_state.lgmp, &udataSize, (uint8_t **)&udata) != LGMP_OK)
|
|
|
|
g_state.ds->wait(1000);
|
|
|
|
|
|
|
|
if (g_state.state != APP_STATE_RUNNING)
|
|
|
|
return -1;
|
|
|
|
|
2021-08-05 11:45:50 +00:00
|
|
|
break;
|
2021-08-05 11:43:14 +00:00
|
|
|
}
|
|
|
|
|
2020-05-20 23:28:41 +00:00
|
|
|
if (status != LGMP_ERR_INVALID_SESSION && status != LGMP_ERR_INVALID_MAGIC)
|
2020-01-09 09:32:42 +00:00
|
|
|
{
|
2020-05-20 23:28:41 +00:00
|
|
|
DEBUG_ERROR("lgmpClientSessionInit Failed: %s", lgmpStatusString(status));
|
|
|
|
return -1;
|
|
|
|
}
|
2020-05-17 01:54:07 +00:00
|
|
|
|
2020-05-20 23:28:41 +00:00
|
|
|
if (waitCount++ == 0)
|
|
|
|
{
|
|
|
|
DEBUG_BREAK();
|
|
|
|
DEBUG_INFO("The host application seems to not be running");
|
|
|
|
DEBUG_INFO("Waiting for the host application to start...");
|
|
|
|
}
|
2020-05-17 01:54:07 +00:00
|
|
|
|
2020-05-20 23:28:41 +00:00
|
|
|
if (waitCount == 30)
|
|
|
|
{
|
|
|
|
DEBUG_BREAK();
|
|
|
|
DEBUG_INFO("Please check the host application is running and is the correct version");
|
2021-02-22 21:41:38 +00:00
|
|
|
DEBUG_INFO("Check the host log in your guest at %%ProgramData%%\\Looking Glass (host)\\looking-glass-host.txt");
|
2020-05-20 23:28:41 +00:00
|
|
|
DEBUG_INFO("Continuing to wait...");
|
2020-05-17 01:13:08 +00:00
|
|
|
}
|
|
|
|
|
2021-01-26 10:46:30 +00:00
|
|
|
g_state.ds->wait(1000);
|
2019-10-26 01:03:10 +00:00
|
|
|
}
|
2017-12-13 23:08:47 +00:00
|
|
|
|
2021-01-04 01:06:54 +00:00
|
|
|
if (g_state.state != APP_STATE_RUNNING)
|
2019-10-26 01:03:10 +00:00
|
|
|
return -1;
|
2020-01-09 09:32:42 +00:00
|
|
|
|
2020-08-11 04:52:22 +00:00
|
|
|
// dont show warnings again after the first startup
|
|
|
|
waitCount = 100;
|
|
|
|
|
2020-05-29 04:14:31 +00:00
|
|
|
const bool magicMatches = memcmp(udata->magic, KVMFR_MAGIC, sizeof(udata->magic)) == 0;
|
|
|
|
if (udataSize != sizeof(KVMFR) || !magicMatches || udata->version != KVMFR_VERSION)
|
2020-05-17 01:54:07 +00:00
|
|
|
{
|
2021-08-05 11:43:14 +00:00
|
|
|
reportBadVersion();
|
2020-05-29 04:14:31 +00:00
|
|
|
if (magicMatches)
|
2020-05-29 04:24:06 +00:00
|
|
|
{
|
2020-05-29 04:14:31 +00:00
|
|
|
DEBUG_ERROR("Expected KVMFR version %d, got %d", KVMFR_VERSION, udata->version);
|
2021-01-04 22:18:40 +00:00
|
|
|
DEBUG_ERROR("Client version: %s", BUILD_VERSION);
|
2020-05-29 04:24:06 +00:00
|
|
|
if (udata->version >= 2)
|
2021-01-04 22:18:40 +00:00
|
|
|
DEBUG_ERROR(" Host version: %s", udata->hostver);
|
2020-05-29 04:24:06 +00:00
|
|
|
}
|
2020-05-29 04:14:31 +00:00
|
|
|
else
|
|
|
|
DEBUG_ERROR("Invalid KVMFR magic");
|
|
|
|
|
2020-05-17 01:54:07 +00:00
|
|
|
DEBUG_BREAK();
|
2021-01-04 22:13:54 +00:00
|
|
|
|
|
|
|
if (magicMatches)
|
|
|
|
{
|
|
|
|
DEBUG_INFO("Waiting for you to upgrade the host application");
|
|
|
|
while (g_state.state == APP_STATE_RUNNING && udata->version != KVMFR_VERSION)
|
2021-01-26 10:46:30 +00:00
|
|
|
g_state.ds->wait(1000);
|
2021-01-04 22:13:54 +00:00
|
|
|
|
|
|
|
if (g_state.state != APP_STATE_RUNNING)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
goto restart;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return -1;
|
2020-05-17 01:54:07 +00:00
|
|
|
}
|
|
|
|
|
2020-05-29 04:14:31 +00:00
|
|
|
DEBUG_INFO("Host ready, reported version: %s", udata->hostver);
|
|
|
|
DEBUG_INFO("Starting session");
|
2017-12-19 13:53:45 +00:00
|
|
|
|
2021-08-05 12:19:35 +00:00
|
|
|
g_state.kvmfrFeatures = udata->features;
|
|
|
|
|
2021-10-06 09:04:06 +00:00
|
|
|
if (!core_startCursorThread() || !core_startFrameThread())
|
2019-10-26 01:03:10 +00:00
|
|
|
return -1;
|
2017-12-19 13:53:45 +00:00
|
|
|
|
2021-01-04 01:06:54 +00:00
|
|
|
while(g_state.state == APP_STATE_RUNNING)
|
2019-10-26 01:03:10 +00:00
|
|
|
{
|
2021-01-04 01:06:54 +00:00
|
|
|
if (!lgmpClientSessionValid(g_state.lgmp))
|
2020-01-09 09:32:42 +00:00
|
|
|
{
|
2021-01-04 01:06:54 +00:00
|
|
|
g_state.state = APP_STATE_RESTART;
|
2020-04-12 03:48:59 +00:00
|
|
|
break;
|
2020-01-09 09:32:42 +00:00
|
|
|
}
|
2021-01-26 10:46:30 +00:00
|
|
|
g_state.ds->wait(100);
|
2017-10-19 04:15:49 +00:00
|
|
|
}
|
|
|
|
|
2021-01-04 01:06:54 +00:00
|
|
|
if (g_state.state == APP_STATE_RESTART)
|
2020-08-11 04:30:44 +00:00
|
|
|
{
|
|
|
|
lgSignalEvent(e_startup);
|
2021-07-25 05:29:29 +00:00
|
|
|
lgSignalEvent(g_state.frameEvent);
|
2021-01-25 16:34:22 +00:00
|
|
|
|
|
|
|
core_stopFrameThread();
|
2021-10-06 09:04:06 +00:00
|
|
|
core_stopCursorThread();
|
2020-08-11 04:45:43 +00:00
|
|
|
|
2020-12-03 13:32:28 +00:00
|
|
|
lgInit();
|
|
|
|
|
2021-08-08 05:43:42 +00:00
|
|
|
RENDERER(onRestart);
|
2020-08-11 04:30:44 +00:00
|
|
|
|
|
|
|
DEBUG_INFO("Waiting for the host to restart...");
|
|
|
|
goto restart;
|
|
|
|
}
|
|
|
|
|
2019-10-26 01:03:10 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-01-14 06:05:26 +00:00
|
|
|
static void lg_shutdown(void)
|
2019-10-26 01:03:10 +00:00
|
|
|
{
|
2021-01-04 01:06:54 +00:00
|
|
|
g_state.state = APP_STATE_SHUTDOWN;
|
2018-01-29 06:27:12 +00:00
|
|
|
if (t_render)
|
2020-01-27 01:25:47 +00:00
|
|
|
{
|
2021-08-07 23:01:48 +00:00
|
|
|
if (g_state.jitRender && g_state.ds->stopWaitFrame)
|
2021-08-01 09:45:21 +00:00
|
|
|
g_state.ds->stopWaitFrame();
|
2020-01-27 01:25:47 +00:00
|
|
|
lgSignalEvent(e_startup);
|
2021-07-25 05:29:29 +00:00
|
|
|
lgSignalEvent(g_state.frameEvent);
|
2020-01-02 12:59:06 +00:00
|
|
|
lgJoinThread(t_render, NULL);
|
2020-01-27 01:25:47 +00:00
|
|
|
}
|
2018-01-29 06:27:12 +00:00
|
|
|
|
2021-01-04 01:06:54 +00:00
|
|
|
lgmpClientFree(&g_state.lgmp);
|
2020-01-09 09:32:42 +00:00
|
|
|
|
2021-07-25 05:29:29 +00:00
|
|
|
if (g_state.frameEvent)
|
2020-05-21 03:41:59 +00:00
|
|
|
{
|
2021-07-25 05:29:29 +00:00
|
|
|
lgFreeEvent(g_state.frameEvent);
|
|
|
|
g_state.frameEvent = NULL;
|
2020-05-21 03:41:59 +00:00
|
|
|
}
|
|
|
|
|
2020-01-02 13:08:43 +00:00
|
|
|
if (e_startup)
|
2019-12-09 16:30:04 +00:00
|
|
|
{
|
2020-01-02 13:08:43 +00:00
|
|
|
lgFreeEvent(e_startup);
|
|
|
|
e_startup = NULL;
|
2019-12-09 16:30:04 +00:00
|
|
|
}
|
|
|
|
|
2018-01-29 06:27:12 +00:00
|
|
|
// if spice is still connected send key up events for any pressed keys
|
2021-12-28 11:03:48 +00:00
|
|
|
if (g_params.useSpiceInput && purespice_ready())
|
2018-01-29 06:27:12 +00:00
|
|
|
{
|
2021-01-25 15:51:03 +00:00
|
|
|
for(int scancode = 0; scancode < KEY_MAX; ++scancode)
|
2021-01-18 02:54:21 +00:00
|
|
|
if (g_state.keyDown[scancode])
|
2018-01-29 06:27:12 +00:00
|
|
|
{
|
2021-01-18 02:54:21 +00:00
|
|
|
g_state.keyDown[scancode] = false;
|
2021-12-28 11:03:48 +00:00
|
|
|
purespice_keyUp(scancode);
|
2018-01-29 06:27:12 +00:00
|
|
|
}
|
|
|
|
|
2021-12-28 11:03:48 +00:00
|
|
|
purespice_disconnect();
|
2018-01-29 06:27:12 +00:00
|
|
|
if (t_spice)
|
2020-01-02 12:59:06 +00:00
|
|
|
lgJoinThread(t_spice, NULL);
|
2018-01-29 06:27:12 +00:00
|
|
|
}
|
2017-10-19 04:15:49 +00:00
|
|
|
|
2021-01-15 09:30:03 +00:00
|
|
|
if (g_state.ds)
|
|
|
|
g_state.ds->shutdown();
|
2019-02-22 11:38:52 +00:00
|
|
|
|
2021-01-15 09:30:03 +00:00
|
|
|
if (g_state.cbRequestList)
|
|
|
|
{
|
2021-01-04 01:06:54 +00:00
|
|
|
ll_free(g_state.cbRequestList);
|
2021-01-15 09:30:03 +00:00
|
|
|
g_state.cbRequestList = NULL;
|
2019-02-24 18:43:18 +00:00
|
|
|
}
|
|
|
|
|
2021-01-25 15:33:28 +00:00
|
|
|
app_releaseAllKeybinds();
|
2021-01-27 08:27:19 +00:00
|
|
|
|
|
|
|
if (g_state.dsInitialized)
|
|
|
|
g_state.ds->free();
|
|
|
|
|
2021-09-11 01:17:51 +00:00
|
|
|
if (g_state.overlays)
|
|
|
|
{
|
|
|
|
app_freeOverlays();
|
|
|
|
ll_free(g_state.overlays);
|
|
|
|
g_state.overlays = NULL;
|
|
|
|
}
|
|
|
|
|
2021-01-26 10:46:30 +00:00
|
|
|
ivshmemClose(&g_state.shm);
|
2021-07-08 04:05:42 +00:00
|
|
|
|
2021-07-08 15:53:49 +00:00
|
|
|
// free metrics ringbuffers
|
|
|
|
ringbuffer_free(&g_state.renderTimings);
|
2021-08-03 21:39:44 +00:00
|
|
|
ringbuffer_free(&g_state.uploadTimings);
|
2021-08-03 10:34:55 +00:00
|
|
|
ringbuffer_free(&g_state.renderDuration);
|
2021-07-08 15:53:49 +00:00
|
|
|
|
2021-07-23 04:01:10 +00:00
|
|
|
free(g_state.fontName);
|
2021-07-08 04:05:42 +00:00
|
|
|
igDestroyContext(NULL);
|
2021-07-31 14:55:36 +00:00
|
|
|
free(g_state.imGuiIni);
|
2017-11-25 07:20:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char * argv[])
|
|
|
|
{
|
2021-07-07 13:05:46 +00:00
|
|
|
// initialize for DEBUG_* macros
|
|
|
|
debug_init();
|
|
|
|
|
2020-05-29 04:18:02 +00:00
|
|
|
if (getuid() == 0)
|
|
|
|
{
|
|
|
|
DEBUG_ERROR("Do not run looking glass as root!");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2021-07-30 11:15:41 +00:00
|
|
|
if (getuid() != geteuid())
|
|
|
|
{
|
|
|
|
DEBUG_ERROR("Do not run looking glass as setuid!");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-10-08 15:17:20 +00:00
|
|
|
DEBUG_INFO("Looking Glass (%s)", BUILD_VERSION);
|
2019-10-26 01:03:10 +00:00
|
|
|
DEBUG_INFO("Locking Method: " LG_LOCK_MODE);
|
2021-08-31 00:12:35 +00:00
|
|
|
lgDebugCPU();
|
2019-10-26 01:03:10 +00:00
|
|
|
|
2019-05-26 15:20:05 +00:00
|
|
|
if (!installCrashHandler("/proc/self/exe"))
|
2019-04-11 06:41:52 +00:00
|
|
|
DEBUG_WARN("Failed to install the crash handler");
|
|
|
|
|
2021-07-31 11:05:48 +00:00
|
|
|
lgPathsInit("looking-glass");
|
2019-05-21 05:03:59 +00:00
|
|
|
config_init();
|
2020-01-03 04:17:14 +00:00
|
|
|
ivshmemOptionsInit();
|
2021-01-26 10:46:30 +00:00
|
|
|
egl_dynProcsInit();
|
2021-09-30 10:14:25 +00:00
|
|
|
gl_dynProcsInit();
|
2019-05-21 05:03:59 +00:00
|
|
|
|
2021-08-04 20:40:06 +00:00
|
|
|
g_state.overlays = ll_new();
|
|
|
|
app_registerOverlay(&LGOverlayConfig, NULL);
|
|
|
|
app_registerOverlay(&LGOverlayAlert , NULL);
|
|
|
|
app_registerOverlay(&LGOverlayFPS , NULL);
|
|
|
|
app_registerOverlay(&LGOverlayGraphs, NULL);
|
|
|
|
app_registerOverlay(&LGOverlayHelp , NULL);
|
|
|
|
|
2021-08-04 21:01:28 +00:00
|
|
|
// early renderer setup for option registration
|
|
|
|
for(unsigned int i = 0; i < LG_RENDERER_COUNT; ++i)
|
|
|
|
LG_Renderers[i]->setup();
|
|
|
|
|
|
|
|
for(unsigned int i = 0; i < LG_DISPLAYSERVER_COUNT; ++i)
|
|
|
|
LG_DisplayServers[i]->setup();
|
|
|
|
|
2019-03-30 04:52:00 +00:00
|
|
|
if (!config_load(argc, argv))
|
2017-12-19 00:58:38 +00:00
|
|
|
return -1;
|
2019-03-30 04:52:00 +00:00
|
|
|
|
2019-10-26 01:03:10 +00:00
|
|
|
const int ret = lg_run();
|
|
|
|
lg_shutdown();
|
2017-12-28 08:55:13 +00:00
|
|
|
|
2019-03-30 05:00:47 +00:00
|
|
|
config_free();
|
2019-10-26 01:03:10 +00:00
|
|
|
|
2021-08-08 06:21:48 +00:00
|
|
|
util_freeUIFonts();
|
2021-01-25 07:36:51 +00:00
|
|
|
cleanupCrashHandler();
|
|
|
|
return ret;
|
2020-01-17 03:35:08 +00:00
|
|
|
}
|