Compare commits

...

18 Commits

Author SHA1 Message Date
Geoffrey McRae
6ee30b2308 [client] update for the new LGMP interface prototypes 2022-11-07 20:59:36 +11:00
Geoffrey McRae
714b1bce70 [lgmp] update to fix array overflow bug 2022-11-07 20:51:10 +11:00
Geoffrey McRae
3ec632dd3c [host] initial changes to implement frame rate control 2022-11-07 19:48:38 +11:00
Geoffrey McRae
f0cb9d1167 [client] main: fix spice display fallback when waiting for LGMP upgrade 2022-11-07 19:16:52 +11:00
Geoffrey McRae
6cd88a70ad [host/client] lgmp: update to fix possible race issue 2022-11-07 14:44:26 +11:00
Geoffrey McRae
697bbcd6d4 [host] app: correct timer restart on lgmp corruption recovery 2022-11-07 13:36:57 +11:00
Geoffrey McRae
ecca5720a9 [host] app: ensure that rand will be random 2022-11-07 13:26:52 +11:00
Geoffrey McRae
50e856f823 [host] lgmp: update LGMP again to fix bug in last update 2022-11-07 13:20:24 +11:00
Geoffrey McRae
6359bb9acd [host] lgmp: update to fix failure to randomize the session ID 2022-11-07 13:12:51 +11:00
Geoffrey McRae
938011fce6 [module] swap offset & size in printk output for consistency 2022-11-07 13:12:51 +11:00
Geoffrey McRae
d09a10299e [module] cosmetics 2022-11-07 13:12:51 +11:00
Geoffrey McRae
8e706636d3 [host] app: don't stop the lgmpTimer on session recovery 2022-11-07 12:20:25 +11:00
Ali Abdel-Qader
352cd2fafe [client] remove non-prototype function declarations
With -Wstrict-prototypes on non-protyped functions are deprecated and
functions must include a void parameter if they do not take parameters.
2022-11-01 08:03:15 +11:00
Chris Spencer
081a0a419d [client] audio: use actual device period if larger than expected maximum
This is rare and I'm not sure what causes it, but PipeWire sometimes uses a
larger period size than requested for no obvious reason (e.g., we could
request a period size of 512, but PipeWire uses 2048 anyway). This causes
us to stay in a permanent state of underrunning because the target latency
is too low.

With this change, we use the actual device period in the target latency
calculation if it is larger than the expected maximum. We may still get
some glitches at the beginning of playback (because the startup latency is
based upon the expected maximum period size), but it will recover after a
few seconds as it adjusts to the new target latency.
2022-11-01 08:02:37 +11:00
esi
7e42e6cdce [obs] Fix function call causing crash on lgUpdate 2022-11-01 08:01:54 +11:00
Quantum
d857b2a36e [cmake] CheckSubmodule: check for nanosvg 2022-11-01 08:01:08 +11:00
Quantum
ba64a2d400 [repos] nanosvg: convert to submodule
This is fine now that nanosvg can be compiled with -Wstrict-prototypes
without warning.
2022-11-01 08:01:08 +11:00
Geoffrey McRae
9d8bc46812 [client] keybind: fix typo 2022-11-01 08:00:37 +11:00
20 changed files with 93 additions and 4561 deletions

3
.gitmodules vendored
View File

@@ -10,3 +10,6 @@
[submodule "repos/wayland-protocols"]
path = repos/wayland-protocols
url = https://gitlab.freedesktop.org/wayland/wayland-protocols.git
[submodule "repos/nanosvg"]
path = repos/nanosvg
url = https://github.com/memononen/nanosvg.git

View File

@@ -9,7 +9,7 @@ arcnmx <arcnmx@users.noreply.github.com> (arcnmx)
TheCakeIsNaOH <TheCakeIsNaOH@gmail.com> (TheCakeIsNaOH)
NamoDev <namodev@users.noreply.github.com> (NamoDev)
feltcat <58396817+feltcat@users.noreply.github.com> (feltcat)
Ali Abdel-Qader <abdelqaderali@protonmail.com>
Ali Abdel-Qader <abdelqaderali@protonmail.com> (thrifty-txt)
Jack Karamanian <karamanian.jack@gmail.com>
Mikko Rasa <tdb@tdb.fi> (DataBeaver)
Omar Pakker <Omar007@users.noreply.github.com> (Omar007)

View File

@@ -103,7 +103,7 @@ bool x11CBEventThread(const XEvent xe)
return false;
}
bool x11CBInit()
bool x11CBInit(void)
{
x11cb.aCurSelection = BadValue;
for(int i = 0; i < LG_CLIPBOARD_DATA_NONE; ++i)

View File

@@ -458,7 +458,7 @@ void app_handleMouseRelative(double normx, double normy,
// cursor warp support. Instead, we attempt a best-effort emulation which works
// with a 1:1 mouse movement patch applied in the guest. For anything fancy, use
// capture mode.
void app_handleMouseBasic()
void app_handleMouseBasic(void)
{
/* do not pass mouse events to the guest if we do not have focus */
if (!g_cursor.guest.valid || !g_state.haveSrcSize || !g_state.focused ||
@@ -495,7 +495,7 @@ void app_handleMouseBasic()
DEBUG_ERROR("failed to send mouse motion message");
}
void app_resyncMouseBasic()
void app_resyncMouseBasic(void)
{
if (!g_cursor.guest.valid)
return;

View File

@@ -525,12 +525,15 @@ void audio_playbackData(uint8_t * data, size_t size)
}
/* Determine the target latency. This is made up of the maximum audio device
* period (plus a little extra to absorb timing jitter) and a configurable
* period (or the current actual period, if larger than the expected maximum),
* plus a little extra to absorb timing jitter, and a configurable
* additional buffer period. The default is set high enough to absorb typical
* timing jitter from qemu. */
int configLatencyMs = max(g_params.audioBufferLatency, 0);
int maxPeriodFrames =
max(audio.playback.deviceMaxPeriodFrames, spiceData->devPeriodFrames);
double targetLatencyFrames =
audio.playback.deviceMaxPeriodFrames * 1.1 +
maxPeriodFrames * 1.1 +
configLatencyMs * audio.playback.sampleRate / 1000.0;
/* If the device is currently at a lower period size than its maximum (which

View File

@@ -478,10 +478,11 @@ void core_handleMouseNormal(double ex, double ey)
if (!g_state.stopVideo &&
g_state.kvmfrFeatures & KVMFR_FEATURE_SETCURSORPOS)
{
const KVMFRSetCursorPos msg = {
.msg.type = KVMFR_MESSAGE_SETCURSORPOS,
.x = round(guest.x),
.y = round(guest.y)
const KVMFRMessage_SetCursorPos msg = {
.msg.type = KVMFR_MESSAGE_SETCURSORPOS,
.msg.clientID = g_state.clientID,
.x = round(guest.x),
.y = round(guest.y)
};
uint32_t setPosSerial;

View File

@@ -170,7 +170,7 @@ void keybind_spiceRegister(void)
"Spice keyboard & mouse toggle");
app_registerKeybind(KEY_INSERT, 0, bind_mouseSens, (void *) true ,
"Increase mouse sensitivity 0, in capture mode");
"Increase mouse sensitivity in capture mode");
app_registerKeybind(KEY_DELETE, 0, bind_mouseSens, (void *) false,
"Descrease mouse sensitivity in capture mode");

View File

@@ -1433,7 +1433,8 @@ restart:
initialSpiceEnable = 0;
}
status = lgmpClientSessionInit(g_state.lgmp, &udataSize, (uint8_t **)&udata);
status = lgmpClientSessionInit(g_state.lgmp, &udataSize, (uint8_t **)&udata,
&g_state.clientID);
switch(status)
{
case LGMP_OK:
@@ -1442,21 +1443,19 @@ restart:
case LGMP_ERR_INVALID_VERSION:
{
reportBadVersion();
msgs[msgsCount++] = app_msgBox(
"Incompatible LGMP Version",
"The host application is not compatible with this client.\n"
"Please download and install the matching version."
);
if (waitCount++ == 0)
{
reportBadVersion();
msgs[msgsCount++] = app_msgBox(
"Incompatible LGMP Version",
"The host application is not compatible with this client.\n"
"Please download and install the matching version."
);
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;
DEBUG_INFO("Waiting for you to upgrade the host application");
}
g_state.ds->wait(1000);
continue;
}

View File

@@ -121,6 +121,7 @@ struct AppState
struct IVSHMEM shm;
PLGMPClient lgmp;
uint32_t clientID;
PLGMPClientQueue pointerQueue;
LG_Lock pointerQueueLock;
KVMFRFeatureFlags kvmfrFeatures;

View File

@@ -3,7 +3,8 @@ if (EXISTS "${PROJECT_TOP}/.git" AND (
(NOT EXISTS "${PROJECT_TOP}/repos/LGMP/.git") OR
(NOT EXISTS "${PROJECT_TOP}/repos/PureSpice/.git") OR
(NOT EXISTS "${PROJECT_TOP}/repos/cimgui/imgui/.git") OR
(NOT EXISTS "${PROJECT_TOP}/repos/wayland-protocols/.git")
(NOT EXISTS "${PROJECT_TOP}/repos/wayland-protocols/.git") OR
(NOT EXISTS "${PROJECT_TOP}/repos/nanosvg/.git")
))
message(FATAL_ERROR "Submodules are not initialized. Run\n\tgit submodule update --init --recursive")
endif()

View File

@@ -28,7 +28,7 @@
#include "types.h"
#define KVMFR_MAGIC "KVMFR---"
#define KVMFR_VERSION 19
#define KVMFR_VERSION 20
#define KVMFR_MAX_DAMAGE_RECTS 64
@@ -56,7 +56,8 @@ typedef uint32_t KVMFRFeatureFlags;
enum
{
KVMFR_MESSAGE_SETCURSORPOS
KVMFR_MESSAGE_SETCURSORPOS,
KVMFR_MESSAGE_FRAME_TIME
};
typedef uint32_t KVMFRMessageType;
@@ -137,6 +138,8 @@ typedef struct KVMFRFrame
{
uint32_t formatVer; // the frame format version number
uint32_t frameSerial; // the unique frame number
uint64_t frameTimeUs; // when the capture was started
uint64_t frameElapsedUs; // total time elapsed to capture the frame
FrameType type; // the frame data type
uint32_t screenWidth; // the client's screen width
uint32_t screenHeight; // the client's screen height
@@ -155,14 +158,28 @@ KVMFRFrame;
typedef struct KVMFRMessage
{
KVMFRMessageType type;
uint32_t clientID;
}
KVMFRMessage;
typedef struct KVMFRSetCursorPos
typedef struct KVMFRMessage_SetCursorPos
{
KVMFRMessage msg;
int32_t x, y;
}
KVMFRSetCursorPos;
KVMFRMessage_SetCursorPos;
typedef struct KVMFRMessage_FrameTime
{
KVMFRMessage msg;
/* this is the desired time to start the next capture operation where zero is
* immediate. This is only a hint to the host application and may not be
* honored. The value provided should be offset from the latest frame's
* frameTimeUs. When multiple clients are sending this message, the one with
* the lowest value will be used.
*/
uint64_t frameTimeUs;
}
KVMFRMessage_FrameTime;
#endif

View File

@@ -96,6 +96,7 @@ struct app
unsigned int frameIndex;
bool frameValid;
uint32_t frameSerial;
uint64_t frameTimeUs;
CaptureInterface * iface;
@@ -268,6 +269,7 @@ static bool sendFrame(void)
}
fi->formatVer = frame.formatVer;
fi->frameTimeUs = app.frameTimeUs;
fi->frameSerial = app.frameSerial++;
fi->screenWidth = frame.screenWidth;
fi->screenHeight = frame.screenHeight;
@@ -295,6 +297,11 @@ static bool sendFrame(void)
FrameBuffer * fb = (FrameBuffer *)(((uint8_t*)fi) + fi->offset);
framebuffer_prepare(fb);
// calculate the elapsed time as late as possible, note that this does not
// take into account the memory copy time and the client's that care about
// this value will need to take this into account
fi->frameElapsedUs = microtime() - app.frameTimeUs;
/* we post and then get the frame, this is intentional! */
if ((status = lgmpHostQueuePost(app.frameQueue, 0,
app.frameMemory[app.frameIndex])) != LGMP_OK)
@@ -719,6 +726,9 @@ int app_main(int argc, char * argv[])
if (!installCrashHandler(os_getExecutable()))
DEBUG_WARN("Failed to install the crash handler");
// make sure rng is actually seeded for LGMP
srand((unsigned)time(NULL));
app.state = APP_STATE_RUNNING;
ivshmemOptionsInit();
@@ -855,9 +865,9 @@ int app_main(int argc, char * argv[])
{
DEBUG_INFO("Performing LGMP reinitialization");
lgmpShutdown();
app.state = APP_STATE_RUNNING;
if (!lgmpSetup(&shmDev))
goto fail_lgmp;
app.state = APP_STATE_RUNNING;
}
if (app.state == APP_STATE_IDLE)
@@ -908,6 +918,7 @@ int app_main(int argc, char * argv[])
else
previousFrameTime = now;
app.frameTimeUs = microtime();
switch(iface->capture())
{
case CAPTURE_RESULT_OK:

View File

@@ -124,7 +124,8 @@ err:
return ERR_PTR(ret);
}
static void unmap_kvmfrbuf(struct dma_buf_attachment * at, struct sg_table * sg, enum dma_data_direction direction)
static void unmap_kvmfrbuf(struct dma_buf_attachment * at, struct sg_table * sg,
enum dma_data_direction direction)
{
dma_unmap_sg(at->dev, sg->sgl, sg->nents, direction);
sg_free_table(sg);
@@ -144,7 +145,8 @@ static int mmap_kvmfrbuf(struct dma_buf * buf, struct vm_area_struct * vma)
unsigned long size = vma->vm_end - vma->vm_start;
unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
if ((offset + size > (kbuf->pagecount << PAGE_SHIFT)) || (offset + size < offset))
if ((offset + size > (kbuf->pagecount << PAGE_SHIFT))
|| (offset + size < offset))
return -EINVAL;
if ((vma->vm_flags & (VM_SHARED | VM_MAYSHARE)) == 0)
@@ -158,7 +160,8 @@ static int mmap_kvmfrbuf(struct dma_buf * buf, struct vm_area_struct * vma)
return 0;
case KVMFR_TYPE_STATIC:
return remap_vmalloc_range(vma, kbuf->kdev->addr + kbuf->offset, vma->vm_pgoff);
return remap_vmalloc_range(vma, kbuf->kdev->addr + kbuf->offset,
vma->vm_pgoff);
default:
return -EINVAL;
@@ -173,7 +176,8 @@ static const struct dma_buf_ops kvmfrbuf_ops =
.mmap = mmap_kvmfrbuf
};
static long kvmfr_dmabuf_create(struct kvmfr_dev * kdev, struct file * filp, unsigned long arg)
static long kvmfr_dmabuf_create(struct kvmfr_dev * kdev, struct file * filp,
unsigned long arg)
{
struct kvmfr_dmabuf_create create;
DEFINE_DMA_BUF_EXPORT_INFO(exp_kdev);
@@ -194,7 +198,8 @@ static long kvmfr_dmabuf_create(struct kvmfr_dev * kdev, struct file * filp, uns
return -EINVAL;
}
if ((create.offset + create.size > kdev->size) || (create.offset + create.size < create.offset))
if ((create.offset + create.size > kdev->size) ||
(create.offset + create.size < create.offset))
return -EINVAL;
kbuf = kzalloc(sizeof(struct kvmfrbuf), GFP_KERNEL);
@@ -204,7 +209,8 @@ static long kvmfr_dmabuf_create(struct kvmfr_dev * kdev, struct file * filp, uns
kbuf->kdev = kdev;
kbuf->pagecount = create.size >> PAGE_SHIFT;
kbuf->offset = create.offset;
kbuf->pages = kmalloc_array(kbuf->pagecount, sizeof(*kbuf->pages), GFP_KERNEL);
kbuf->pages = kmalloc_array(kbuf->pagecount, sizeof(*kbuf->pages),
GFP_KERNEL);
if (!kbuf->pages)
{
ret = -ENOMEM;
@@ -244,7 +250,8 @@ static long kvmfr_dmabuf_create(struct kvmfr_dev * kdev, struct file * filp, uns
goto err;
}
printk("kvmfr_dmabuf_create: offset: %llu, size: %llu\n", create.offset, create.size);
printk("kvmfr_dmabuf_create with size %llu offset: %llu",
create.size, create.offset);
return dma_buf_fd(buf, create.flags & KVMFR_DMABUF_FLAG_CLOEXEC ? O_CLOEXEC : 0);
err:
@@ -253,7 +260,8 @@ err:
return ret;
}
static long device_ioctl(struct file * filp, unsigned int ioctl, unsigned long arg)
static long device_ioctl(struct file * filp, unsigned int ioctl,
unsigned long arg)
{
struct kvmfr_dev * kdev;
long ret;
@@ -359,7 +367,8 @@ static int kvmfr_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
mutex_unlock(&minor_lock);
kdev->devNo = MKDEV(kvmfr->major, kdev->minor);
kdev->pDev = device_create(kvmfr->pClass, NULL, kdev->devNo, NULL, KVMFR_DEV_NAME "%d", kdev->minor);
kdev->pDev = device_create(kvmfr->pClass, NULL, kdev->devNo, NULL,
KVMFR_DEV_NAME "%d", kdev->minor);
if (IS_ERR(kdev->pDev))
goto out_unminor;
@@ -451,7 +460,9 @@ static int create_static_device_unlocked(int size_mb)
kdev->addr = vmalloc_user(kdev->size);
if (!kdev->addr)
{
printk(KERN_ERR "kvmfr: failed to allocate memory for static device: %d MiB\n", size_mb);
printk(
KERN_ERR "kvmfr: failed to allocate memory for static device: %d MiB\n",
size_mb);
ret = -ENOMEM;
goto out_free;
}
@@ -461,7 +472,8 @@ static int create_static_device_unlocked(int size_mb)
goto out_release;
kdev->devNo = MKDEV(kvmfr->major, kdev->minor);
kdev->pDev = device_create(kvmfr->pClass, NULL, kdev->devNo, NULL, KVMFR_DEV_NAME "%d", kdev->minor);
kdev->pDev = device_create(kvmfr->pClass, NULL, kdev->devNo, NULL,
KVMFR_DEV_NAME "%d", kdev->minor);
if (IS_ERR(kdev->pDev))
goto out_unminor;

View File

@@ -156,7 +156,7 @@ static void deinit(LGPlugin * this)
case STATE_STOPPING:
case STATE_RESTARTING:
this->state = STATE_STOPPING;
createThreads(this);
waitThreads(this);
this->state = STATE_STOPPED;
/* fallthrough */

1
repos/nanosvg Submodule

Submodule repos/nanosvg added at 64d59e4d53

View File

@@ -1,31 +0,0 @@
## Compiled source #
*.com
*.class
*.dll
*.exe
*.o
*.so
test
## Logs and databases #
*.log
*.sql
*.sqlite
## OS generated files #
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
## Build dir
build/*
## Stuff in example
example/_*
## xcode specific
*xcuserdata*

View File

@@ -1,18 +0,0 @@
Copyright (c) 2013-14 Mikko Mononen memon@inside.org
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff