mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-22 15:11:31 +00:00
[client/idd] input: synchronize keyboard LEDs
Report guest HID keyboard LED state from LGInput to LGIdd and publish it with the LGMP input endpoint status. Forward SPICE modifier updates through the same provider state so evdev can synchronize physical keyboard LEDs across transport changes and reconnects. Bump the versioned pipe and KVMFR input status contracts for the new feedback state. Accept earlier LGMP input status versions without LED feedback so the client retains input while the guest driver is being upgraded.
This commit is contained in:
@@ -23,6 +23,9 @@
|
||||
|
||||
#include "interface/input.h"
|
||||
|
||||
typedef void (*LGInputKeyboardLEDsFn)(void * opaque,
|
||||
bool valid, uint8_t leds);
|
||||
|
||||
void lgInput_init(void);
|
||||
void lgInput_free(void);
|
||||
|
||||
@@ -35,6 +38,11 @@ void lgInput_useTransport(bool enable);
|
||||
|
||||
bool lgInput_available(void);
|
||||
bool lgInput_supports(LG_InputSupport support);
|
||||
/* The listener is called synchronously with the current guest LED state and
|
||||
* later from transport threads whenever it changes. The callback must not
|
||||
* call back into the input layer. */
|
||||
void lgInput_setKeyboardLEDsListener(
|
||||
LGInputKeyboardLEDsFn callback, void * opaque);
|
||||
|
||||
bool lgInput_keyDown(int key);
|
||||
bool lgInput_keyUp(int key);
|
||||
|
||||
@@ -30,10 +30,22 @@ typedef enum LG_InputSupport
|
||||
}
|
||||
LG_InputSupport;
|
||||
|
||||
enum
|
||||
{
|
||||
LG_KEYBOARD_LED_NUM_LOCK = 0x1,
|
||||
LG_KEYBOARD_LED_CAPS_LOCK = 0x2,
|
||||
LG_KEYBOARD_LED_SCROLL_LOCK = 0x4,
|
||||
LG_KEYBOARD_LED_COMPOSE = 0x8,
|
||||
LG_KEYBOARD_LED_KANA = 0x10,
|
||||
};
|
||||
|
||||
typedef struct LG_InputStatus
|
||||
{
|
||||
bool available;
|
||||
uint32_t generation;
|
||||
/* USB HID keyboard LED bits reported by the guest. */
|
||||
bool keyboardLEDsValid;
|
||||
uint8_t keyboardLEDs;
|
||||
}
|
||||
LG_InputStatus;
|
||||
|
||||
|
||||
@@ -32,6 +32,8 @@ struct InputBinding
|
||||
void * opaque;
|
||||
bool available;
|
||||
bool mouseAbsolute;
|
||||
bool keyboardLEDsValid;
|
||||
uint8_t keyboardLEDs;
|
||||
uint32_t epoch;
|
||||
};
|
||||
|
||||
@@ -39,8 +41,12 @@ static struct
|
||||
{
|
||||
LG_Lock bindingLock;
|
||||
LG_RWLock activeLock;
|
||||
LG_Lock keyboardLEDsDispatch;
|
||||
uint32_t nextBindingEpoch;
|
||||
|
||||
LGInputKeyboardLEDsFn keyboardLEDsCallback;
|
||||
void * keyboardLEDsOpaque;
|
||||
|
||||
struct InputBinding fallback;
|
||||
struct InputBinding transport;
|
||||
struct InputBinding active;
|
||||
@@ -95,6 +101,31 @@ static bool bindingEqual(const struct InputBinding * a,
|
||||
a->epoch == b->epoch;
|
||||
}
|
||||
|
||||
static bool keyboardLEDsEqual(const struct InputBinding * a,
|
||||
const struct InputBinding * b)
|
||||
{
|
||||
const bool aValid = a->ops && a->available && a->keyboardLEDsValid;
|
||||
const bool bValid = b->ops && b->available && b->keyboardLEDsValid;
|
||||
return aValid == bValid && (!aValid || a->keyboardLEDs == b->keyboardLEDs);
|
||||
}
|
||||
|
||||
static void dispatchKeyboardLEDs(void)
|
||||
{
|
||||
LG_LOCK(l_input.keyboardLEDsDispatch);
|
||||
LG_LOCK_SHARED(l_input.activeLock);
|
||||
LGInputKeyboardLEDsFn callback = l_input.keyboardLEDsCallback;
|
||||
void * opaque = l_input.keyboardLEDsOpaque;
|
||||
const bool valid = l_input.active.ops &&
|
||||
l_input.active.available &&
|
||||
l_input.active.keyboardLEDsValid;
|
||||
const uint8_t leds = l_input.active.keyboardLEDs;
|
||||
LG_UNLOCK_SHARED(l_input.activeLock);
|
||||
|
||||
if (callback)
|
||||
callback(opaque, valid, leds);
|
||||
LG_UNLOCK(l_input.keyboardLEDsDispatch);
|
||||
}
|
||||
|
||||
static void releaseKeysNL(void)
|
||||
{
|
||||
for (int key = 0; key < KEY_MAX; ++key)
|
||||
@@ -132,7 +163,7 @@ static void clearStateNL(void)
|
||||
atomic_store_explicit(&l_input.buttons, 0, memory_order_relaxed);
|
||||
}
|
||||
|
||||
static void updateActiveNL(bool dropActive)
|
||||
static bool updateActiveNL(bool dropActive)
|
||||
{
|
||||
const struct InputBinding next =
|
||||
l_input.useTransport && l_input.transport.available ?
|
||||
@@ -142,10 +173,15 @@ static void updateActiveNL(bool dropActive)
|
||||
|
||||
if (bindingEqual(&next, &l_input.active))
|
||||
{
|
||||
const bool keyboardLEDsChanged =
|
||||
!keyboardLEDsEqual(&next, &l_input.active);
|
||||
l_input.active = next;
|
||||
return;
|
||||
return keyboardLEDsChanged;
|
||||
}
|
||||
|
||||
const bool keyboardLEDsChanged =
|
||||
!keyboardLEDsEqual(&next, &l_input.active);
|
||||
|
||||
if (dropActive)
|
||||
clearStateNL();
|
||||
else
|
||||
@@ -160,11 +196,13 @@ static void updateActiveNL(bool dropActive)
|
||||
}
|
||||
else
|
||||
DEBUG_INFO("Input is unavailable");
|
||||
return keyboardLEDsChanged;
|
||||
}
|
||||
|
||||
static void updateStatusNL(struct InputBinding * binding,
|
||||
static bool updateStatusNL(struct InputBinding * binding,
|
||||
const LG_InputStatus * status)
|
||||
{
|
||||
const struct InputBinding oldActive = l_input.active;
|
||||
const bool wasActive = bindingEqual(&l_input.active, binding);
|
||||
|
||||
if (wasActive && !status->available)
|
||||
@@ -178,7 +216,11 @@ static void updateStatusNL(struct InputBinding * binding,
|
||||
binding->ops->mousePosition &&
|
||||
binding->ops->supports(binding->opaque,
|
||||
LG_INPUT_SUPPORT_MOUSE_ABSOLUTE);
|
||||
binding->keyboardLEDsValid = status->available &&
|
||||
status->keyboardLEDsValid;
|
||||
binding->keyboardLEDs = status->keyboardLEDs;
|
||||
updateActiveNL(false);
|
||||
return !keyboardLEDsEqual(&oldActive, &l_input.active);
|
||||
}
|
||||
|
||||
static void fallbackStatusChanged(void * opaque,
|
||||
@@ -188,10 +230,13 @@ static void fallbackStatusChanged(void * opaque,
|
||||
return;
|
||||
|
||||
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
||||
bool keyboardLEDsChanged = false;
|
||||
if (l_input.fallback.ops &&
|
||||
l_input.fallback.epoch == (uint32_t)(uintptr_t)opaque)
|
||||
updateStatusNL(&l_input.fallback, status);
|
||||
keyboardLEDsChanged = updateStatusNL(&l_input.fallback, status);
|
||||
LG_UNLOCK_EXCLUSIVE(l_input.activeLock);
|
||||
if (keyboardLEDsChanged)
|
||||
dispatchKeyboardLEDs();
|
||||
}
|
||||
|
||||
static void transportStatusChanged(void * opaque,
|
||||
@@ -201,10 +246,13 @@ static void transportStatusChanged(void * opaque,
|
||||
return;
|
||||
|
||||
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
||||
bool keyboardLEDsChanged = false;
|
||||
if (l_input.transport.ops &&
|
||||
l_input.transport.epoch == (uint32_t)(uintptr_t)opaque)
|
||||
updateStatusNL(&l_input.transport, status);
|
||||
keyboardLEDsChanged = updateStatusNL(&l_input.transport, status);
|
||||
LG_UNLOCK_EXCLUSIVE(l_input.activeLock);
|
||||
if (keyboardLEDsChanged)
|
||||
dispatchKeyboardLEDs();
|
||||
}
|
||||
|
||||
void lgInput_init(void)
|
||||
@@ -220,6 +268,7 @@ void lgInput_init(void)
|
||||
|
||||
LG_LOCK_INIT(l_input.bindingLock);
|
||||
LG_RWLOCK_INIT(l_input.activeLock);
|
||||
LG_LOCK_INIT(l_input.keyboardLEDsDispatch);
|
||||
}
|
||||
|
||||
void lgInput_free(void)
|
||||
@@ -228,6 +277,7 @@ void lgInput_free(void)
|
||||
struct InputBinding transport;
|
||||
|
||||
LG_LOCK(l_input.bindingLock);
|
||||
LG_LOCK(l_input.keyboardLEDsDispatch);
|
||||
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
||||
resetActiveNL();
|
||||
fallback = l_input.fallback;
|
||||
@@ -235,7 +285,10 @@ void lgInput_free(void)
|
||||
l_input.active = (struct InputBinding) { 0 };
|
||||
l_input.fallback = (struct InputBinding) { 0 };
|
||||
l_input.transport = (struct InputBinding) { 0 };
|
||||
l_input.keyboardLEDsCallback = NULL;
|
||||
l_input.keyboardLEDsOpaque = NULL;
|
||||
LG_UNLOCK_EXCLUSIVE(l_input.activeLock);
|
||||
LG_UNLOCK(l_input.keyboardLEDsDispatch);
|
||||
|
||||
if (fallback.ops && fallback.ops->setStatusListener)
|
||||
fallback.ops->setStatusListener(fallback.opaque, NULL, NULL);
|
||||
@@ -244,6 +297,7 @@ void lgInput_free(void)
|
||||
|
||||
LG_UNLOCK(l_input.bindingLock);
|
||||
LG_RWLOCK_FREE(l_input.activeLock);
|
||||
LG_LOCK_FREE(l_input.keyboardLEDsDispatch);
|
||||
LG_LOCK_FREE(l_input.bindingLock);
|
||||
}
|
||||
|
||||
@@ -270,9 +324,12 @@ static void setBinding(struct InputBinding * target,
|
||||
const struct InputBinding next = makeBinding(ops, opaque);
|
||||
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
||||
*target = next;
|
||||
updateActiveNL(false);
|
||||
const bool keyboardLEDsChanged = updateActiveNL(false);
|
||||
LG_UNLOCK_EXCLUSIVE(l_input.activeLock);
|
||||
|
||||
if (keyboardLEDsChanged)
|
||||
dispatchKeyboardLEDs();
|
||||
|
||||
if (next.ops && next.ops->setStatusListener)
|
||||
next.ops->setStatusListener(next.opaque, statusFn,
|
||||
(void *)(uintptr_t)next.epoch);
|
||||
@@ -285,8 +342,10 @@ static void dropBinding(struct InputBinding * target)
|
||||
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
||||
const bool wasActive = bindingEqual(&l_input.active, target);
|
||||
*target = (struct InputBinding) { 0 };
|
||||
updateActiveNL(wasActive);
|
||||
const bool keyboardLEDsChanged = updateActiveNL(wasActive);
|
||||
LG_UNLOCK_EXCLUSIVE(l_input.activeLock);
|
||||
if (keyboardLEDsChanged)
|
||||
dispatchKeyboardLEDs();
|
||||
LG_UNLOCK(l_input.bindingLock);
|
||||
}
|
||||
|
||||
@@ -314,8 +373,10 @@ void lgInput_useTransport(bool enable)
|
||||
{
|
||||
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
||||
l_input.useTransport = enable;
|
||||
updateActiveNL(false);
|
||||
const bool keyboardLEDsChanged = updateActiveNL(false);
|
||||
LG_UNLOCK_EXCLUSIVE(l_input.activeLock);
|
||||
if (keyboardLEDsChanged)
|
||||
dispatchKeyboardLEDs();
|
||||
}
|
||||
|
||||
bool lgInput_available(void)
|
||||
@@ -344,6 +405,24 @@ bool lgInput_supports(LG_InputSupport support)
|
||||
return result;
|
||||
}
|
||||
|
||||
void lgInput_setKeyboardLEDsListener(
|
||||
LGInputKeyboardLEDsFn callback, void * opaque)
|
||||
{
|
||||
LG_LOCK(l_input.keyboardLEDsDispatch);
|
||||
LG_LOCK_SHARED(l_input.activeLock);
|
||||
l_input.keyboardLEDsCallback = callback;
|
||||
l_input.keyboardLEDsOpaque = opaque;
|
||||
const bool valid = l_input.active.ops &&
|
||||
l_input.active.available &&
|
||||
l_input.active.keyboardLEDsValid;
|
||||
const uint8_t leds = l_input.active.keyboardLEDs;
|
||||
LG_UNLOCK_SHARED(l_input.activeLock);
|
||||
|
||||
if (callback)
|
||||
callback(opaque, valid, leds);
|
||||
LG_UNLOCK(l_input.keyboardLEDsDispatch);
|
||||
}
|
||||
|
||||
bool lgInput_keyDown(int key)
|
||||
{
|
||||
if (key < 0 || key >= KEY_MAX)
|
||||
|
||||
@@ -41,6 +41,7 @@ extern const LG_TransportOps LGT_SPICE;
|
||||
struct SpiceInput
|
||||
{
|
||||
bool available;
|
||||
uint32_t keyModifiers;
|
||||
unsigned int onN;
|
||||
unsigned int offN;
|
||||
unsigned int onOrder;
|
||||
@@ -304,6 +305,11 @@ void spiceInput_setAvailable(SpiceInput * input, bool available)
|
||||
}
|
||||
}
|
||||
|
||||
void spiceInput_setKeyboardLEDs(SpiceInput * input, uint32_t modifiers)
|
||||
{
|
||||
input->keyModifiers = modifiers;
|
||||
}
|
||||
|
||||
bool spiceClipboard_init(SpiceClipboard ** clipboard)
|
||||
{
|
||||
*clipboard = calloc(1, sizeof(**clipboard));
|
||||
|
||||
@@ -52,6 +52,18 @@
|
||||
#define INPUT_MOUSE_DELTA_MIN (INT16_MIN * INPUT_MAX_SPLIT_REPORTS)
|
||||
#define INPUT_MOUSE_DELTA_MAX (INT16_MAX * INPUT_MAX_SPLIT_REPORTS)
|
||||
|
||||
_Static_assert(
|
||||
(int)LG_KEYBOARD_LED_NUM_LOCK ==
|
||||
(int)KVMFR_INPUT_KEYBOARD_LED_NUM_LOCK &&
|
||||
(int)LG_KEYBOARD_LED_CAPS_LOCK ==
|
||||
(int)KVMFR_INPUT_KEYBOARD_LED_CAPS_LOCK &&
|
||||
(int)LG_KEYBOARD_LED_SCROLL_LOCK ==
|
||||
(int)KVMFR_INPUT_KEYBOARD_LED_SCROLL_LOCK &&
|
||||
(int)LG_KEYBOARD_LED_COMPOSE ==
|
||||
(int)KVMFR_INPUT_KEYBOARD_LED_COMPOSE &&
|
||||
(int)LG_KEYBOARD_LED_KANA == (int)KVMFR_INPUT_KEYBOARD_LED_KANA,
|
||||
"Client and KVMFR keyboard LED bits differ");
|
||||
|
||||
enum LGMPInputMouseMode
|
||||
{
|
||||
LGMP_INPUT_MOUSE_NONE,
|
||||
@@ -104,6 +116,8 @@ struct LGMPInput
|
||||
bool claimed;
|
||||
bool available;
|
||||
bool ownerBlocked;
|
||||
bool keyboardLEDsValid;
|
||||
uint8_t keyboardLEDs;
|
||||
|
||||
uint32_t generation;
|
||||
uint32_t sequence;
|
||||
@@ -159,8 +173,10 @@ static LG_InputStatus inputStatus(const LGMPInput * input)
|
||||
{
|
||||
return (LG_InputStatus)
|
||||
{
|
||||
.available = input->available,
|
||||
.generation = input->endpointGeneration,
|
||||
.available = input->available,
|
||||
.generation = input->endpointGeneration,
|
||||
.keyboardLEDsValid = input->available && input->keyboardLEDsValid,
|
||||
.keyboardLEDs = input->keyboardLEDs,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -227,12 +243,15 @@ static void connectionFailed(LGMPInput * input, LGMP_STATUS status)
|
||||
++input->stats.counters.terminalFailures;
|
||||
}
|
||||
|
||||
if (input->available || input->endpointGeneration)
|
||||
if (input->available || input->endpointGeneration ||
|
||||
input->keyboardLEDsValid)
|
||||
input->notifyStatus = true;
|
||||
input->connected = false;
|
||||
input->claimed = false;
|
||||
input->available = false;
|
||||
input->ownerBlocked = false;
|
||||
input->keyboardLEDsValid = false;
|
||||
input->keyboardLEDs = 0;
|
||||
input->pendingHead = 0;
|
||||
input->pendingCount = 0;
|
||||
input->mouseMode = LGMP_INPUT_MOUSE_NONE;
|
||||
@@ -551,11 +570,25 @@ static bool validInputStatus(const KVMFRInputStatus * status)
|
||||
KVMFR_INPUT_CAP_MOUSE_RELATIVE |
|
||||
KVMFR_INPUT_CAP_MOUSE_ABSOLUTE |
|
||||
KVMFR_INPUT_CAP_KEYBOARD;
|
||||
static const uint32_t flags =
|
||||
static const uint32_t commonFlags =
|
||||
KVMFR_INPUT_STATUS_AVAILABLE |
|
||||
KVMFR_INPUT_STATUS_HAS_OWNER;
|
||||
static const uint8_t keyboardLEDs =
|
||||
KVMFR_INPUT_KEYBOARD_LED_NUM_LOCK |
|
||||
KVMFR_INPUT_KEYBOARD_LED_CAPS_LOCK |
|
||||
KVMFR_INPUT_KEYBOARD_LED_SCROLL_LOCK |
|
||||
KVMFR_INPUT_KEYBOARD_LED_COMPOSE |
|
||||
KVMFR_INPUT_KEYBOARD_LED_KANA;
|
||||
|
||||
if (status->version != KVMFR_INPUT_VERSION ||
|
||||
const bool supportedVersion = status->version > 0 &&
|
||||
status->version <= KVMFR_INPUT_VERSION;
|
||||
const bool keyboardLEDsVersion =
|
||||
status->version >= KVMFR_INPUT_KEYBOARD_LEDS_VERSION;
|
||||
const uint32_t flags = commonFlags |
|
||||
(keyboardLEDsVersion ?
|
||||
KVMFR_INPUT_STATUS_KEYBOARD_LEDS_VALID : 0);
|
||||
|
||||
if (!supportedVersion ||
|
||||
status->capabilities & ~capabilities ||
|
||||
status->flags & ~flags ||
|
||||
!status->generation ||
|
||||
@@ -567,6 +600,12 @@ static bool validInputStatus(const KVMFRInputStatus * status)
|
||||
!status->streamGeneration)
|
||||
return false;
|
||||
|
||||
if (status->keyboardLEDs & ~keyboardLEDs)
|
||||
return false;
|
||||
for (size_t i = 0; i < sizeof(status->statusReserved); ++i)
|
||||
if (status->statusReserved[i])
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < sizeof(status->streamReserved) /
|
||||
sizeof(status->streamReserved[0]); ++i)
|
||||
if (status->streamReserved[i])
|
||||
@@ -617,8 +656,14 @@ static bool validInputStatus(const KVMFRInputStatus * status)
|
||||
(status->flags & KVMFR_INPUT_STATUS_AVAILABLE) != 0;
|
||||
const bool hasOwner =
|
||||
(status->flags & KVMFR_INPUT_STATUS_HAS_OWNER) != 0;
|
||||
const bool keyboardLEDsValid = keyboardLEDsVersion &&
|
||||
(status->flags & KVMFR_INPUT_STATUS_KEYBOARD_LEDS_VALID) != 0;
|
||||
if (!available && (status->capabilities || hasOwner))
|
||||
return false;
|
||||
if (!keyboardLEDsValid && status->keyboardLEDs)
|
||||
return false;
|
||||
if (!available && keyboardLEDsValid)
|
||||
return false;
|
||||
if (available &&
|
||||
(status->capabilities &
|
||||
(KVMFR_INPUT_CAP_MOUSE_RELATIVE | KVMFR_INPUT_CAP_KEYBOARD)) !=
|
||||
@@ -714,10 +759,12 @@ static bool reconcileInputStream(LGMPInput * input,
|
||||
static void applyInputStatus(LGMPInput * input,
|
||||
const KVMFRInputStatus * status, uint32_t serial, bool * wake)
|
||||
{
|
||||
const bool wasValid = input->statusValid;
|
||||
const bool wasAvailable = input->available;
|
||||
const uint32_t oldCapabilities = input->capabilities;
|
||||
const uint32_t oldGeneration = input->endpointGeneration;
|
||||
const bool wasValid = input->statusValid;
|
||||
const bool wasAvailable = input->available;
|
||||
const uint32_t oldCapabilities = input->capabilities;
|
||||
const uint32_t oldGeneration = input->endpointGeneration;
|
||||
const bool oldKeyboardLEDsValid = input->keyboardLEDsValid;
|
||||
const uint8_t oldKeyboardLEDs = input->keyboardLEDs;
|
||||
const bool targetAvailable =
|
||||
(status->flags & KVMFR_INPUT_STATUS_AVAILABLE) != 0;
|
||||
const bool endpointChanged = wasValid &&
|
||||
@@ -730,6 +777,9 @@ static void applyInputStatus(LGMPInput * input,
|
||||
input->endpointGeneration = status->generation;
|
||||
input->statusOwnerClientID = status->ownerClientID;
|
||||
input->statusOwnerGeneration = status->ownerGeneration;
|
||||
input->keyboardLEDsValid =
|
||||
(status->flags & KVMFR_INPUT_STATUS_KEYBOARD_LEDS_VALID) != 0;
|
||||
input->keyboardLEDs = status->keyboardLEDs;
|
||||
const bool streamChanged = reconcileInputStream(input, status);
|
||||
const bool available = targetAvailable &&
|
||||
input->streamEndpointBound;
|
||||
@@ -773,7 +823,9 @@ static void applyInputStatus(LGMPInput * input,
|
||||
|
||||
if (!wasValid || wasAvailable != available ||
|
||||
oldCapabilities != status->capabilities ||
|
||||
oldGeneration != status->generation)
|
||||
oldGeneration != status->generation ||
|
||||
oldKeyboardLEDsValid != input->keyboardLEDsValid ||
|
||||
oldKeyboardLEDs != input->keyboardLEDs)
|
||||
input->notifyStatus = true;
|
||||
}
|
||||
|
||||
@@ -1112,6 +1164,8 @@ bool lgmpInput_connect(LGMPInput * input, uint32_t clientID)
|
||||
input->claimed = false;
|
||||
input->available = false;
|
||||
input->ownerBlocked = false;
|
||||
input->keyboardLEDsValid = false;
|
||||
input->keyboardLEDs = 0;
|
||||
input->pendingHead = 0;
|
||||
input->pendingCount = 0;
|
||||
input->clientID = clientID;
|
||||
@@ -1170,11 +1224,14 @@ void lgmpInput_disconnect(LGMPInput * input)
|
||||
input->pendingCount = 0;
|
||||
input->connected = false;
|
||||
input->claimed = false;
|
||||
if (input->available || input->endpointGeneration)
|
||||
if (input->available || input->endpointGeneration ||
|
||||
input->keyboardLEDsValid)
|
||||
input->notifyStatus = true;
|
||||
input->available = false;
|
||||
input->ownerBlocked = false;
|
||||
input->capabilities = 0;
|
||||
input->available = false;
|
||||
input->ownerBlocked = false;
|
||||
input->keyboardLEDsValid = false;
|
||||
input->keyboardLEDs = 0;
|
||||
input->capabilities = 0;
|
||||
input->streamEndpointBound = false;
|
||||
memset(&input->streamEndpoint, 0,
|
||||
sizeof(input->streamEndpoint));
|
||||
@@ -1196,6 +1253,8 @@ void lgmpInput_disconnect(LGMPInput * input)
|
||||
detachInputStream(input);
|
||||
input->available = false;
|
||||
input->ownerBlocked = false;
|
||||
input->keyboardLEDsValid = false;
|
||||
input->keyboardLEDs = 0;
|
||||
input->statusValid = false;
|
||||
input->ownerConfirmed = false;
|
||||
input->notifyStatus = false;
|
||||
|
||||
@@ -34,6 +34,8 @@ struct SpiceInput
|
||||
LG_Lock statusDispatch;
|
||||
|
||||
bool available;
|
||||
bool keyboardLEDsValid;
|
||||
uint8_t keyboardLEDs;
|
||||
uint32_t statusGeneration;
|
||||
LG_InputStatusFn statusCallback;
|
||||
void * statusOpaque;
|
||||
@@ -64,8 +66,10 @@ static void spiceSetStatusListener(void * opaque,
|
||||
input->statusOpaque = callbackOpaque;
|
||||
const LG_InputStatus status =
|
||||
{
|
||||
.available = input->available,
|
||||
.generation = input->statusGeneration,
|
||||
.available = input->available,
|
||||
.generation = input->statusGeneration,
|
||||
.keyboardLEDsValid = input->available && input->keyboardLEDsValid,
|
||||
.keyboardLEDs = input->keyboardLEDs,
|
||||
};
|
||||
LG_UNLOCK(input->stateLock);
|
||||
|
||||
@@ -193,19 +197,58 @@ void spiceInput_setAvailable(SpiceInput * input, bool available)
|
||||
LG_LOCK(input->statusDispatch);
|
||||
LG_LOCK(input->stateLock);
|
||||
|
||||
const bool oldLEDsValid =
|
||||
input->available && input->keyboardLEDsValid;
|
||||
const bool changed = input->available != available;
|
||||
if (changed)
|
||||
{
|
||||
input->available = available;
|
||||
input->statusGeneration = nextGeneration(input->statusGeneration);
|
||||
}
|
||||
if (!available)
|
||||
input->keyboardLEDsValid = false;
|
||||
|
||||
const bool statusChanged = changed || oldLEDsValid !=
|
||||
(input->available && input->keyboardLEDsValid);
|
||||
|
||||
const LG_InputStatusFn callback = input->statusCallback;
|
||||
void * callbackOpaque = input->statusOpaque;
|
||||
const LG_InputStatus status =
|
||||
{
|
||||
.available = input->available,
|
||||
.generation = input->statusGeneration,
|
||||
.available = input->available,
|
||||
.generation = input->statusGeneration,
|
||||
.keyboardLEDsValid = input->available && input->keyboardLEDsValid,
|
||||
.keyboardLEDs = input->keyboardLEDs,
|
||||
};
|
||||
LG_UNLOCK(input->stateLock);
|
||||
|
||||
if (statusChanged && callback)
|
||||
callback(callbackOpaque, &status);
|
||||
LG_UNLOCK(input->statusDispatch);
|
||||
}
|
||||
|
||||
void spiceInput_setKeyboardLEDs(SpiceInput * input, uint32_t modifiers)
|
||||
{
|
||||
const uint8_t keyboardLEDs =
|
||||
(modifiers & 2 ? LG_KEYBOARD_LED_NUM_LOCK : 0) |
|
||||
(modifiers & 4 ? LG_KEYBOARD_LED_CAPS_LOCK : 0) |
|
||||
(modifiers & 1 ? LG_KEYBOARD_LED_SCROLL_LOCK : 0);
|
||||
|
||||
LG_LOCK(input->statusDispatch);
|
||||
LG_LOCK(input->stateLock);
|
||||
const bool changed = !input->keyboardLEDsValid ||
|
||||
input->keyboardLEDs != keyboardLEDs;
|
||||
input->keyboardLEDsValid = true;
|
||||
input->keyboardLEDs = keyboardLEDs;
|
||||
|
||||
const LG_InputStatusFn callback = input->statusCallback;
|
||||
void * callbackOpaque = input->statusOpaque;
|
||||
const LG_InputStatus status =
|
||||
{
|
||||
.available = input->available,
|
||||
.generation = input->statusGeneration,
|
||||
.keyboardLEDsValid = input->available,
|
||||
.keyboardLEDs = input->keyboardLEDs,
|
||||
};
|
||||
LG_UNLOCK(input->stateLock);
|
||||
|
||||
|
||||
@@ -30,5 +30,6 @@ void spiceInput_free(SpiceInput ** input);
|
||||
|
||||
const LG_InputOps * spiceInput_getOps(void);
|
||||
void spiceInput_setAvailable(SpiceInput * input, bool available);
|
||||
void spiceInput_setKeyboardLEDs(SpiceInput * input, uint32_t modifiers);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -83,6 +83,13 @@ static void ready(void)
|
||||
lgSignalEvent(transport->connectEvent);
|
||||
}
|
||||
|
||||
static void keyModifiers(uint32_t modifiers)
|
||||
{
|
||||
LG_Transport * transport = callbackTarget();
|
||||
if (transport && transport->input)
|
||||
spiceInput_setKeyboardLEDs(transport->input, modifiers);
|
||||
}
|
||||
|
||||
static void surfaceCreate(unsigned int surfaceId, PSSurfaceFormat format,
|
||||
unsigned int width, unsigned int height)
|
||||
{
|
||||
@@ -258,8 +265,9 @@ int spiceSession_thread(void * opaque)
|
||||
.ready = ready,
|
||||
.inputs =
|
||||
{
|
||||
.enable = transport->inputEnabled,
|
||||
.autoConnect = true,
|
||||
.enable = transport->inputEnabled,
|
||||
.autoConnect = true,
|
||||
.keyModifiers = keyModifiers,
|
||||
},
|
||||
.clipboard =
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user