2017-10-31 08:07:16 +00:00
|
|
|
/*
|
2017-12-03 15:22:49 +00:00
|
|
|
Looking Glass - KVM FrameRelay (KVMFR) Client
|
2021-01-25 16:34:22 +00:00
|
|
|
Copyright (C) 2017-2021 Geoffrey McRae <geoff@hostfission.com>
|
2017-12-11 17:30:47 +00:00
|
|
|
https://looking-glass.hostfission.com
|
2017-10-31 08:07:16 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
|
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>
|
|
|
|
#include <assert.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
|
|
|
|
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"
|
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"
|
2019-02-24 18:43:18 +00:00
|
|
|
#include "ll.h"
|
2021-01-26 10:46:30 +00:00
|
|
|
#include "egl_dynprocs.h"
|
2020-01-03 04:53:44 +00:00
|
|
|
|
2019-03-30 01:26:06 +00:00
|
|
|
// forwards
|
|
|
|
static int cursorThread(void * unused);
|
|
|
|
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;
|
2020-05-21 03:41:59 +00:00
|
|
|
static LGEvent *e_frame = NULL;
|
2020-01-02 13:08:43 +00:00
|
|
|
static LGThread *t_spice = NULL;
|
|
|
|
static LGThread *t_render = NULL;
|
|
|
|
static LGThread *t_cursor = NULL;
|
2019-10-26 01:03:10 +00:00
|
|
|
|
2021-01-04 01:06:54 +00:00
|
|
|
struct AppState g_state;
|
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-01-25 08:58:36 +00:00
|
|
|
if (!app_inputEnabled() && g_params.hideMouse)
|
2021-01-26 10:46:30 +00:00
|
|
|
g_state.ds->showPointer(false);
|
2021-01-21 04:39:15 +00:00
|
|
|
else
|
2021-01-26 10:46:30 +00:00
|
|
|
g_state.ds->showPointer(true);
|
2020-12-03 13:32:28 +00:00
|
|
|
}
|
|
|
|
|
2019-03-28 10:23:24 +00:00
|
|
|
static int renderThread(void * unused)
|
2017-10-19 04:15:49 +00:00
|
|
|
{
|
2021-01-26 10:46:30 +00:00
|
|
|
if (!g_state.lgr->render_startup(g_state.lgrData))
|
2019-05-27 08:38:36 +00:00
|
|
|
{
|
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;
|
|
|
|
}
|
|
|
|
|
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-01-25 08:58:36 +00:00
|
|
|
if (g_params.fpsMin != 0)
|
2020-05-22 08:39:19 +00:00
|
|
|
{
|
2020-10-26 07:09:45 +00:00
|
|
|
lgWaitEventAbs(e_frame, &time);
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &time);
|
2021-01-04 01:06:54 +00:00
|
|
|
tsAdd(&time, g_state.frameTime);
|
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-01-04 01:06:54 +00:00
|
|
|
if (g_state.lgr)
|
2021-01-18 15:44:56 +00:00
|
|
|
g_state.lgr->on_resize(g_state.lgrData, g_state.windowW, g_state.windowH,
|
2021-01-25 08:58:36 +00:00
|
|
|
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-01-26 10:46:30 +00:00
|
|
|
if (!g_state.lgr->render(g_state.lgrData, g_params.winRotate))
|
2018-05-28 05:30:04 +00:00
|
|
|
break;
|
2018-05-23 23:01:53 +00:00
|
|
|
|
2021-01-25 08:58:36 +00:00
|
|
|
if (g_params.showFPS)
|
2018-11-19 18:26:51 +00:00
|
|
|
{
|
2018-12-12 14:28:00 +00:00
|
|
|
const uint64_t t = nanotime();
|
2021-01-04 01:06:54 +00:00
|
|
|
g_state.renderTime += t - g_state.lastFrameTime;
|
|
|
|
g_state.lastFrameTime = t;
|
|
|
|
++g_state.renderCount;
|
2018-12-12 14:28:00 +00:00
|
|
|
|
2021-01-04 01:06:54 +00:00
|
|
|
if (g_state.renderTime > 1e9)
|
2018-12-12 14:28:00 +00:00
|
|
|
{
|
2021-01-04 01:06:54 +00:00
|
|
|
const float avgUPS = 1000.0f / (((float)g_state.renderTime /
|
|
|
|
atomic_exchange_explicit(&g_state.frameCount, 0, memory_order_acquire)) /
|
2020-10-26 07:09:45 +00:00
|
|
|
1e6f);
|
|
|
|
|
2021-01-04 01:06:54 +00:00
|
|
|
const float avgFPS = 1000.0f / (((float)g_state.renderTime /
|
|
|
|
g_state.renderCount) /
|
2020-10-26 07:09:45 +00:00
|
|
|
1e6f);
|
|
|
|
|
2021-01-04 01:06:54 +00:00
|
|
|
g_state.lgr->update_fps(g_state.lgrData, avgUPS, avgFPS);
|
2018-12-12 14:21:56 +00:00
|
|
|
|
2021-01-04 01:06:54 +00:00
|
|
|
g_state.renderTime = 0;
|
|
|
|
g_state.renderCount = 0;
|
2018-12-12 14:28:00 +00:00
|
|
|
}
|
2018-11-19 18:26:51 +00:00
|
|
|
}
|
|
|
|
|
2021-01-04 01:06:54 +00:00
|
|
|
if (!g_state.resizeDone && g_state.resizeTimeout < microtime())
|
2020-01-03 04:53:44 +00:00
|
|
|
{
|
2021-01-26 10:46:30 +00:00
|
|
|
g_state.ds->setWindowSize(
|
2021-01-04 01:06:54 +00:00
|
|
|
g_state.dstRect.w,
|
|
|
|
g_state.dstRect.h
|
2020-01-03 04:53:44 +00:00
|
|
|
);
|
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
|
|
|
|
|
|
|
if (t_cursor)
|
2020-01-02 12:59:06 +00:00
|
|
|
lgJoinThread(t_cursor, NULL);
|
2019-08-30 01:50:43 +00:00
|
|
|
|
2021-01-25 16:34:22 +00:00
|
|
|
core_stopFrameThread();
|
2019-08-30 01:36:28 +00:00
|
|
|
|
2021-01-04 01:06:54 +00:00
|
|
|
g_state.lgr->deinitialize(g_state.lgrData);
|
|
|
|
g_state.lgr = NULL;
|
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
|
|
|
|
2019-03-28 10:23:24 +00:00
|
|
|
static int cursorThread(void * unused)
|
2017-12-19 13:53:45 +00:00
|
|
|
{
|
2020-01-09 09:32:42 +00:00
|
|
|
LGMP_STATUS status;
|
2020-01-10 00:00:46 +00:00
|
|
|
PLGMPClientQueue queue;
|
2017-12-15 08:13:36 +00:00
|
|
|
LG_RendererCursor cursorType = LG_CURSOR_COLOR;
|
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-01-04 01:06:54 +00:00
|
|
|
status = lgmpClientSubscribe(g_state.lgmp, LGMP_Q_POINTER, &queue);
|
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-01-04 01:06:54 +00:00
|
|
|
while(g_state.state == APP_STATE_RUNNING)
|
2020-01-09 09:32:42 +00:00
|
|
|
{
|
|
|
|
LGMPMessage msg;
|
|
|
|
if ((status = lgmpClientProcess(queue, &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-01-04 01:06:54 +00:00
|
|
|
g_state.lgr->on_mouse_event
|
2020-01-10 19:03:16 +00:00
|
|
|
(
|
2021-01-04 01:06:54 +00:00
|
|
|
g_state.lgrData,
|
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,
|
|
|
|
g_cursor.guest.y
|
2020-01-10 19:03:16 +00:00
|
|
|
);
|
2020-08-20 06:05:55 +00:00
|
|
|
|
|
|
|
lgSignalEvent(e_frame);
|
2020-01-10 19:03:16 +00:00
|
|
|
}
|
|
|
|
|
2021-01-07 21:27:12 +00:00
|
|
|
const struct timespec req =
|
|
|
|
{
|
|
|
|
.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)
|
|
|
|
if (errno != -EINTR)
|
|
|
|
{
|
|
|
|
DEBUG_ERROR("nanosleep failed");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2020-01-09 09:32:42 +00:00
|
|
|
KVMFRCursor * cursor = (KVMFRCursor *)msg.mem;
|
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");
|
|
|
|
lgmpClientMessageDone(queue);
|
|
|
|
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-01-04 01:06:54 +00:00
|
|
|
if (!g_state.lgr->on_mouse_shape(
|
|
|
|
g_state.lgrData,
|
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
|
|
|
lgmpClientMessageDone(queue);
|
|
|
|
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-15 09:30:03 +00:00
|
|
|
if (valid != true && app_inputEnabled())
|
2021-01-25 08:58:36 +00:00
|
|
|
core_alignToGuest();
|
2020-08-20 04:52:24 +00:00
|
|
|
}
|
|
|
|
|
2020-01-09 09:32:42 +00:00
|
|
|
lgmpClientMessageDone(queue);
|
2021-01-04 01:04:43 +00:00
|
|
|
g_cursor.redraw = false;
|
2020-01-26 06:30:16 +00:00
|
|
|
|
2021-01-04 01:06:54 +00:00
|
|
|
g_state.lgr->on_mouse_event
|
2020-01-26 15:11:21 +00:00
|
|
|
(
|
2021-01-04 01:06:54 +00:00
|
|
|
g_state.lgrData,
|
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,
|
|
|
|
g_cursor.guest.y
|
2020-01-26 15:11:21 +00:00
|
|
|
);
|
2020-08-20 05:50:33 +00:00
|
|
|
|
2021-01-25 08:58:36 +00:00
|
|
|
if (g_params.mouseRedraw && g_cursor.guest.visible)
|
2020-08-20 05:50:33 +00:00
|
|
|
lgSignalEvent(e_frame);
|
2017-12-19 13:53:45 +00:00
|
|
|
}
|
2017-12-15 07:47:44 +00:00
|
|
|
|
2020-01-09 09:32:42 +00:00
|
|
|
lgmpClientUnsubscribe(&queue);
|
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
|
|
|
|
2020-10-12 07:52:37 +00:00
|
|
|
uint32_t formatVer = 0;
|
2021-01-21 06:25:53 +00:00
|
|
|
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};
|
2020-10-29 15:32:25 +00:00
|
|
|
const bool useDMA =
|
2021-01-25 08:58:36 +00:00
|
|
|
g_params.allowDMA &&
|
2021-01-04 01:06:54 +00:00
|
|
|
ivshmemHasDMA(&g_state.shm) &&
|
|
|
|
g_state.lgr->supports &&
|
|
|
|
g_state.lgr->supports(g_state.lgrData, LG_SUPPORTS_DMABUF);
|
2020-10-29 11:57:04 +00:00
|
|
|
|
|
|
|
if (useDMA)
|
|
|
|
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-01-07 21:27:12 +00:00
|
|
|
const struct timespec req =
|
|
|
|
{
|
|
|
|
.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)
|
|
|
|
if (errno != -EINTR)
|
|
|
|
{
|
|
|
|
DEBUG_ERROR("nanosleep failed");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
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;
|
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-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-01-04 01:06:54 +00:00
|
|
|
if (!g_state.lgr->on_frame_format(g_state.lgrData, lgrFormat, useDMA))
|
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;
|
2020-10-12 08:43:29 +00:00
|
|
|
break;
|
|
|
|
}
|
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-04 22:48:30 +00:00
|
|
|
g_cursor.guest.dpiScale = frame->mouseScalePercent;
|
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-01-21 06:25:53 +00:00
|
|
|
if (useDMA)
|
2020-10-29 11:57:04 +00:00
|
|
|
{
|
|
|
|
/* find the existing dma buffer if it exists */
|
|
|
|
for(int i = 0; i < sizeof(dmaInfo) / sizeof(struct DMAFrameInfo); ++i)
|
|
|
|
{
|
|
|
|
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)
|
|
|
|
for(int i = 0; i < sizeof(dmaInfo) / sizeof(struct DMAFrameInfo); ++i)
|
|
|
|
{
|
|
|
|
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-01-04 01:06:54 +00:00
|
|
|
if (!g_state.lgr->on_frame(g_state.lgrData, fb, useDMA ? dma->fd : -1))
|
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-01-04 01:06:54 +00:00
|
|
|
atomic_fetch_add_explicit(&g_state.frameCount, 1, memory_order_relaxed);
|
2020-10-26 07:09:45 +00:00
|
|
|
lgSignalEvent(e_frame);
|
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-01-04 01:06:54 +00:00
|
|
|
g_state.lgr->on_restart(g_state.lgrData);
|
2020-10-14 08:40:20 +00:00
|
|
|
|
2020-10-29 11:57:04 +00:00
|
|
|
if (useDMA)
|
|
|
|
{
|
|
|
|
for(int i = 0; i < sizeof(dmaInfo) / sizeof(struct DMAFrameInfo); ++i)
|
|
|
|
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)
|
2020-01-18 19:51:21 +00:00
|
|
|
if (!spice_process(1000))
|
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-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
|
|
|
{
|
2019-05-21 05:03:59 +00:00
|
|
|
const LG_Renderer *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-01-04 01:06:54 +00:00
|
|
|
g_state.lgrData = NULL;
|
2021-01-27 08:38:34 +00:00
|
|
|
*needsOpenGL = false;
|
|
|
|
if (!r->create(&g_state.lgrData, lgrParams, needsOpenGL))
|
2017-12-17 12:15:18 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
// initialize the renderer
|
2021-01-26 10:46:30 +00:00
|
|
|
if (!r->initialize(g_state.lgrData))
|
2017-12-17 12:15:18 +00:00
|
|
|
{
|
2021-01-04 01:06:54 +00:00
|
|
|
r->deinitialize(g_state.lgrData);
|
2017-12-17 12:15:18 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
DEBUG_INFO("Using Renderer: %s", r->get_name());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-01-14 06:05:26 +00:00
|
|
|
static int lg_run(void)
|
2017-10-19 04:15:49 +00:00
|
|
|
{
|
2021-01-04 01:06:54 +00:00
|
|
|
memset(&g_state, 0, sizeof(g_state));
|
2021-01-05 00:47:17 +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-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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
// 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-01-25 08:58:36 +00:00
|
|
|
if (g_params.useSpiceInput || g_params.useSpiceClipboard)
|
2019-10-26 01:03:10 +00:00
|
|
|
{
|
|
|
|
spice_set_clipboard_cb(
|
2021-01-25 08:58:36 +00:00
|
|
|
cb_spiceNotice,
|
|
|
|
cb_spiceData,
|
|
|
|
cb_spiceRelease,
|
|
|
|
cb_spiceRequest);
|
2019-10-26 01:03:10 +00:00
|
|
|
|
2021-01-25 08:58:36 +00:00
|
|
|
if (!spice_connect(g_params.spiceHost, g_params.spicePort, ""))
|
2019-10-26 01:03:10 +00:00
|
|
|
{
|
|
|
|
DEBUG_ERROR("Failed to connect to spice server");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2021-01-04 01:06:54 +00:00
|
|
|
while(g_state.state != APP_STATE_SHUTDOWN && !spice_ready())
|
2020-01-18 19:51:21 +00:00
|
|
|
if (!spice_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;
|
|
|
|
}
|
|
|
|
|
2020-08-09 06:17:08 +00:00
|
|
|
spice_mouse_mode(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.showFPS = g_params.showFPS;
|
|
|
|
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
|
|
|
}
|
2021-01-25 08:58:36 +00:00
|
|
|
g_state.lgr = LG_Renderers[g_params.forceRendererIndex];
|
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
|
|
|
{
|
2021-01-04 01:06:54 +00:00
|
|
|
g_state.lgr = LG_Renderers[i];
|
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-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-01-27 08:38:34 +00:00
|
|
|
.minimizeOnFocusLoss = g_params.minimizeOnFocusLoss,
|
|
|
|
.opengl = needsOpenGL
|
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-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
|
|
|
|
if (!(e_frame = lgCreateEvent(true, 0)))
|
|
|
|
{
|
|
|
|
DEBUG_ERROR("failed to create the frame event");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
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-01-25 08:58:36 +00:00
|
|
|
DEBUG_INFO("Check the host log in your guest at %%TEMP%%\\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
|
|
|
{
|
|
|
|
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");
|
2020-05-29 04:14:31 +00:00
|
|
|
DEBUG_ERROR("Please install the matching host application for this client");
|
|
|
|
|
|
|
|
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
|
|
|
|
2020-01-02 12:59:06 +00:00
|
|
|
if (!lgCreateThread("cursorThread", cursorThread, NULL, &t_cursor))
|
2019-12-09 16:30:04 +00:00
|
|
|
{
|
|
|
|
DEBUG_ERROR("cursor create thread failed");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-01-25 16:34:22 +00:00
|
|
|
if (!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);
|
|
|
|
lgSignalEvent(e_frame);
|
2021-01-25 16:34:22 +00:00
|
|
|
|
|
|
|
core_stopFrameThread();
|
|
|
|
|
2020-08-11 04:30:44 +00:00
|
|
|
lgJoinThread(t_cursor, NULL);
|
2020-08-11 04:45:43 +00:00
|
|
|
t_cursor = NULL;
|
|
|
|
|
2020-12-03 13:32:28 +00:00
|
|
|
lgInit();
|
|
|
|
|
2021-01-04 01:06:54 +00:00
|
|
|
g_state.lgr->on_restart(g_state.lgrData);
|
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
|
|
|
{
|
|
|
|
lgSignalEvent(e_startup);
|
2020-05-21 03:41:59 +00:00
|
|
|
lgSignalEvent(e_frame);
|
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
|
|
|
|
2020-05-21 03:41:59 +00:00
|
|
|
if (e_frame)
|
|
|
|
{
|
|
|
|
lgFreeEvent(e_frame);
|
|
|
|
e_frame = NULL;
|
|
|
|
}
|
|
|
|
|
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-01-25 08:58:36 +00:00
|
|
|
if (g_params.useSpiceInput && spice_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;
|
2018-01-29 06:27:12 +00:00
|
|
|
spice_key_up(scancode);
|
|
|
|
}
|
|
|
|
|
2020-02-01 03:24:23 +00:00
|
|
|
spice_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-01-26 10:46:30 +00:00
|
|
|
ivshmemClose(&g_state.shm);
|
2017-11-25 07:20:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char * argv[])
|
|
|
|
{
|
2020-05-29 04:18:02 +00:00
|
|
|
if (getuid() == 0)
|
|
|
|
{
|
|
|
|
DEBUG_ERROR("Do not run looking glass as root!");
|
|
|
|
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);
|
|
|
|
|
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");
|
|
|
|
|
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();
|
2019-05-21 05:03:59 +00:00
|
|
|
|
|
|
|
// early renderer setup for option registration
|
|
|
|
for(unsigned int i = 0; i < LG_RENDERER_COUNT; ++i)
|
|
|
|
LG_Renderers[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-01-25 07:36:51 +00:00
|
|
|
cleanupCrashHandler();
|
|
|
|
return ret;
|
2020-01-17 03:35:08 +00:00
|
|
|
}
|