mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-22 07:01:30 +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"
|
#include "interface/input.h"
|
||||||
|
|
||||||
|
typedef void (*LGInputKeyboardLEDsFn)(void * opaque,
|
||||||
|
bool valid, uint8_t leds);
|
||||||
|
|
||||||
void lgInput_init(void);
|
void lgInput_init(void);
|
||||||
void lgInput_free(void);
|
void lgInput_free(void);
|
||||||
|
|
||||||
@@ -35,6 +38,11 @@ void lgInput_useTransport(bool enable);
|
|||||||
|
|
||||||
bool lgInput_available(void);
|
bool lgInput_available(void);
|
||||||
bool lgInput_supports(LG_InputSupport support);
|
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_keyDown(int key);
|
||||||
bool lgInput_keyUp(int key);
|
bool lgInput_keyUp(int key);
|
||||||
|
|||||||
@@ -30,10 +30,22 @@ typedef enum LG_InputSupport
|
|||||||
}
|
}
|
||||||
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
|
typedef struct LG_InputStatus
|
||||||
{
|
{
|
||||||
bool available;
|
bool available;
|
||||||
uint32_t generation;
|
uint32_t generation;
|
||||||
|
/* USB HID keyboard LED bits reported by the guest. */
|
||||||
|
bool keyboardLEDsValid;
|
||||||
|
uint8_t keyboardLEDs;
|
||||||
}
|
}
|
||||||
LG_InputStatus;
|
LG_InputStatus;
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ struct InputBinding
|
|||||||
void * opaque;
|
void * opaque;
|
||||||
bool available;
|
bool available;
|
||||||
bool mouseAbsolute;
|
bool mouseAbsolute;
|
||||||
|
bool keyboardLEDsValid;
|
||||||
|
uint8_t keyboardLEDs;
|
||||||
uint32_t epoch;
|
uint32_t epoch;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -39,8 +41,12 @@ static struct
|
|||||||
{
|
{
|
||||||
LG_Lock bindingLock;
|
LG_Lock bindingLock;
|
||||||
LG_RWLock activeLock;
|
LG_RWLock activeLock;
|
||||||
|
LG_Lock keyboardLEDsDispatch;
|
||||||
uint32_t nextBindingEpoch;
|
uint32_t nextBindingEpoch;
|
||||||
|
|
||||||
|
LGInputKeyboardLEDsFn keyboardLEDsCallback;
|
||||||
|
void * keyboardLEDsOpaque;
|
||||||
|
|
||||||
struct InputBinding fallback;
|
struct InputBinding fallback;
|
||||||
struct InputBinding transport;
|
struct InputBinding transport;
|
||||||
struct InputBinding active;
|
struct InputBinding active;
|
||||||
@@ -95,6 +101,31 @@ static bool bindingEqual(const struct InputBinding * a,
|
|||||||
a->epoch == b->epoch;
|
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)
|
static void releaseKeysNL(void)
|
||||||
{
|
{
|
||||||
for (int key = 0; key < KEY_MAX; ++key)
|
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);
|
atomic_store_explicit(&l_input.buttons, 0, memory_order_relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void updateActiveNL(bool dropActive)
|
static bool updateActiveNL(bool dropActive)
|
||||||
{
|
{
|
||||||
const struct InputBinding next =
|
const struct InputBinding next =
|
||||||
l_input.useTransport && l_input.transport.available ?
|
l_input.useTransport && l_input.transport.available ?
|
||||||
@@ -142,10 +173,15 @@ static void updateActiveNL(bool dropActive)
|
|||||||
|
|
||||||
if (bindingEqual(&next, &l_input.active))
|
if (bindingEqual(&next, &l_input.active))
|
||||||
{
|
{
|
||||||
|
const bool keyboardLEDsChanged =
|
||||||
|
!keyboardLEDsEqual(&next, &l_input.active);
|
||||||
l_input.active = next;
|
l_input.active = next;
|
||||||
return;
|
return keyboardLEDsChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bool keyboardLEDsChanged =
|
||||||
|
!keyboardLEDsEqual(&next, &l_input.active);
|
||||||
|
|
||||||
if (dropActive)
|
if (dropActive)
|
||||||
clearStateNL();
|
clearStateNL();
|
||||||
else
|
else
|
||||||
@@ -160,11 +196,13 @@ static void updateActiveNL(bool dropActive)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
DEBUG_INFO("Input is unavailable");
|
DEBUG_INFO("Input is unavailable");
|
||||||
|
return keyboardLEDsChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void updateStatusNL(struct InputBinding * binding,
|
static bool updateStatusNL(struct InputBinding * binding,
|
||||||
const LG_InputStatus * status)
|
const LG_InputStatus * status)
|
||||||
{
|
{
|
||||||
|
const struct InputBinding oldActive = l_input.active;
|
||||||
const bool wasActive = bindingEqual(&l_input.active, binding);
|
const bool wasActive = bindingEqual(&l_input.active, binding);
|
||||||
|
|
||||||
if (wasActive && !status->available)
|
if (wasActive && !status->available)
|
||||||
@@ -178,7 +216,11 @@ static void updateStatusNL(struct InputBinding * binding,
|
|||||||
binding->ops->mousePosition &&
|
binding->ops->mousePosition &&
|
||||||
binding->ops->supports(binding->opaque,
|
binding->ops->supports(binding->opaque,
|
||||||
LG_INPUT_SUPPORT_MOUSE_ABSOLUTE);
|
LG_INPUT_SUPPORT_MOUSE_ABSOLUTE);
|
||||||
|
binding->keyboardLEDsValid = status->available &&
|
||||||
|
status->keyboardLEDsValid;
|
||||||
|
binding->keyboardLEDs = status->keyboardLEDs;
|
||||||
updateActiveNL(false);
|
updateActiveNL(false);
|
||||||
|
return !keyboardLEDsEqual(&oldActive, &l_input.active);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fallbackStatusChanged(void * opaque,
|
static void fallbackStatusChanged(void * opaque,
|
||||||
@@ -188,10 +230,13 @@ static void fallbackStatusChanged(void * opaque,
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
||||||
|
bool keyboardLEDsChanged = false;
|
||||||
if (l_input.fallback.ops &&
|
if (l_input.fallback.ops &&
|
||||||
l_input.fallback.epoch == (uint32_t)(uintptr_t)opaque)
|
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);
|
LG_UNLOCK_EXCLUSIVE(l_input.activeLock);
|
||||||
|
if (keyboardLEDsChanged)
|
||||||
|
dispatchKeyboardLEDs();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void transportStatusChanged(void * opaque,
|
static void transportStatusChanged(void * opaque,
|
||||||
@@ -201,10 +246,13 @@ static void transportStatusChanged(void * opaque,
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
||||||
|
bool keyboardLEDsChanged = false;
|
||||||
if (l_input.transport.ops &&
|
if (l_input.transport.ops &&
|
||||||
l_input.transport.epoch == (uint32_t)(uintptr_t)opaque)
|
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);
|
LG_UNLOCK_EXCLUSIVE(l_input.activeLock);
|
||||||
|
if (keyboardLEDsChanged)
|
||||||
|
dispatchKeyboardLEDs();
|
||||||
}
|
}
|
||||||
|
|
||||||
void lgInput_init(void)
|
void lgInput_init(void)
|
||||||
@@ -220,6 +268,7 @@ void lgInput_init(void)
|
|||||||
|
|
||||||
LG_LOCK_INIT(l_input.bindingLock);
|
LG_LOCK_INIT(l_input.bindingLock);
|
||||||
LG_RWLOCK_INIT(l_input.activeLock);
|
LG_RWLOCK_INIT(l_input.activeLock);
|
||||||
|
LG_LOCK_INIT(l_input.keyboardLEDsDispatch);
|
||||||
}
|
}
|
||||||
|
|
||||||
void lgInput_free(void)
|
void lgInput_free(void)
|
||||||
@@ -228,6 +277,7 @@ void lgInput_free(void)
|
|||||||
struct InputBinding transport;
|
struct InputBinding transport;
|
||||||
|
|
||||||
LG_LOCK(l_input.bindingLock);
|
LG_LOCK(l_input.bindingLock);
|
||||||
|
LG_LOCK(l_input.keyboardLEDsDispatch);
|
||||||
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
||||||
resetActiveNL();
|
resetActiveNL();
|
||||||
fallback = l_input.fallback;
|
fallback = l_input.fallback;
|
||||||
@@ -235,7 +285,10 @@ void lgInput_free(void)
|
|||||||
l_input.active = (struct InputBinding) { 0 };
|
l_input.active = (struct InputBinding) { 0 };
|
||||||
l_input.fallback = (struct InputBinding) { 0 };
|
l_input.fallback = (struct InputBinding) { 0 };
|
||||||
l_input.transport = (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_EXCLUSIVE(l_input.activeLock);
|
||||||
|
LG_UNLOCK(l_input.keyboardLEDsDispatch);
|
||||||
|
|
||||||
if (fallback.ops && fallback.ops->setStatusListener)
|
if (fallback.ops && fallback.ops->setStatusListener)
|
||||||
fallback.ops->setStatusListener(fallback.opaque, NULL, NULL);
|
fallback.ops->setStatusListener(fallback.opaque, NULL, NULL);
|
||||||
@@ -244,6 +297,7 @@ void lgInput_free(void)
|
|||||||
|
|
||||||
LG_UNLOCK(l_input.bindingLock);
|
LG_UNLOCK(l_input.bindingLock);
|
||||||
LG_RWLOCK_FREE(l_input.activeLock);
|
LG_RWLOCK_FREE(l_input.activeLock);
|
||||||
|
LG_LOCK_FREE(l_input.keyboardLEDsDispatch);
|
||||||
LG_LOCK_FREE(l_input.bindingLock);
|
LG_LOCK_FREE(l_input.bindingLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,9 +324,12 @@ static void setBinding(struct InputBinding * target,
|
|||||||
const struct InputBinding next = makeBinding(ops, opaque);
|
const struct InputBinding next = makeBinding(ops, opaque);
|
||||||
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
||||||
*target = next;
|
*target = next;
|
||||||
updateActiveNL(false);
|
const bool keyboardLEDsChanged = updateActiveNL(false);
|
||||||
LG_UNLOCK_EXCLUSIVE(l_input.activeLock);
|
LG_UNLOCK_EXCLUSIVE(l_input.activeLock);
|
||||||
|
|
||||||
|
if (keyboardLEDsChanged)
|
||||||
|
dispatchKeyboardLEDs();
|
||||||
|
|
||||||
if (next.ops && next.ops->setStatusListener)
|
if (next.ops && next.ops->setStatusListener)
|
||||||
next.ops->setStatusListener(next.opaque, statusFn,
|
next.ops->setStatusListener(next.opaque, statusFn,
|
||||||
(void *)(uintptr_t)next.epoch);
|
(void *)(uintptr_t)next.epoch);
|
||||||
@@ -285,8 +342,10 @@ static void dropBinding(struct InputBinding * target)
|
|||||||
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
||||||
const bool wasActive = bindingEqual(&l_input.active, target);
|
const bool wasActive = bindingEqual(&l_input.active, target);
|
||||||
*target = (struct InputBinding) { 0 };
|
*target = (struct InputBinding) { 0 };
|
||||||
updateActiveNL(wasActive);
|
const bool keyboardLEDsChanged = updateActiveNL(wasActive);
|
||||||
LG_UNLOCK_EXCLUSIVE(l_input.activeLock);
|
LG_UNLOCK_EXCLUSIVE(l_input.activeLock);
|
||||||
|
if (keyboardLEDsChanged)
|
||||||
|
dispatchKeyboardLEDs();
|
||||||
LG_UNLOCK(l_input.bindingLock);
|
LG_UNLOCK(l_input.bindingLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,8 +373,10 @@ void lgInput_useTransport(bool enable)
|
|||||||
{
|
{
|
||||||
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
||||||
l_input.useTransport = enable;
|
l_input.useTransport = enable;
|
||||||
updateActiveNL(false);
|
const bool keyboardLEDsChanged = updateActiveNL(false);
|
||||||
LG_UNLOCK_EXCLUSIVE(l_input.activeLock);
|
LG_UNLOCK_EXCLUSIVE(l_input.activeLock);
|
||||||
|
if (keyboardLEDsChanged)
|
||||||
|
dispatchKeyboardLEDs();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool lgInput_available(void)
|
bool lgInput_available(void)
|
||||||
@@ -344,6 +405,24 @@ bool lgInput_supports(LG_InputSupport support)
|
|||||||
return result;
|
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)
|
bool lgInput_keyDown(int key)
|
||||||
{
|
{
|
||||||
if (key < 0 || key >= KEY_MAX)
|
if (key < 0 || key >= KEY_MAX)
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ extern const LG_TransportOps LGT_SPICE;
|
|||||||
struct SpiceInput
|
struct SpiceInput
|
||||||
{
|
{
|
||||||
bool available;
|
bool available;
|
||||||
|
uint32_t keyModifiers;
|
||||||
unsigned int onN;
|
unsigned int onN;
|
||||||
unsigned int offN;
|
unsigned int offN;
|
||||||
unsigned int onOrder;
|
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)
|
bool spiceClipboard_init(SpiceClipboard ** clipboard)
|
||||||
{
|
{
|
||||||
*clipboard = calloc(1, sizeof(**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_MIN (INT16_MIN * INPUT_MAX_SPLIT_REPORTS)
|
||||||
#define INPUT_MOUSE_DELTA_MAX (INT16_MAX * 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
|
enum LGMPInputMouseMode
|
||||||
{
|
{
|
||||||
LGMP_INPUT_MOUSE_NONE,
|
LGMP_INPUT_MOUSE_NONE,
|
||||||
@@ -104,6 +116,8 @@ struct LGMPInput
|
|||||||
bool claimed;
|
bool claimed;
|
||||||
bool available;
|
bool available;
|
||||||
bool ownerBlocked;
|
bool ownerBlocked;
|
||||||
|
bool keyboardLEDsValid;
|
||||||
|
uint8_t keyboardLEDs;
|
||||||
|
|
||||||
uint32_t generation;
|
uint32_t generation;
|
||||||
uint32_t sequence;
|
uint32_t sequence;
|
||||||
@@ -161,6 +175,8 @@ static LG_InputStatus inputStatus(const LGMPInput * input)
|
|||||||
{
|
{
|
||||||
.available = input->available,
|
.available = input->available,
|
||||||
.generation = input->endpointGeneration,
|
.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;
|
++input->stats.counters.terminalFailures;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input->available || input->endpointGeneration)
|
if (input->available || input->endpointGeneration ||
|
||||||
|
input->keyboardLEDsValid)
|
||||||
input->notifyStatus = true;
|
input->notifyStatus = true;
|
||||||
input->connected = false;
|
input->connected = false;
|
||||||
input->claimed = false;
|
input->claimed = false;
|
||||||
input->available = false;
|
input->available = false;
|
||||||
input->ownerBlocked = false;
|
input->ownerBlocked = false;
|
||||||
|
input->keyboardLEDsValid = false;
|
||||||
|
input->keyboardLEDs = 0;
|
||||||
input->pendingHead = 0;
|
input->pendingHead = 0;
|
||||||
input->pendingCount = 0;
|
input->pendingCount = 0;
|
||||||
input->mouseMode = LGMP_INPUT_MOUSE_NONE;
|
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_RELATIVE |
|
||||||
KVMFR_INPUT_CAP_MOUSE_ABSOLUTE |
|
KVMFR_INPUT_CAP_MOUSE_ABSOLUTE |
|
||||||
KVMFR_INPUT_CAP_KEYBOARD;
|
KVMFR_INPUT_CAP_KEYBOARD;
|
||||||
static const uint32_t flags =
|
static const uint32_t commonFlags =
|
||||||
KVMFR_INPUT_STATUS_AVAILABLE |
|
KVMFR_INPUT_STATUS_AVAILABLE |
|
||||||
KVMFR_INPUT_STATUS_HAS_OWNER;
|
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->capabilities & ~capabilities ||
|
||||||
status->flags & ~flags ||
|
status->flags & ~flags ||
|
||||||
!status->generation ||
|
!status->generation ||
|
||||||
@@ -567,6 +600,12 @@ static bool validInputStatus(const KVMFRInputStatus * status)
|
|||||||
!status->streamGeneration)
|
!status->streamGeneration)
|
||||||
return false;
|
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) /
|
for (size_t i = 0; i < sizeof(status->streamReserved) /
|
||||||
sizeof(status->streamReserved[0]); ++i)
|
sizeof(status->streamReserved[0]); ++i)
|
||||||
if (status->streamReserved[i])
|
if (status->streamReserved[i])
|
||||||
@@ -617,8 +656,14 @@ static bool validInputStatus(const KVMFRInputStatus * status)
|
|||||||
(status->flags & KVMFR_INPUT_STATUS_AVAILABLE) != 0;
|
(status->flags & KVMFR_INPUT_STATUS_AVAILABLE) != 0;
|
||||||
const bool hasOwner =
|
const bool hasOwner =
|
||||||
(status->flags & KVMFR_INPUT_STATUS_HAS_OWNER) != 0;
|
(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))
|
if (!available && (status->capabilities || hasOwner))
|
||||||
return false;
|
return false;
|
||||||
|
if (!keyboardLEDsValid && status->keyboardLEDs)
|
||||||
|
return false;
|
||||||
|
if (!available && keyboardLEDsValid)
|
||||||
|
return false;
|
||||||
if (available &&
|
if (available &&
|
||||||
(status->capabilities &
|
(status->capabilities &
|
||||||
(KVMFR_INPUT_CAP_MOUSE_RELATIVE | KVMFR_INPUT_CAP_KEYBOARD)) !=
|
(KVMFR_INPUT_CAP_MOUSE_RELATIVE | KVMFR_INPUT_CAP_KEYBOARD)) !=
|
||||||
@@ -718,6 +763,8 @@ static void applyInputStatus(LGMPInput * input,
|
|||||||
const bool wasAvailable = input->available;
|
const bool wasAvailable = input->available;
|
||||||
const uint32_t oldCapabilities = input->capabilities;
|
const uint32_t oldCapabilities = input->capabilities;
|
||||||
const uint32_t oldGeneration = input->endpointGeneration;
|
const uint32_t oldGeneration = input->endpointGeneration;
|
||||||
|
const bool oldKeyboardLEDsValid = input->keyboardLEDsValid;
|
||||||
|
const uint8_t oldKeyboardLEDs = input->keyboardLEDs;
|
||||||
const bool targetAvailable =
|
const bool targetAvailable =
|
||||||
(status->flags & KVMFR_INPUT_STATUS_AVAILABLE) != 0;
|
(status->flags & KVMFR_INPUT_STATUS_AVAILABLE) != 0;
|
||||||
const bool endpointChanged = wasValid &&
|
const bool endpointChanged = wasValid &&
|
||||||
@@ -730,6 +777,9 @@ static void applyInputStatus(LGMPInput * input,
|
|||||||
input->endpointGeneration = status->generation;
|
input->endpointGeneration = status->generation;
|
||||||
input->statusOwnerClientID = status->ownerClientID;
|
input->statusOwnerClientID = status->ownerClientID;
|
||||||
input->statusOwnerGeneration = status->ownerGeneration;
|
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 streamChanged = reconcileInputStream(input, status);
|
||||||
const bool available = targetAvailable &&
|
const bool available = targetAvailable &&
|
||||||
input->streamEndpointBound;
|
input->streamEndpointBound;
|
||||||
@@ -773,7 +823,9 @@ static void applyInputStatus(LGMPInput * input,
|
|||||||
|
|
||||||
if (!wasValid || wasAvailable != available ||
|
if (!wasValid || wasAvailable != available ||
|
||||||
oldCapabilities != status->capabilities ||
|
oldCapabilities != status->capabilities ||
|
||||||
oldGeneration != status->generation)
|
oldGeneration != status->generation ||
|
||||||
|
oldKeyboardLEDsValid != input->keyboardLEDsValid ||
|
||||||
|
oldKeyboardLEDs != input->keyboardLEDs)
|
||||||
input->notifyStatus = true;
|
input->notifyStatus = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1112,6 +1164,8 @@ bool lgmpInput_connect(LGMPInput * input, uint32_t clientID)
|
|||||||
input->claimed = false;
|
input->claimed = false;
|
||||||
input->available = false;
|
input->available = false;
|
||||||
input->ownerBlocked = false;
|
input->ownerBlocked = false;
|
||||||
|
input->keyboardLEDsValid = false;
|
||||||
|
input->keyboardLEDs = 0;
|
||||||
input->pendingHead = 0;
|
input->pendingHead = 0;
|
||||||
input->pendingCount = 0;
|
input->pendingCount = 0;
|
||||||
input->clientID = clientID;
|
input->clientID = clientID;
|
||||||
@@ -1170,10 +1224,13 @@ void lgmpInput_disconnect(LGMPInput * input)
|
|||||||
input->pendingCount = 0;
|
input->pendingCount = 0;
|
||||||
input->connected = false;
|
input->connected = false;
|
||||||
input->claimed = false;
|
input->claimed = false;
|
||||||
if (input->available || input->endpointGeneration)
|
if (input->available || input->endpointGeneration ||
|
||||||
|
input->keyboardLEDsValid)
|
||||||
input->notifyStatus = true;
|
input->notifyStatus = true;
|
||||||
input->available = false;
|
input->available = false;
|
||||||
input->ownerBlocked = false;
|
input->ownerBlocked = false;
|
||||||
|
input->keyboardLEDsValid = false;
|
||||||
|
input->keyboardLEDs = 0;
|
||||||
input->capabilities = 0;
|
input->capabilities = 0;
|
||||||
input->streamEndpointBound = false;
|
input->streamEndpointBound = false;
|
||||||
memset(&input->streamEndpoint, 0,
|
memset(&input->streamEndpoint, 0,
|
||||||
@@ -1196,6 +1253,8 @@ void lgmpInput_disconnect(LGMPInput * input)
|
|||||||
detachInputStream(input);
|
detachInputStream(input);
|
||||||
input->available = false;
|
input->available = false;
|
||||||
input->ownerBlocked = false;
|
input->ownerBlocked = false;
|
||||||
|
input->keyboardLEDsValid = false;
|
||||||
|
input->keyboardLEDs = 0;
|
||||||
input->statusValid = false;
|
input->statusValid = false;
|
||||||
input->ownerConfirmed = false;
|
input->ownerConfirmed = false;
|
||||||
input->notifyStatus = false;
|
input->notifyStatus = false;
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ struct SpiceInput
|
|||||||
LG_Lock statusDispatch;
|
LG_Lock statusDispatch;
|
||||||
|
|
||||||
bool available;
|
bool available;
|
||||||
|
bool keyboardLEDsValid;
|
||||||
|
uint8_t keyboardLEDs;
|
||||||
uint32_t statusGeneration;
|
uint32_t statusGeneration;
|
||||||
LG_InputStatusFn statusCallback;
|
LG_InputStatusFn statusCallback;
|
||||||
void * statusOpaque;
|
void * statusOpaque;
|
||||||
@@ -66,6 +68,8 @@ static void spiceSetStatusListener(void * opaque,
|
|||||||
{
|
{
|
||||||
.available = input->available,
|
.available = input->available,
|
||||||
.generation = input->statusGeneration,
|
.generation = input->statusGeneration,
|
||||||
|
.keyboardLEDsValid = input->available && input->keyboardLEDsValid,
|
||||||
|
.keyboardLEDs = input->keyboardLEDs,
|
||||||
};
|
};
|
||||||
LG_UNLOCK(input->stateLock);
|
LG_UNLOCK(input->stateLock);
|
||||||
|
|
||||||
@@ -193,12 +197,19 @@ void spiceInput_setAvailable(SpiceInput * input, bool available)
|
|||||||
LG_LOCK(input->statusDispatch);
|
LG_LOCK(input->statusDispatch);
|
||||||
LG_LOCK(input->stateLock);
|
LG_LOCK(input->stateLock);
|
||||||
|
|
||||||
|
const bool oldLEDsValid =
|
||||||
|
input->available && input->keyboardLEDsValid;
|
||||||
const bool changed = input->available != available;
|
const bool changed = input->available != available;
|
||||||
if (changed)
|
if (changed)
|
||||||
{
|
{
|
||||||
input->available = available;
|
input->available = available;
|
||||||
input->statusGeneration = nextGeneration(input->statusGeneration);
|
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;
|
const LG_InputStatusFn callback = input->statusCallback;
|
||||||
void * callbackOpaque = input->statusOpaque;
|
void * callbackOpaque = input->statusOpaque;
|
||||||
@@ -206,6 +217,38 @@ void spiceInput_setAvailable(SpiceInput * input, bool available)
|
|||||||
{
|
{
|
||||||
.available = input->available,
|
.available = input->available,
|
||||||
.generation = input->statusGeneration,
|
.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);
|
LG_UNLOCK(input->stateLock);
|
||||||
|
|
||||||
|
|||||||
@@ -30,5 +30,6 @@ void spiceInput_free(SpiceInput ** input);
|
|||||||
|
|
||||||
const LG_InputOps * spiceInput_getOps(void);
|
const LG_InputOps * spiceInput_getOps(void);
|
||||||
void spiceInput_setAvailable(SpiceInput * input, bool available);
|
void spiceInput_setAvailable(SpiceInput * input, bool available);
|
||||||
|
void spiceInput_setKeyboardLEDs(SpiceInput * input, uint32_t modifiers);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -83,6 +83,13 @@ static void ready(void)
|
|||||||
lgSignalEvent(transport->connectEvent);
|
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,
|
static void surfaceCreate(unsigned int surfaceId, PSSurfaceFormat format,
|
||||||
unsigned int width, unsigned int height)
|
unsigned int width, unsigned int height)
|
||||||
{
|
{
|
||||||
@@ -260,6 +267,7 @@ int spiceSession_thread(void * opaque)
|
|||||||
{
|
{
|
||||||
.enable = transport->inputEnabled,
|
.enable = transport->inputEnabled,
|
||||||
.autoConnect = true,
|
.autoConnect = true,
|
||||||
|
.keyModifiers = keyModifiers,
|
||||||
},
|
},
|
||||||
.clipboard =
|
.clipboard =
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -28,7 +28,8 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#define KVMFR_INPUT_VERSION 2
|
#define KVMFR_INPUT_VERSION 3
|
||||||
|
#define KVMFR_INPUT_KEYBOARD_LEDS_VERSION 3
|
||||||
#define KVMFR_INPUT_STREAM_VERSION 1
|
#define KVMFR_INPUT_STREAM_VERSION 1
|
||||||
#define KVMFR_INPUT_STREAM_ENDPOINT_COUNT 8
|
#define KVMFR_INPUT_STREAM_ENDPOINT_COUNT 8
|
||||||
#define KVMFR_INPUT_STREAM_SLOT_COUNT 128
|
#define KVMFR_INPUT_STREAM_SLOT_COUNT 128
|
||||||
@@ -120,10 +121,22 @@ enum
|
|||||||
|
|
||||||
typedef uint32_t KVMFRInputCapabilityFlags;
|
typedef uint32_t KVMFRInputCapabilityFlags;
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
KVMFR_INPUT_KEYBOARD_LED_NUM_LOCK = 0x1,
|
||||||
|
KVMFR_INPUT_KEYBOARD_LED_CAPS_LOCK = 0x2,
|
||||||
|
KVMFR_INPUT_KEYBOARD_LED_SCROLL_LOCK = 0x4,
|
||||||
|
KVMFR_INPUT_KEYBOARD_LED_COMPOSE = 0x8,
|
||||||
|
KVMFR_INPUT_KEYBOARD_LED_KANA = 0x10,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef uint8_t KVMFRInputKeyboardLEDFlags;
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
KVMFR_INPUT_STATUS_AVAILABLE = 0x1,
|
KVMFR_INPUT_STATUS_AVAILABLE = 0x1,
|
||||||
KVMFR_INPUT_STATUS_HAS_OWNER = 0x2,
|
KVMFR_INPUT_STATUS_HAS_OWNER = 0x2,
|
||||||
|
KVMFR_INPUT_STATUS_KEYBOARD_LEDS_VALID = 0x4,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef uint32_t KVMFRInputStatusFlags;
|
typedef uint32_t KVMFRInputStatusFlags;
|
||||||
@@ -169,7 +182,10 @@ typedef struct KVMFRInputStatus
|
|||||||
uint32_t streamVersion;
|
uint32_t streamVersion;
|
||||||
uint32_t streamEndpointCount;
|
uint32_t streamEndpointCount;
|
||||||
uint32_t streamGeneration;
|
uint32_t streamGeneration;
|
||||||
uint32_t streamReserved[5];
|
// USB HID keyboard LED bits; valid only when the status flag is set.
|
||||||
|
uint8_t keyboardLEDs;
|
||||||
|
uint8_t statusReserved[3];
|
||||||
|
uint32_t streamReserved[4];
|
||||||
KVMFRInputStreamEndpoint streamEndpoint[
|
KVMFRInputStreamEndpoint streamEndpoint[
|
||||||
KVMFR_INPUT_STREAM_ENDPOINT_COUNT];
|
KVMFR_INPUT_STREAM_ENDPOINT_COUNT];
|
||||||
}
|
}
|
||||||
@@ -192,6 +208,8 @@ static_assert(sizeof(KVMFRInputStreamEndpoint) == 48,
|
|||||||
"KVMFR input stream endpoint layout changed");
|
"KVMFR input stream endpoint layout changed");
|
||||||
static_assert(offsetof(KVMFRInputStatus, streamVersion) == 32,
|
static_assert(offsetof(KVMFRInputStatus, streamVersion) == 32,
|
||||||
"KVMFR input status stream layout changed");
|
"KVMFR input status stream layout changed");
|
||||||
|
static_assert(offsetof(KVMFRInputStatus, keyboardLEDs) == 44,
|
||||||
|
"KVMFR input status keyboard LED layout changed");
|
||||||
static_assert(offsetof(KVMFRInputStatus, streamEndpoint) == 64,
|
static_assert(offsetof(KVMFRInputStatus, streamEndpoint) == 64,
|
||||||
"KVMFR input stream discovery layout changed");
|
"KVMFR input stream discovery layout changed");
|
||||||
static_assert(sizeof(KVMFRInputStatus) == 448,
|
static_assert(sizeof(KVMFRInputStatus) == 448,
|
||||||
@@ -215,6 +233,8 @@ _Static_assert(sizeof(KVMFRInputStreamEndpoint) == 48,
|
|||||||
"KVMFR input stream endpoint layout changed");
|
"KVMFR input stream endpoint layout changed");
|
||||||
_Static_assert(offsetof(KVMFRInputStatus, streamVersion) == 32,
|
_Static_assert(offsetof(KVMFRInputStatus, streamVersion) == 32,
|
||||||
"KVMFR input status stream layout changed");
|
"KVMFR input status stream layout changed");
|
||||||
|
_Static_assert(offsetof(KVMFRInputStatus, keyboardLEDs) == 44,
|
||||||
|
"KVMFR input status keyboard LED layout changed");
|
||||||
_Static_assert(offsetof(KVMFRInputStatus, streamEndpoint) == 64,
|
_Static_assert(offsetof(KVMFRInputStatus, streamEndpoint) == 64,
|
||||||
"KVMFR input stream discovery layout changed");
|
"KVMFR input stream discovery layout changed");
|
||||||
_Static_assert(sizeof(KVMFRInputStatus) == 448,
|
_Static_assert(sizeof(KVMFRInputStatus) == 448,
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ static constexpr wchar_t LG_INPUT_PIPE_NAME[] =
|
|||||||
L"\\\\.\\pipe\\LookingGlassIDDInput";
|
L"\\\\.\\pipe\\LookingGlassIDDInput";
|
||||||
|
|
||||||
static constexpr uint32_t LG_INPUT_PIPE_MAGIC = 0x5049474c;
|
static constexpr uint32_t LG_INPUT_PIPE_MAGIC = 0x5049474c;
|
||||||
static constexpr uint16_t LG_INPUT_PIPE_VERSION = 3;
|
static constexpr uint16_t LG_INPUT_PIPE_VERSION = 4;
|
||||||
static constexpr size_t LG_INPUT_PIPE_MAX_PAYLOAD_SIZE = 64;
|
static constexpr size_t LG_INPUT_PIPE_MAX_PAYLOAD_SIZE = 64;
|
||||||
static constexpr uint16_t LG_INPUT_MOUSE_ABSOLUTE_MAX =
|
static constexpr uint16_t LG_INPUT_MOUSE_ABSOLUTE_MAX =
|
||||||
KVMFR_INPUT_MOUSE_ABSOLUTE_MAX;
|
KVMFR_INPUT_MOUSE_ABSOLUTE_MAX;
|
||||||
@@ -67,9 +67,12 @@ enum LGInputKeyboardModifier : uint8_t
|
|||||||
|
|
||||||
enum LGInputPipeMessageType : uint16_t
|
enum LGInputPipeMessageType : uint16_t
|
||||||
{
|
{
|
||||||
|
// LGIdd to LGInput.
|
||||||
LG_INPUT_PIPE_MESSAGE_MOUSE_ABSOLUTE = 1,
|
LG_INPUT_PIPE_MESSAGE_MOUSE_ABSOLUTE = 1,
|
||||||
LG_INPUT_PIPE_MESSAGE_MOUSE_RELATIVE = 2,
|
LG_INPUT_PIPE_MESSAGE_MOUSE_RELATIVE = 2,
|
||||||
LG_INPUT_PIPE_MESSAGE_KEYBOARD = 3,
|
LG_INPUT_PIPE_MESSAGE_KEYBOARD = 3,
|
||||||
|
// LGInput to LGIdd.
|
||||||
|
LG_INPUT_PIPE_MESSAGE_KEYBOARD_LEDS = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
@@ -88,11 +91,18 @@ using LGInputPipeMouseRelative = KVMFRInputMouseRelative;
|
|||||||
using LGInputPipeMouseAbsolute = KVMFRInputMouseAbsolute;
|
using LGInputPipeMouseAbsolute = KVMFRInputMouseAbsolute;
|
||||||
using LGInputPipeKeyboard = KVMFRInputKeyboard;
|
using LGInputPipeKeyboard = KVMFRInputKeyboard;
|
||||||
|
|
||||||
|
struct LGInputPipeKeyboardLEDs
|
||||||
|
{
|
||||||
|
KVMFRInputKeyboardLEDFlags leds;
|
||||||
|
};
|
||||||
|
|
||||||
static_assert(sizeof(LGInputPipeMouseRelative) == 16,
|
static_assert(sizeof(LGInputPipeMouseRelative) == 16,
|
||||||
"LGInputPipeMouseRelative wire layout changed");
|
"LGInputPipeMouseRelative wire layout changed");
|
||||||
static_assert(sizeof(LGInputPipeMouseAbsolute) == 16,
|
static_assert(sizeof(LGInputPipeMouseAbsolute) == 16,
|
||||||
"LGInputPipeMouseAbsolute wire layout changed");
|
"LGInputPipeMouseAbsolute wire layout changed");
|
||||||
static_assert(sizeof(LGInputPipeKeyboard) == 16,
|
static_assert(sizeof(LGInputPipeKeyboard) == 16,
|
||||||
"LGInputPipeKeyboard wire layout changed");
|
"LGInputPipeKeyboard wire layout changed");
|
||||||
|
static_assert(sizeof(LGInputPipeKeyboardLEDs) == 1,
|
||||||
|
"LGInputPipeKeyboardLEDs wire layout changed");
|
||||||
static_assert(sizeof(LGInputPipeMessage) == 84,
|
static_assert(sizeof(LGInputPipeMessage) == 84,
|
||||||
"LGInputPipeMessage wire layout changed");
|
"LGInputPipeMessage wire layout changed");
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ public:
|
|||||||
|
|
||||||
// Odd states are available; a state change invalidates in-flight input.
|
// Odd states are available; a state change invalidates in-flight input.
|
||||||
virtual uint64_t GetState() const = 0;
|
virtual uint64_t GetState() const = 0;
|
||||||
|
// Returns false until the guest endpoint has reported its HID LED state.
|
||||||
|
virtual bool GetKeyboardLEDs(uint8_t& leds) const = 0;
|
||||||
virtual bool SendMouseRelative(int32_t deltaX, int32_t deltaY,
|
virtual bool SendMouseRelative(int32_t deltaX, int32_t deltaY,
|
||||||
int32_t wheel, uint32_t buttons) = 0;
|
int32_t wheel, uint32_t buttons) = 0;
|
||||||
virtual bool SendMouseAbsolute(uint16_t x, uint16_t y,
|
virtual bool SendMouseAbsolute(uint16_t x, uint16_t y,
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ bool CInputPipeServer::Init()
|
|||||||
DeInit();
|
DeInit();
|
||||||
|
|
||||||
Atomic::Store(m_state, 0, std::memory_order_release);
|
Atomic::Store(m_state, 0, std::memory_order_release);
|
||||||
|
Atomic::Store(m_keyboardLEDs, 0, std::memory_order_release);
|
||||||
|
Atomic::Store(m_keyboardLEDsValid, false, std::memory_order_release);
|
||||||
m_performanceFrequency.QuadPart = 0;
|
m_performanceFrequency.QuadPart = 0;
|
||||||
if (!QueryPerformanceFrequency(&m_performanceFrequency))
|
if (!QueryPerformanceFrequency(&m_performanceFrequency))
|
||||||
m_performanceFrequency.QuadPart = 0;
|
m_performanceFrequency.QuadPart = 0;
|
||||||
@@ -551,6 +553,7 @@ void CInputPipeServer::Invalidate(uint64_t state, bool requireMatch)
|
|||||||
CSRWExclusiveLock queueLock(m_queueLock);
|
CSRWExclusiveLock queueLock(m_queueLock);
|
||||||
m_queueHead = 0;
|
m_queueHead = 0;
|
||||||
m_queueCount = 0;
|
m_queueCount = 0;
|
||||||
|
Atomic::Store(m_keyboardLEDsValid, false, std::memory_order_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD WINAPI CInputPipeServer::ThreadProc(void * context)
|
DWORD WINAPI CInputPipeServer::ThreadProc(void * context)
|
||||||
@@ -597,6 +600,8 @@ void CInputPipeServer::OnPipeConnected()
|
|||||||
++state;
|
++state;
|
||||||
++state;
|
++state;
|
||||||
m_sequence = 0;
|
m_sequence = 0;
|
||||||
|
m_feedbackSequence = 0;
|
||||||
|
Atomic::Store(m_keyboardLEDsValid, false, std::memory_order_release);
|
||||||
|
|
||||||
m_queueHead = 0;
|
m_queueHead = 0;
|
||||||
m_queueCount = 0;
|
m_queueCount = 0;
|
||||||
@@ -623,8 +628,40 @@ bool CInputPipeServer::OnPipeMessage(
|
|||||||
const void * message,
|
const void * message,
|
||||||
size_t size)
|
size_t size)
|
||||||
{
|
{
|
||||||
UNREFERENCED_PARAMETER(message);
|
if (size != sizeof(LGInputPipeMessage))
|
||||||
UNREFERENCED_PARAMETER(size);
|
{
|
||||||
DEBUG_WARN("LGInput sent an unexpected message");
|
DEBUG_WARN("Received a malformed LGInput feedback message");
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const LGInputPipeMessage& frame =
|
||||||
|
*static_cast<const LGInputPipeMessage *>(message);
|
||||||
|
if (frame.magic != LG_INPUT_PIPE_MAGIC ||
|
||||||
|
frame.version != LG_INPUT_PIPE_VERSION ||
|
||||||
|
frame.type != LG_INPUT_PIPE_MESSAGE_KEYBOARD_LEDS ||
|
||||||
|
frame.payloadSize != sizeof(LGInputPipeKeyboardLEDs) ||
|
||||||
|
!frame.sequence || frame.sequence != m_feedbackSequence + 1)
|
||||||
|
{
|
||||||
|
DEBUG_WARN("Received a malformed LGInput feedback message");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LGInputPipeKeyboardLEDs feedback = {};
|
||||||
|
memcpy(&feedback, frame.payload, sizeof(feedback));
|
||||||
|
const uint8_t mask =
|
||||||
|
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 (feedback.leds & ~mask)
|
||||||
|
{
|
||||||
|
DEBUG_WARN("Received invalid LGInput keyboard LEDs");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_feedbackSequence = frame.sequence;
|
||||||
|
Atomic::Store(m_keyboardLEDs, feedback.leds, std::memory_order_release);
|
||||||
|
Atomic::Store(m_keyboardLEDsValid, true, std::memory_order_release);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,10 @@ private:
|
|||||||
size_t m_queueHead = 0;
|
size_t m_queueHead = 0;
|
||||||
size_t m_queueCount = 0;
|
size_t m_queueCount = 0;
|
||||||
uint64_t m_sequence = 0;
|
uint64_t m_sequence = 0;
|
||||||
|
uint64_t m_feedbackSequence = 0;
|
||||||
|
|
||||||
|
std::atomic<uint8_t> m_keyboardLEDs { 0 };
|
||||||
|
std::atomic<bool> m_keyboardLEDsValid { false };
|
||||||
|
|
||||||
MouseMode m_mouseMode = MouseMode::NONE;
|
MouseMode m_mouseMode = MouseMode::NONE;
|
||||||
uint16_t m_absoluteX = 0;
|
uint16_t m_absoluteX = 0;
|
||||||
@@ -118,6 +122,14 @@ public:
|
|||||||
return Atomic::Load(m_state, std::memory_order_acquire);
|
return Atomic::Load(m_state, std::memory_order_acquire);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GetKeyboardLEDs(uint8_t& leds) const override
|
||||||
|
{
|
||||||
|
if (!Atomic::Load(m_keyboardLEDsValid, std::memory_order_acquire))
|
||||||
|
return false;
|
||||||
|
leds = Atomic::Load(m_keyboardLEDs, std::memory_order_acquire);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool SendMouseRelative(
|
bool SendMouseRelative(
|
||||||
_In_ int32_t deltaX,
|
_In_ int32_t deltaX,
|
||||||
_In_ int32_t deltaY,
|
_In_ int32_t deltaY,
|
||||||
|
|||||||
@@ -551,6 +551,8 @@ InputTargetState CInputHub::GetState(const SourceKey& source)
|
|||||||
return result;
|
return result;
|
||||||
if (!BindingValid(source))
|
if (!BindingValid(source))
|
||||||
return result;
|
return result;
|
||||||
|
result.keyboardLEDsValid = m_sink->GetKeyboardLEDs(
|
||||||
|
result.keyboardLEDs);
|
||||||
if (!m_owner.backend)
|
if (!m_owner.backend)
|
||||||
result.available = true;
|
result.available = true;
|
||||||
else if (source.client && source.generation && m_owner == source)
|
else if (source.client && source.generation && m_owner == source)
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ struct InputTargetState
|
|||||||
InputSourceId owner;
|
InputSourceId owner;
|
||||||
bool available = false;
|
bool available = false;
|
||||||
bool owned = false;
|
bool owned = false;
|
||||||
|
bool keyboardLEDsValid = false;
|
||||||
|
uint8_t keyboardLEDs = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class InputResult
|
enum class InputResult
|
||||||
|
|||||||
@@ -333,6 +333,8 @@ void CLGMPInputTransport::UpdateTargetState(
|
|||||||
if (state.state == m_targetState.state &&
|
if (state.state == m_targetState.state &&
|
||||||
state.available == m_targetState.available &&
|
state.available == m_targetState.available &&
|
||||||
state.owned == m_targetState.owned &&
|
state.owned == m_targetState.owned &&
|
||||||
|
state.keyboardLEDsValid == m_targetState.keyboardLEDsValid &&
|
||||||
|
state.keyboardLEDs == m_targetState.keyboardLEDs &&
|
||||||
state.owner.client == m_targetState.owner.client &&
|
state.owner.client == m_targetState.owner.client &&
|
||||||
state.owner.generation == m_targetState.owner.generation)
|
state.owner.generation == m_targetState.owner.generation)
|
||||||
return;
|
return;
|
||||||
@@ -389,6 +391,11 @@ bool CLGMPInputTransport::PublishStatus()
|
|||||||
status.ownerClientID = m_targetState.owner.client;
|
status.ownerClientID = m_targetState.owner.client;
|
||||||
status.ownerGeneration = m_targetState.owner.generation;
|
status.ownerGeneration = m_targetState.owner.generation;
|
||||||
}
|
}
|
||||||
|
if (available && m_targetState.keyboardLEDsValid)
|
||||||
|
{
|
||||||
|
status.flags |= KVMFR_INPUT_STATUS_KEYBOARD_LEDS_VALID;
|
||||||
|
status.keyboardLEDs = m_targetState.keyboardLEDs;
|
||||||
|
}
|
||||||
status.generation = m_endpointGeneration;
|
status.generation = m_endpointGeneration;
|
||||||
status.lease = static_cast<uint32_t>(OWNER_LEASE_MS);
|
status.lease = static_cast<uint32_t>(OWNER_LEASE_MS);
|
||||||
status.maxButtons = KVMFR_INPUT_MOUSE_BUTTON_COUNT;
|
status.maxButtons = KVMFR_INPUT_MOUSE_BUTTON_COUNT;
|
||||||
|
|||||||
@@ -165,7 +165,10 @@ static NTSTATUS SetOutputReport(
|
|||||||
return STATUS_INVALID_PARAMETER;
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
WDFDEVICE device = WdfIoQueueGetDevice(queue);
|
WDFDEVICE device = WdfIoQueueGetDevice(queue);
|
||||||
HIDGetDeviceContext(device)->keyboardLeds = report->leds;
|
HIDDeviceContext * context = HIDGetDeviceContext(device);
|
||||||
|
context->keyboardLeds = report->leds;
|
||||||
|
if (context->inputPipe)
|
||||||
|
context->inputPipe->UpdateKeyboardLEDs(report->leds);
|
||||||
WdfRequestSetInformation(request, sizeof(*report));
|
WdfRequestSetInformation(request, sizeof(*report));
|
||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,15 @@ static constexpr uint16_t HID_CONSUMER_USAGE_VOLUME_DOWN = 0xea;
|
|||||||
|
|
||||||
bool CInputPipeClient::Start()
|
bool CInputPipeClient::Start()
|
||||||
{
|
{
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_feedbackLock);
|
||||||
|
m_feedbackReady = false;
|
||||||
|
}
|
||||||
m_endpoint.Stop();
|
m_endpoint.Stop();
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_feedbackLock);
|
||||||
|
m_feedbackSequence = 0;
|
||||||
|
}
|
||||||
m_lastSequence = 0;
|
m_lastSequence = 0;
|
||||||
m_statReceived = 0;
|
m_statReceived = 0;
|
||||||
m_statMalformed = 0;
|
m_statMalformed = 0;
|
||||||
@@ -55,6 +63,10 @@ bool CInputPipeClient::Start()
|
|||||||
void CInputPipeClient::Stop()
|
void CInputPipeClient::Stop()
|
||||||
{
|
{
|
||||||
const bool wasRunning = m_endpoint.IsRunning();
|
const bool wasRunning = m_endpoint.IsRunning();
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_feedbackLock);
|
||||||
|
m_feedbackReady = false;
|
||||||
|
}
|
||||||
m_endpoint.Stop();
|
m_endpoint.Stop();
|
||||||
m_lastSequence = 0;
|
m_lastSequence = 0;
|
||||||
if (wasRunning)
|
if (wasRunning)
|
||||||
@@ -67,11 +79,22 @@ void CInputPipeClient::Stop()
|
|||||||
void CInputPipeClient::OnPipeConnected()
|
void CInputPipeClient::OnPipeConnected()
|
||||||
{
|
{
|
||||||
m_lastSequence = 0;
|
m_lastSequence = 0;
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_feedbackLock);
|
||||||
|
m_feedbackSequence = 0;
|
||||||
|
m_feedbackReady = SendKeyboardLEDsLocked();
|
||||||
|
if (!m_feedbackReady)
|
||||||
|
DEBUG_WARN("Failed to report LGInput keyboard LEDs");
|
||||||
|
}
|
||||||
DEBUG_INFO("Connected to the LGIdd input transport");
|
DEBUG_INFO("Connected to the LGIdd input transport");
|
||||||
}
|
}
|
||||||
|
|
||||||
void CInputPipeClient::OnPipeDisconnected()
|
void CInputPipeClient::OnPipeDisconnected()
|
||||||
{
|
{
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_feedbackLock);
|
||||||
|
m_feedbackReady = false;
|
||||||
|
}
|
||||||
m_lastSequence = 0;
|
m_lastSequence = 0;
|
||||||
LogStatistics(true);
|
LogStatistics(true);
|
||||||
CHIDDevice::ResetReports();
|
CHIDDevice::ResetReports();
|
||||||
@@ -319,6 +342,46 @@ bool CInputPipeClient::SubmitReport(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CInputPipeClient::UpdateKeyboardLEDs(uint8_t leds)
|
||||||
|
{
|
||||||
|
const uint8_t mask =
|
||||||
|
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;
|
||||||
|
leds &= mask;
|
||||||
|
|
||||||
|
CSRWExclusiveLock lock(m_feedbackLock);
|
||||||
|
if (m_keyboardLEDs == leds)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_keyboardLEDs = leds;
|
||||||
|
if (m_feedbackReady && !SendKeyboardLEDsLocked())
|
||||||
|
{
|
||||||
|
m_feedbackReady = false;
|
||||||
|
DEBUG_WARN("Failed to report LGInput keyboard LEDs");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CInputPipeClient::SendKeyboardLEDsLocked()
|
||||||
|
{
|
||||||
|
LGInputPipeMessage message = {};
|
||||||
|
message.magic = LG_INPUT_PIPE_MAGIC;
|
||||||
|
message.version = LG_INPUT_PIPE_VERSION;
|
||||||
|
message.type = LG_INPUT_PIPE_MESSAGE_KEYBOARD_LEDS;
|
||||||
|
message.payloadSize = sizeof(LGInputPipeKeyboardLEDs);
|
||||||
|
message.sequence = ++m_feedbackSequence;
|
||||||
|
|
||||||
|
const LGInputPipeKeyboardLEDs feedback = { m_keyboardLEDs };
|
||||||
|
memcpy(message.payload, &feedback, sizeof(feedback));
|
||||||
|
if (m_endpoint.Send(&message, sizeof(message)))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
--m_feedbackSequence;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void CInputPipeClient::LogStatistics(bool force)
|
void CInputPipeClient::LogStatistics(bool force)
|
||||||
{
|
{
|
||||||
const ULONGLONG now = GetTickCount64();
|
const ULONGLONG now = GetTickCount64();
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "CPipeEndpoint.h"
|
#include "CPipeEndpoint.h"
|
||||||
|
#include "CSRWLock.h"
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -32,6 +33,7 @@ public:
|
|||||||
|
|
||||||
bool Start();
|
bool Start();
|
||||||
void Stop();
|
void Stop();
|
||||||
|
void UpdateKeyboardLEDs(uint8_t leds);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr ULONGLONG STATISTICS_INTERVAL_MS = 5000;
|
static constexpr ULONGLONG STATISTICS_INTERVAL_MS = 5000;
|
||||||
@@ -43,9 +45,14 @@ private:
|
|||||||
bool HandleMouseAbsolute(const void * payload, size_t size);
|
bool HandleMouseAbsolute(const void * payload, size_t size);
|
||||||
bool HandleKeyboard(const void * payload, size_t size);
|
bool HandleKeyboard(const void * payload, size_t size);
|
||||||
bool SubmitReport(const void * report, size_t size);
|
bool SubmitReport(const void * report, size_t size);
|
||||||
|
bool SendKeyboardLEDsLocked();
|
||||||
void LogStatistics(bool force);
|
void LogStatistics(bool force);
|
||||||
|
|
||||||
CPipeEndpoint m_endpoint;
|
CPipeEndpoint m_endpoint;
|
||||||
|
CSRWLock m_feedbackLock;
|
||||||
|
bool m_feedbackReady = false;
|
||||||
|
uint64_t m_feedbackSequence = 0;
|
||||||
|
uint8_t m_keyboardLEDs = 0;
|
||||||
uint64_t m_lastSequence = 0;
|
uint64_t m_lastSequence = 0;
|
||||||
uint64_t m_statReceived = 0;
|
uint64_t m_statReceived = 0;
|
||||||
uint64_t m_statMalformed = 0;
|
uint64_t m_statMalformed = 0;
|
||||||
|
|||||||
Submodule repos/PureSpice updated: 13695607de...4e8583a437
Reference in New Issue
Block a user