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:
@@ -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