[client] spice: move protocol into transport

This commit is contained in:
Geoffrey McRae
2026-08-11 15:33:53 +10:00
parent 2bd0db1a26
commit 5b12685e3c
28 changed files with 2398 additions and 1058 deletions

View File

@@ -0,0 +1,215 @@
/**
* Looking Glass
* Copyright © 2017-2026 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "input.h"
#include "kb.h"
#include "common/locking.h"
#include <purespice.h>
#include <stddef.h>
#include <stdlib.h>
struct SpiceInput
{
LG_Lock stateLock;
LG_Lock statusDispatch;
bool available;
uint32_t statusGeneration;
LG_InputStatusFn statusCallback;
void * statusOpaque;
};
static uint32_t nextGeneration(uint32_t generation)
{
if (++generation == 0)
++generation;
return generation;
}
static bool spiceSupports(void * opaque, LG_InputSupport support)
{
(void)opaque;
(void)support;
return false;
}
static void spiceSetStatusListener(void * opaque,
LG_InputStatusFn callback, void * callbackOpaque)
{
SpiceInput * input = opaque;
LG_LOCK(input->statusDispatch);
LG_LOCK(input->stateLock);
input->statusCallback = callback;
input->statusOpaque = callbackOpaque;
const LG_InputStatus status =
{
.available = input->available,
.generation = input->statusGeneration,
};
LG_UNLOCK(input->stateLock);
if (callback)
callback(callbackOpaque, &status);
LG_UNLOCK(input->statusDispatch);
}
static bool spiceKeyDown(void * opaque, int key)
{
SpiceInput * input = opaque;
const uint32_t ps2 = linux_to_ps2[key];
LG_LOCK(input->stateLock);
const bool result = input->available &&
(!ps2 || purespice_keyDown(ps2));
LG_UNLOCK(input->stateLock);
return result;
}
static bool spiceKeyUp(void * opaque, int key)
{
SpiceInput * input = opaque;
const uint32_t ps2 = linux_to_ps2[key];
LG_LOCK(input->stateLock);
const bool result = !ps2 || purespice_keyUp(ps2);
LG_UNLOCK(input->stateLock);
return result;
}
static bool spiceKeyboardLEDs(void * opaque, bool numLock, bool capsLock,
bool scrollLock)
{
SpiceInput * input = opaque;
const uint32_t modifiers =
(scrollLock ? 1 /* SPICE_SCROLL_LOCK_MODIFIER */ : 0) |
(numLock ? 2 /* SPICE_NUM_LOCK_MODIFIER */ : 0) |
(capsLock ? 4 /* SPICE_CAPS_LOCK_MODIFIER */ : 0);
LG_LOCK(input->stateLock);
const bool result = input->available &&
purespice_keyModifiers(modifiers);
LG_UNLOCK(input->stateLock);
return result;
}
static bool spiceMouseMotion(void * opaque, int32_t x, int32_t y)
{
SpiceInput * input = opaque;
LG_LOCK(input->stateLock);
const bool result = input->available && purespice_mouseMotion(x, y);
LG_UNLOCK(input->stateLock);
return result;
}
static bool spiceMousePress(void * opaque, unsigned int button)
{
if (button > 7)
return true;
SpiceInput * input = opaque;
LG_LOCK(input->stateLock);
const bool result = input->available && purespice_mousePress(button);
LG_UNLOCK(input->stateLock);
return result;
}
static bool spiceMouseRelease(void * opaque, unsigned int button)
{
if (button > 7)
return true;
SpiceInput * input = opaque;
LG_LOCK(input->stateLock);
const bool result = purespice_mouseRelease(button);
LG_UNLOCK(input->stateLock);
return result;
}
static const LG_InputOps l_inputOps =
{
.name = "SPICE",
.supports = spiceSupports,
.setStatusListener = spiceSetStatusListener,
.keyDown = spiceKeyDown,
.keyUp = spiceKeyUp,
.keyboardLEDs = spiceKeyboardLEDs,
.mouseMotion = spiceMouseMotion,
.mousePosition = NULL,
.mousePress = spiceMousePress,
.mouseRelease = spiceMouseRelease,
.reset = NULL,
};
bool spiceInput_init(SpiceInput ** input)
{
*input = calloc(1, sizeof(**input));
if (!*input)
return false;
LG_LOCK_INIT((*input)->stateLock);
LG_LOCK_INIT((*input)->statusDispatch);
return true;
}
void spiceInput_free(SpiceInput ** input)
{
if (!input || !*input)
return;
LG_LOCK_FREE((*input)->statusDispatch);
LG_LOCK_FREE((*input)->stateLock);
free(*input);
*input = NULL;
}
const LG_InputOps * spiceInput_getOps(void)
{
return &l_inputOps;
}
void spiceInput_setAvailable(SpiceInput * input, bool available)
{
LG_LOCK(input->statusDispatch);
LG_LOCK(input->stateLock);
const bool changed = input->available != available;
if (changed)
{
input->available = available;
input->statusGeneration = nextGeneration(input->statusGeneration);
}
const LG_InputStatusFn callback = input->statusCallback;
void * callbackOpaque = input->statusOpaque;
const LG_InputStatus status =
{
.available = input->available,
.generation = input->statusGeneration,
};
LG_UNLOCK(input->stateLock);
if (changed && callback)
callback(callbackOpaque, &status);
LG_UNLOCK(input->statusDispatch);
}