mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-09 08:41:31 +00:00
[client/idd] input: report endpoint status
This commit is contained in:
@@ -30,6 +30,16 @@ typedef enum LG_InputSupport
|
|||||||
}
|
}
|
||||||
LG_InputSupport;
|
LG_InputSupport;
|
||||||
|
|
||||||
|
typedef struct LG_InputStatus
|
||||||
|
{
|
||||||
|
bool available;
|
||||||
|
uint32_t generation;
|
||||||
|
}
|
||||||
|
LG_InputStatus;
|
||||||
|
|
||||||
|
typedef void (*LG_InputStatusFn)(void * opaque,
|
||||||
|
const LG_InputStatus * status);
|
||||||
|
|
||||||
typedef struct LG_InputOps
|
typedef struct LG_InputOps
|
||||||
{
|
{
|
||||||
const char * name;
|
const char * name;
|
||||||
@@ -37,6 +47,11 @@ typedef struct LG_InputOps
|
|||||||
/* Operations must fail safely if a remote endpoint disappears. */
|
/* Operations must fail safely if a remote endpoint disappears. */
|
||||||
bool (*supports)(void * opaque, LG_InputSupport support);
|
bool (*supports)(void * opaque, LG_InputSupport support);
|
||||||
|
|
||||||
|
/* Registration must synchronously report the current status after releasing
|
||||||
|
* any backend locks. Passing NULL unregisters the listener. */
|
||||||
|
void (*setStatusListener)(void * opaque, LG_InputStatusFn callback,
|
||||||
|
void * callbackOpaque);
|
||||||
|
|
||||||
/* Keyboard codes use the Linux input-event KEY_* values. */
|
/* Keyboard codes use the Linux input-event KEY_* values. */
|
||||||
bool (*keyDown)(void * opaque, int key);
|
bool (*keyDown)(void * opaque, int key);
|
||||||
bool (*keyUp)(void * opaque, int key);
|
bool (*keyUp)(void * opaque, int key);
|
||||||
|
|||||||
@@ -30,7 +30,9 @@ struct InputBinding
|
|||||||
{
|
{
|
||||||
const LG_InputOps * ops;
|
const LG_InputOps * ops;
|
||||||
void * opaque;
|
void * opaque;
|
||||||
|
bool available;
|
||||||
bool mouseAbsolute;
|
bool mouseAbsolute;
|
||||||
|
uint32_t generation;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct
|
static struct
|
||||||
@@ -63,12 +65,15 @@ static bool validOps(const LG_InputOps * ops)
|
|||||||
static struct InputBinding makeBinding(const LG_InputOps * ops,
|
static struct InputBinding makeBinding(const LG_InputOps * ops,
|
||||||
void * opaque)
|
void * opaque)
|
||||||
{
|
{
|
||||||
|
const bool available = ops && !ops->setStatusListener;
|
||||||
return (struct InputBinding)
|
return (struct InputBinding)
|
||||||
{
|
{
|
||||||
.ops = ops,
|
.ops = ops,
|
||||||
.opaque = opaque,
|
.opaque = opaque,
|
||||||
.mouseAbsolute = ops && ops->mousePosition &&
|
.available = available,
|
||||||
|
.mouseAbsolute = available && ops->mousePosition &&
|
||||||
ops->supports(opaque, LG_INPUT_SUPPORT_MOUSE_ABSOLUTE),
|
ops->supports(opaque, LG_INPUT_SUPPORT_MOUSE_ABSOLUTE),
|
||||||
|
.generation = 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,12 +117,17 @@ static void clearStateNL(void)
|
|||||||
static void updateActiveNL(void)
|
static void updateActiveNL(void)
|
||||||
{
|
{
|
||||||
const struct InputBinding next =
|
const struct InputBinding next =
|
||||||
l_input.useTransport && l_input.transport.ops ?
|
l_input.useTransport && l_input.transport.available ?
|
||||||
l_input.transport : l_input.fallback;
|
l_input.transport :
|
||||||
|
l_input.fallback.available ? l_input.fallback :
|
||||||
|
(struct InputBinding) { 0 };
|
||||||
|
|
||||||
if (next.ops == l_input.active.ops &&
|
if (next.ops == l_input.active.ops &&
|
||||||
next.opaque == l_input.active.opaque)
|
next.opaque == l_input.active.opaque)
|
||||||
|
{
|
||||||
|
l_input.active = next;
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
resetActiveNL();
|
resetActiveNL();
|
||||||
l_input.active = next;
|
l_input.active = next;
|
||||||
@@ -132,6 +142,52 @@ static void updateActiveNL(void)
|
|||||||
DEBUG_INFO("Input is unavailable");
|
DEBUG_INFO("Input is unavailable");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void updateStatusNL(struct InputBinding * binding,
|
||||||
|
const LG_InputStatus * status)
|
||||||
|
{
|
||||||
|
const bool wasActive =
|
||||||
|
l_input.active.ops == binding->ops &&
|
||||||
|
l_input.active.opaque == binding->opaque;
|
||||||
|
|
||||||
|
if (wasActive && !status->available)
|
||||||
|
{
|
||||||
|
resetActiveNL();
|
||||||
|
l_input.active = (struct InputBinding) { 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
binding->available = status->available;
|
||||||
|
binding->mouseAbsolute = status->available &&
|
||||||
|
binding->ops->mousePosition &&
|
||||||
|
binding->ops->supports(binding->opaque,
|
||||||
|
LG_INPUT_SUPPORT_MOUSE_ABSOLUTE);
|
||||||
|
binding->generation = status->generation;
|
||||||
|
updateActiveNL();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fallbackStatusChanged(void * opaque,
|
||||||
|
const LG_InputStatus * status)
|
||||||
|
{
|
||||||
|
if (!status)
|
||||||
|
return;
|
||||||
|
|
||||||
|
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
||||||
|
if (l_input.fallback.ops && l_input.fallback.opaque == opaque)
|
||||||
|
updateStatusNL(&l_input.fallback, status);
|
||||||
|
LG_UNLOCK_EXCLUSIVE(l_input.activeLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void transportStatusChanged(void * opaque,
|
||||||
|
const LG_InputStatus * status)
|
||||||
|
{
|
||||||
|
if (!status)
|
||||||
|
return;
|
||||||
|
|
||||||
|
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
||||||
|
if (l_input.transport.ops && l_input.transport.opaque == opaque)
|
||||||
|
updateStatusNL(&l_input.transport, status);
|
||||||
|
LG_UNLOCK_EXCLUSIVE(l_input.activeLock);
|
||||||
|
}
|
||||||
|
|
||||||
void lgInput_init(void)
|
void lgInput_init(void)
|
||||||
{
|
{
|
||||||
l_input.fallback = (struct InputBinding) { 0 };
|
l_input.fallback = (struct InputBinding) { 0 };
|
||||||
@@ -148,12 +204,23 @@ void lgInput_init(void)
|
|||||||
|
|
||||||
void lgInput_free(void)
|
void lgInput_free(void)
|
||||||
{
|
{
|
||||||
|
struct InputBinding fallback;
|
||||||
|
struct InputBinding transport;
|
||||||
|
|
||||||
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
||||||
resetActiveNL();
|
resetActiveNL();
|
||||||
|
fallback = l_input.fallback;
|
||||||
|
transport = l_input.transport;
|
||||||
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 };
|
||||||
LG_UNLOCK_EXCLUSIVE(l_input.activeLock);
|
LG_UNLOCK_EXCLUSIVE(l_input.activeLock);
|
||||||
|
|
||||||
|
if (fallback.ops && fallback.ops->setStatusListener)
|
||||||
|
fallback.ops->setStatusListener(fallback.opaque, NULL, NULL);
|
||||||
|
if (transport.ops && transport.ops->setStatusListener)
|
||||||
|
transport.ops->setStatusListener(transport.opaque, NULL, NULL);
|
||||||
|
|
||||||
LG_RWLOCK_FREE(l_input.activeLock);
|
LG_RWLOCK_FREE(l_input.activeLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,10 +233,20 @@ void lgInput_setFallback(const LG_InputOps * ops, void * opaque)
|
|||||||
opaque = NULL;
|
opaque = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const struct InputBinding next = makeBinding(ops, opaque);
|
||||||
|
struct InputBinding old;
|
||||||
|
|
||||||
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
||||||
l_input.fallback = makeBinding(ops, opaque);
|
old = l_input.fallback;
|
||||||
|
l_input.fallback = next;
|
||||||
updateActiveNL();
|
updateActiveNL();
|
||||||
LG_UNLOCK_EXCLUSIVE(l_input.activeLock);
|
LG_UNLOCK_EXCLUSIVE(l_input.activeLock);
|
||||||
|
|
||||||
|
if (old.ops && old.ops->setStatusListener)
|
||||||
|
old.ops->setStatusListener(old.opaque, NULL, NULL);
|
||||||
|
if (next.ops && next.ops->setStatusListener)
|
||||||
|
next.ops->setStatusListener(next.opaque,
|
||||||
|
fallbackStatusChanged, next.opaque);
|
||||||
}
|
}
|
||||||
|
|
||||||
void lgInput_setTransport(const LG_InputOps * ops, void * opaque)
|
void lgInput_setTransport(const LG_InputOps * ops, void * opaque)
|
||||||
@@ -181,15 +258,28 @@ void lgInput_setTransport(const LG_InputOps * ops, void * opaque)
|
|||||||
opaque = NULL;
|
opaque = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const struct InputBinding next = makeBinding(ops, opaque);
|
||||||
|
struct InputBinding old;
|
||||||
|
|
||||||
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
||||||
l_input.transport = makeBinding(ops, opaque);
|
old = l_input.transport;
|
||||||
|
l_input.transport = next;
|
||||||
updateActiveNL();
|
updateActiveNL();
|
||||||
LG_UNLOCK_EXCLUSIVE(l_input.activeLock);
|
LG_UNLOCK_EXCLUSIVE(l_input.activeLock);
|
||||||
|
|
||||||
|
if (old.ops && old.ops->setStatusListener)
|
||||||
|
old.ops->setStatusListener(old.opaque, NULL, NULL);
|
||||||
|
if (next.ops && next.ops->setStatusListener)
|
||||||
|
next.ops->setStatusListener(next.opaque,
|
||||||
|
transportStatusChanged, next.opaque);
|
||||||
}
|
}
|
||||||
|
|
||||||
void lgInput_dropTransport(void)
|
void lgInput_dropTransport(void)
|
||||||
{
|
{
|
||||||
|
struct InputBinding old;
|
||||||
|
|
||||||
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
||||||
|
old = l_input.transport;
|
||||||
if (l_input.useTransport && l_input.active.ops == l_input.transport.ops &&
|
if (l_input.useTransport && l_input.active.ops == l_input.transport.ops &&
|
||||||
l_input.active.opaque == l_input.transport.opaque)
|
l_input.active.opaque == l_input.transport.opaque)
|
||||||
{
|
{
|
||||||
@@ -200,6 +290,9 @@ void lgInput_dropTransport(void)
|
|||||||
l_input.transport = (struct InputBinding) { 0 };
|
l_input.transport = (struct InputBinding) { 0 };
|
||||||
updateActiveNL();
|
updateActiveNL();
|
||||||
LG_UNLOCK_EXCLUSIVE(l_input.activeLock);
|
LG_UNLOCK_EXCLUSIVE(l_input.activeLock);
|
||||||
|
|
||||||
|
if (old.ops && old.ops->setStatusListener)
|
||||||
|
old.ops->setStatusListener(old.opaque, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void lgInput_useTransport(bool enable)
|
void lgInput_useTransport(bool enable)
|
||||||
|
|||||||
@@ -2362,7 +2362,7 @@ restart:
|
|||||||
g_state.transportOps->getInputOps(g_state.transport, &inputOpaque) : NULL;
|
g_state.transportOps->getInputOps(g_state.transport, &inputOpaque) : NULL;
|
||||||
lgInput_setTransport(inputOps, inputOpaque);
|
lgInput_setTransport(inputOps, inputOpaque);
|
||||||
|
|
||||||
if (lgInput_available())
|
if (inputOps || lgInput_available())
|
||||||
keybind_inputRegister();
|
keybind_inputRegister();
|
||||||
checkUUID();
|
checkUUID();
|
||||||
DEBUG_INFO("Starting session");
|
DEBUG_INFO("Starting session");
|
||||||
|
|||||||
@@ -42,7 +42,7 @@
|
|||||||
#define INPUT_KEEPALIVE_US 200000
|
#define INPUT_KEEPALIVE_US 200000
|
||||||
#define INPUT_IDLE_RELEASE_US 400000
|
#define INPUT_IDLE_RELEASE_US 400000
|
||||||
#define INPUT_RELEASE_TIMEOUT_US 50000
|
#define INPUT_RELEASE_TIMEOUT_US 50000
|
||||||
#define INPUT_WORKER_IDLE_MS 50
|
#define INPUT_WORKER_IDLE_MS 10
|
||||||
#define INPUT_WORKER_RETRY_MS 1
|
#define INPUT_WORKER_RETRY_MS 1
|
||||||
#define INPUT_MAX_SPLIT_REPORTS 4
|
#define INPUT_MAX_SPLIT_REPORTS 4
|
||||||
#define INPUT_MOUSE_DELTA_MIN (INT16_MIN * INPUT_MAX_SPLIT_REPORTS)
|
#define INPUT_MOUSE_DELTA_MIN (INT16_MIN * INPUT_MAX_SPLIT_REPORTS)
|
||||||
@@ -71,6 +71,8 @@ struct LGMPInput
|
|||||||
atomic_bool stop;
|
atomic_bool stop;
|
||||||
bool connected;
|
bool connected;
|
||||||
bool claimed;
|
bool claimed;
|
||||||
|
bool available;
|
||||||
|
bool ownerBlocked;
|
||||||
|
|
||||||
uint32_t generation;
|
uint32_t generation;
|
||||||
uint32_t sequence;
|
uint32_t sequence;
|
||||||
@@ -89,8 +91,27 @@ struct LGMPInput
|
|||||||
uint16_t absoluteY;
|
uint16_t absoluteY;
|
||||||
uint8_t keyState[KVMFR_INPUT_KEYBOARD_USAGE_MAX + 1];
|
uint8_t keyState[KVMFR_INPUT_KEYBOARD_USAGE_MAX + 1];
|
||||||
uint64_t lastInput;
|
uint64_t lastInput;
|
||||||
|
|
||||||
|
uint32_t clientID;
|
||||||
|
uint32_t capabilities;
|
||||||
|
uint32_t endpointGeneration;
|
||||||
|
uint32_t statusSerial;
|
||||||
|
uint32_t statusOwnerClientID;
|
||||||
|
uint32_t statusOwnerGeneration;
|
||||||
|
bool ownerConfirmed;
|
||||||
|
bool statusValid;
|
||||||
|
bool notifyStatus;
|
||||||
|
|
||||||
|
LG_InputStatusFn statusCallback;
|
||||||
|
void * statusOpaque;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void buildKeyboardPayload(const LGMPInput * input,
|
||||||
|
KVMFRInputPayload * payload);
|
||||||
|
static bool queueMouse(LGMPInput * input, enum LGMPInputMouseMode mode,
|
||||||
|
int32_t x, int32_t y, int32_t wheel, uint32_t buttons,
|
||||||
|
bool pureMotion, bool * wake);
|
||||||
|
|
||||||
static struct LGMPInputPending * pendingAt(
|
static struct LGMPInputPending * pendingAt(
|
||||||
LGMPInput * input, unsigned position)
|
LGMPInput * input, unsigned position)
|
||||||
{
|
{
|
||||||
@@ -98,6 +119,38 @@ static struct LGMPInputPending * pendingAt(
|
|||||||
(input->pendingHead + position) % INPUT_PENDING_LENGTH];
|
(input->pendingHead + position) % INPUT_PENDING_LENGTH];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static LG_InputStatus inputStatus(const LGMPInput * input)
|
||||||
|
{
|
||||||
|
return (LG_InputStatus)
|
||||||
|
{
|
||||||
|
.available = input->available,
|
||||||
|
.generation = input->endpointGeneration,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static void notifyInputStatus(LGMPInput * input)
|
||||||
|
{
|
||||||
|
LG_InputStatusFn callback;
|
||||||
|
void * opaque;
|
||||||
|
LG_InputStatus status;
|
||||||
|
|
||||||
|
LG_LOCK(input->lock);
|
||||||
|
if (!input->notifyStatus)
|
||||||
|
{
|
||||||
|
LG_UNLOCK(input->lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
input->notifyStatus = false;
|
||||||
|
callback = input->statusCallback;
|
||||||
|
opaque = input->statusOpaque;
|
||||||
|
status = inputStatus(input);
|
||||||
|
LG_UNLOCK(input->lock);
|
||||||
|
|
||||||
|
if (callback)
|
||||||
|
callback(opaque, &status);
|
||||||
|
}
|
||||||
|
|
||||||
static void published(LGMPInput * input,
|
static void published(LGMPInput * input,
|
||||||
const KVMFRInputMessage * message)
|
const KVMFRInputMessage * message)
|
||||||
{
|
{
|
||||||
@@ -122,14 +175,21 @@ static void connectionFailed(LGMPInput * input, LGMP_STATUS status)
|
|||||||
if (input->connected)
|
if (input->connected)
|
||||||
DEBUG_WARN("LGMP input transport failed: %s", lgmpStatusString(status));
|
DEBUG_WARN("LGMP input transport failed: %s", lgmpStatusString(status));
|
||||||
|
|
||||||
|
if (input->available || input->endpointGeneration)
|
||||||
|
input->notifyStatus = true;
|
||||||
input->connected = false;
|
input->connected = false;
|
||||||
input->claimed = false;
|
input->claimed = false;
|
||||||
|
input->available = false;
|
||||||
|
input->ownerBlocked = false;
|
||||||
input->pendingHead = 0;
|
input->pendingHead = 0;
|
||||||
input->pendingCount = 0;
|
input->pendingCount = 0;
|
||||||
input->mouseMode = LGMP_INPUT_MOUSE_NONE;
|
input->mouseMode = LGMP_INPUT_MOUSE_NONE;
|
||||||
input->mouseButtons = 0;
|
input->mouseButtons = 0;
|
||||||
input->publishedClaimed = false;
|
input->publishedClaimed = false;
|
||||||
input->lastInput = 0;
|
input->lastInput = 0;
|
||||||
|
input->capabilities = 0;
|
||||||
|
input->statusValid = false;
|
||||||
|
input->ownerConfirmed = false;
|
||||||
memset(input->keyState, 0, sizeof(input->keyState));
|
memset(input->keyState, 0, sizeof(input->keyState));
|
||||||
atomic_store_explicit(&input->stop, true, memory_order_release);
|
atomic_store_explicit(&input->stop, true, memory_order_release);
|
||||||
}
|
}
|
||||||
@@ -276,11 +336,14 @@ static bool claim(LGMPInput * input, bool * wake)
|
|||||||
{
|
{
|
||||||
if (input->claimed)
|
if (input->claimed)
|
||||||
return true;
|
return true;
|
||||||
|
if (!input->available || input->ownerBlocked)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (++input->generation == 0)
|
if (++input->generation == 0)
|
||||||
++input->generation;
|
++input->generation;
|
||||||
input->sequence = 0;
|
input->sequence = 0;
|
||||||
input->claimed = true;
|
input->claimed = true;
|
||||||
|
input->ownerConfirmed = false;
|
||||||
|
|
||||||
const KVMFRInputPayload payload = { 0 };
|
const KVMFRInputPayload payload = { 0 };
|
||||||
if (queuePayload(input, KVMFR_INPUT_MESSAGE_CLAIM,
|
if (queuePayload(input, KVMFR_INPUT_MESSAGE_CLAIM,
|
||||||
@@ -312,6 +375,187 @@ static bool inputStateHeld(const LGMPInput * input)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool inputStateActive(const LGMPInput * input)
|
||||||
|
{
|
||||||
|
return input->mouseMode == LGMP_INPUT_MOUSE_ABSOLUTE ||
|
||||||
|
inputStateHeld(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void discardProtocolState(LGMPInput * input)
|
||||||
|
{
|
||||||
|
input->pendingHead = 0;
|
||||||
|
input->pendingCount = 0;
|
||||||
|
input->claimed = false;
|
||||||
|
input->sequence = 0;
|
||||||
|
input->publishedGeneration = 0;
|
||||||
|
input->publishedSequence = 0;
|
||||||
|
input->publishedClaimed = false;
|
||||||
|
input->ownerConfirmed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool restoreInputState(LGMPInput * input, bool * wake)
|
||||||
|
{
|
||||||
|
if (!inputStateActive(input))
|
||||||
|
return true;
|
||||||
|
if (!claim(input, wake))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
KVMFRInputPayload keyboard = { 0 };
|
||||||
|
buildKeyboardPayload(input, &keyboard);
|
||||||
|
if (!queuePayload(input, KVMFR_INPUT_MESSAGE_KEYBOARD,
|
||||||
|
&keyboard, false, wake))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (input->mouseMode != LGMP_INPUT_MOUSE_NONE &&
|
||||||
|
!queueMouse(input, input->mouseMode, 0, 0, 0,
|
||||||
|
input->mouseButtons, false, wake))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool validInputStatus(const KVMFRInputStatus * status)
|
||||||
|
{
|
||||||
|
static const uint32_t capabilities =
|
||||||
|
KVMFR_INPUT_CAP_MOUSE_RELATIVE |
|
||||||
|
KVMFR_INPUT_CAP_MOUSE_ABSOLUTE |
|
||||||
|
KVMFR_INPUT_CAP_KEYBOARD;
|
||||||
|
static const uint32_t flags =
|
||||||
|
KVMFR_INPUT_STATUS_AVAILABLE |
|
||||||
|
KVMFR_INPUT_STATUS_HAS_OWNER;
|
||||||
|
|
||||||
|
if (status->version != KVMFR_INPUT_VERSION ||
|
||||||
|
status->capabilities & ~capabilities ||
|
||||||
|
status->flags & ~flags || !status->generation ||
|
||||||
|
!status->lease || !status->maxButtons ||
|
||||||
|
status->maxButtons > KVMFR_INPUT_MOUSE_BUTTON_COUNT)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const bool available =
|
||||||
|
(status->flags & KVMFR_INPUT_STATUS_AVAILABLE) != 0;
|
||||||
|
const bool hasOwner =
|
||||||
|
(status->flags & KVMFR_INPUT_STATUS_HAS_OWNER) != 0;
|
||||||
|
if (!available && (status->capabilities || hasOwner))
|
||||||
|
return false;
|
||||||
|
if (available &&
|
||||||
|
(status->capabilities &
|
||||||
|
(KVMFR_INPUT_CAP_MOUSE_RELATIVE | KVMFR_INPUT_CAP_KEYBOARD)) !=
|
||||||
|
(KVMFR_INPUT_CAP_MOUSE_RELATIVE | KVMFR_INPUT_CAP_KEYBOARD))
|
||||||
|
return false;
|
||||||
|
return hasOwner ?
|
||||||
|
status->ownerClientID && status->ownerGeneration :
|
||||||
|
!status->ownerClientID && !status->ownerGeneration;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 available =
|
||||||
|
(status->flags & KVMFR_INPUT_STATUS_AVAILABLE) != 0;
|
||||||
|
const bool endpointChanged = wasValid &&
|
||||||
|
oldGeneration != status->generation;
|
||||||
|
bool restore = false;
|
||||||
|
|
||||||
|
input->statusValid = true;
|
||||||
|
input->statusSerial = serial;
|
||||||
|
input->available = available;
|
||||||
|
input->capabilities = status->capabilities;
|
||||||
|
input->endpointGeneration = status->generation;
|
||||||
|
input->statusOwnerClientID = status->ownerClientID;
|
||||||
|
input->statusOwnerGeneration = status->ownerGeneration;
|
||||||
|
|
||||||
|
if (endpointChanged || !available)
|
||||||
|
{
|
||||||
|
discardProtocolState(input);
|
||||||
|
restore = endpointChanged && available;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status->flags & KVMFR_INPUT_STATUS_HAS_OWNER)
|
||||||
|
{
|
||||||
|
if (input->claimed &&
|
||||||
|
status->ownerClientID == input->clientID &&
|
||||||
|
status->ownerGeneration == input->generation)
|
||||||
|
{
|
||||||
|
input->ownerBlocked = false;
|
||||||
|
input->ownerConfirmed = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
discardProtocolState(input);
|
||||||
|
input->ownerBlocked = true;
|
||||||
|
restore = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (input->ownerBlocked ||
|
||||||
|
(input->claimed && input->ownerConfirmed))
|
||||||
|
{
|
||||||
|
discardProtocolState(input);
|
||||||
|
restore = available;
|
||||||
|
}
|
||||||
|
input->ownerBlocked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (restore && !restoreInputState(input, wake))
|
||||||
|
discardProtocolState(input);
|
||||||
|
|
||||||
|
if (!wasValid || wasAvailable != available ||
|
||||||
|
oldCapabilities != status->capabilities ||
|
||||||
|
oldGeneration != status->generation)
|
||||||
|
input->notifyStatus = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void processInputStatus(LGMPInput * input, bool * wake)
|
||||||
|
{
|
||||||
|
LGMP_STATUS result = lgmpClientAdvanceToLast(input->queue);
|
||||||
|
if (result == LGMP_ERR_QUEUE_EMPTY)
|
||||||
|
return;
|
||||||
|
if (result != LGMP_OK)
|
||||||
|
{
|
||||||
|
connectionFailed(input, result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LGMPMessage message;
|
||||||
|
result = lgmpClientProcess(input->queue, &message);
|
||||||
|
if (result == LGMP_ERR_QUEUE_EMPTY)
|
||||||
|
return;
|
||||||
|
if (result != LGMP_OK)
|
||||||
|
{
|
||||||
|
connectionFailed(input, result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
KVMFRInputStatus status = { 0 };
|
||||||
|
const bool valid = message.udata && message.udata <= UINT32_MAX &&
|
||||||
|
message.size == sizeof(status);
|
||||||
|
if (valid)
|
||||||
|
memcpy(&status, message.mem, sizeof(status));
|
||||||
|
|
||||||
|
result = lgmpClientMessageDone(input->queue);
|
||||||
|
if (result != LGMP_OK)
|
||||||
|
{
|
||||||
|
connectionFailed(input, result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint32_t serial = (uint32_t)message.udata;
|
||||||
|
if (!valid || !validInputStatus(&status))
|
||||||
|
{
|
||||||
|
DEBUG_WARN("Ignoring invalid LGMP input status");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (input->statusValid &&
|
||||||
|
(int32_t)(serial - input->statusSerial) <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
applyInputStatus(input, &status, serial, wake);
|
||||||
|
}
|
||||||
|
|
||||||
static bool release(LGMPInput * input, bool * wake)
|
static bool release(LGMPInput * input, bool * wake)
|
||||||
{
|
{
|
||||||
input->pendingHead = 0;
|
input->pendingHead = 0;
|
||||||
@@ -409,7 +653,9 @@ static int inputThread(void * opaque)
|
|||||||
while (!atomic_load_explicit(&input->stop, memory_order_acquire))
|
while (!atomic_load_explicit(&input->stop, memory_order_acquire))
|
||||||
{
|
{
|
||||||
unsigned timeout = INPUT_WORKER_IDLE_MS;
|
unsigned timeout = INPUT_WORKER_IDLE_MS;
|
||||||
|
bool wake = false;
|
||||||
LG_LOCK(input->lock);
|
LG_LOCK(input->lock);
|
||||||
|
processInputStatus(input, &wake);
|
||||||
flushPending(input);
|
flushPending(input);
|
||||||
|
|
||||||
const uint64_t now = microtime();
|
const uint64_t now = microtime();
|
||||||
@@ -418,7 +664,6 @@ static int inputThread(void * opaque)
|
|||||||
input->publishedGeneration == input->generation &&
|
input->publishedGeneration == input->generation &&
|
||||||
now - input->lastInput >= INPUT_IDLE_RELEASE_US)
|
now - input->lastInput >= INPUT_IDLE_RELEASE_US)
|
||||||
{
|
{
|
||||||
bool wake = false;
|
|
||||||
release(input, &wake);
|
release(input, &wake);
|
||||||
}
|
}
|
||||||
else if (input->connected && input->claimed &&
|
else if (input->connected && input->claimed &&
|
||||||
@@ -426,7 +671,6 @@ static int inputThread(void * opaque)
|
|||||||
input->publishedGeneration == input->generation &&
|
input->publishedGeneration == input->generation &&
|
||||||
now - input->lastSend >= INPUT_KEEPALIVE_US)
|
now - input->lastSend >= INPUT_KEEPALIVE_US)
|
||||||
{
|
{
|
||||||
bool wake = false;
|
|
||||||
const KVMFRInputPayload payload = { 0 };
|
const KVMFRInputPayload payload = { 0 };
|
||||||
queuePayload(input, KVMFR_INPUT_MESSAGE_KEEPALIVE,
|
queuePayload(input, KVMFR_INPUT_MESSAGE_KEEPALIVE,
|
||||||
&payload, false, &wake);
|
&payload, false, &wake);
|
||||||
@@ -436,10 +680,15 @@ static int inputThread(void * opaque)
|
|||||||
timeout = INPUT_WORKER_RETRY_MS;
|
timeout = INPUT_WORKER_RETRY_MS;
|
||||||
LG_UNLOCK(input->lock);
|
LG_UNLOCK(input->lock);
|
||||||
|
|
||||||
|
notifyInputStatus(input);
|
||||||
|
if (wake)
|
||||||
|
lgSignalEvent(input->event);
|
||||||
|
|
||||||
if (atomic_load_explicit(&input->stop, memory_order_acquire))
|
if (atomic_load_explicit(&input->stop, memory_order_acquire))
|
||||||
break;
|
break;
|
||||||
lgWaitEvent(input->event, timeout);
|
lgWaitEvent(input->event, timeout);
|
||||||
}
|
}
|
||||||
|
notifyInputStatus(input);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -467,13 +716,17 @@ void lgmpInput_destroy(LGMPInput ** input)
|
|||||||
*input = NULL;
|
*input = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool lgmpInput_connect(LGMPInput * input)
|
bool lgmpInput_connect(LGMPInput * input, uint32_t clientID)
|
||||||
{
|
{
|
||||||
|
if (!clientID)
|
||||||
|
return false;
|
||||||
|
|
||||||
LG_LOCK(input->lock);
|
LG_LOCK(input->lock);
|
||||||
if (input->connected)
|
if (input->connected)
|
||||||
{
|
{
|
||||||
|
const bool sameClient = input->clientID == clientID;
|
||||||
LG_UNLOCK(input->lock);
|
LG_UNLOCK(input->lock);
|
||||||
return true;
|
return sameClient;
|
||||||
}
|
}
|
||||||
if (input->thread || input->queue)
|
if (input->thread || input->queue)
|
||||||
{
|
{
|
||||||
@@ -501,13 +754,24 @@ bool lgmpInput_connect(LGMPInput * input)
|
|||||||
|
|
||||||
input->connected = true;
|
input->connected = true;
|
||||||
input->claimed = false;
|
input->claimed = false;
|
||||||
|
input->available = false;
|
||||||
|
input->ownerBlocked = false;
|
||||||
input->pendingHead = 0;
|
input->pendingHead = 0;
|
||||||
input->pendingCount = 0;
|
input->pendingCount = 0;
|
||||||
|
input->clientID = clientID;
|
||||||
|
input->capabilities = 0;
|
||||||
|
input->endpointGeneration = 0;
|
||||||
|
input->statusSerial = 0;
|
||||||
|
input->statusOwnerClientID = 0;
|
||||||
|
input->statusOwnerGeneration = 0;
|
||||||
|
input->ownerConfirmed = false;
|
||||||
|
input->statusValid = false;
|
||||||
input->publishedGeneration = 0;
|
input->publishedGeneration = 0;
|
||||||
input->publishedSequence = 0;
|
input->publishedSequence = 0;
|
||||||
input->publishedClaimed = false;
|
input->publishedClaimed = false;
|
||||||
input->lastSend = 0;
|
input->lastSend = 0;
|
||||||
input->lastInput = 0;
|
input->lastInput = 0;
|
||||||
|
input->generation = 0;
|
||||||
clearInputState(input);
|
clearInputState(input);
|
||||||
atomic_store_explicit(&input->stop, false, memory_order_release);
|
atomic_store_explicit(&input->stop, false, memory_order_release);
|
||||||
LGThread * thread;
|
LGThread * thread;
|
||||||
@@ -545,6 +809,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)
|
||||||
|
input->notifyStatus = true;
|
||||||
|
input->available = false;
|
||||||
|
input->ownerBlocked = false;
|
||||||
|
input->capabilities = 0;
|
||||||
|
input->statusValid = false;
|
||||||
|
input->ownerConfirmed = false;
|
||||||
clearInputState(input);
|
clearInputState(input);
|
||||||
atomic_store_explicit(&input->stop, true, memory_order_release);
|
atomic_store_explicit(&input->stop, true, memory_order_release);
|
||||||
LGThread * thread = input->thread;
|
LGThread * thread = input->thread;
|
||||||
@@ -580,16 +851,40 @@ void lgmpInput_disconnect(LGMPInput * input)
|
|||||||
|
|
||||||
static bool inputSupports(void * opaque, LG_InputSupport support)
|
static bool inputSupports(void * opaque, LG_InputSupport support)
|
||||||
{
|
{
|
||||||
(void)opaque;
|
LGMPInput * input = opaque;
|
||||||
|
LG_LOCK(input->lock);
|
||||||
|
|
||||||
|
bool result;
|
||||||
switch (support)
|
switch (support)
|
||||||
{
|
{
|
||||||
case LG_INPUT_SUPPORT_MOUSE_ABSOLUTE:
|
case LG_INPUT_SUPPORT_MOUSE_ABSOLUTE:
|
||||||
return true;
|
result = input->available &&
|
||||||
|
(input->capabilities & KVMFR_INPUT_CAP_MOUSE_ABSOLUTE) != 0;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false;
|
result = false;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
LG_UNLOCK(input->lock);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void inputSetStatusListener(void * opaque,
|
||||||
|
LG_InputStatusFn callback, void * callbackOpaque)
|
||||||
|
{
|
||||||
|
LGMPInput * input = opaque;
|
||||||
|
LG_InputStatus status;
|
||||||
|
|
||||||
|
LG_LOCK(input->lock);
|
||||||
|
input->statusCallback = callback;
|
||||||
|
input->statusOpaque = callbackOpaque;
|
||||||
|
input->notifyStatus = false;
|
||||||
|
status = inputStatus(input);
|
||||||
|
LG_UNLOCK(input->lock);
|
||||||
|
|
||||||
|
if (callback)
|
||||||
|
callback(callbackOpaque, &status);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void buildKeyboardPayload(const LGMPInput * input,
|
static void buildKeyboardPayload(const LGMPInput * input,
|
||||||
@@ -626,11 +921,9 @@ static bool updateKey(void * opaque, int key, bool pressed)
|
|||||||
LGMPInput * input = opaque;
|
LGMPInput * input = opaque;
|
||||||
bool wake = false;
|
bool wake = false;
|
||||||
LG_LOCK(input->lock);
|
LG_LOCK(input->lock);
|
||||||
if (!input->connected || !claim(input, &wake))
|
if (!input->connected || !input->available)
|
||||||
{
|
{
|
||||||
LG_UNLOCK(input->lock);
|
LG_UNLOCK(input->lock);
|
||||||
if (wake)
|
|
||||||
lgSignalEvent(input->event);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -644,6 +937,20 @@ static bool updateKey(void * opaque, int key, bool pressed)
|
|||||||
else if (*state)
|
else if (*state)
|
||||||
--*state;
|
--*state;
|
||||||
|
|
||||||
|
if (input->ownerBlocked)
|
||||||
|
{
|
||||||
|
LG_UNLOCK(input->lock);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!claim(input, &wake))
|
||||||
|
{
|
||||||
|
*state = previous;
|
||||||
|
LG_UNLOCK(input->lock);
|
||||||
|
if (wake)
|
||||||
|
lgSignalEvent(input->event);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
KVMFRInputPayload payload = { 0 };
|
KVMFRInputPayload payload = { 0 };
|
||||||
buildKeyboardPayload(input, &payload);
|
buildKeyboardPayload(input, &payload);
|
||||||
bool result = queuePayload(input, KVMFR_INPUT_MESSAGE_KEYBOARD,
|
bool result = queuePayload(input, KVMFR_INPUT_MESSAGE_KEYBOARD,
|
||||||
@@ -704,7 +1011,15 @@ static bool inputMouseMotion(void * opaque, int32_t x, int32_t y)
|
|||||||
LGMPInput * input = opaque;
|
LGMPInput * input = opaque;
|
||||||
bool wake = false;
|
bool wake = false;
|
||||||
LG_LOCK(input->lock);
|
LG_LOCK(input->lock);
|
||||||
bool result = input->connected && claim(input, &wake) &&
|
if (input->connected && input->available && input->ownerBlocked)
|
||||||
|
{
|
||||||
|
input->mouseMode = LGMP_INPUT_MOUSE_RELATIVE;
|
||||||
|
LG_UNLOCK(input->lock);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool result = input->connected && input->available &&
|
||||||
|
claim(input, &wake) &&
|
||||||
queueMouse(input, LGMP_INPUT_MOUSE_RELATIVE,
|
queueMouse(input, LGMP_INPUT_MOUSE_RELATIVE,
|
||||||
x, y, 0, input->mouseButtons, true, &wake);
|
x, y, 0, input->mouseButtons, true, &wake);
|
||||||
if (result)
|
if (result)
|
||||||
@@ -732,10 +1047,24 @@ static bool inputMousePosition(void * opaque, uint32_t x, uint32_t y,
|
|||||||
LGMPInput * input = opaque;
|
LGMPInput * input = opaque;
|
||||||
bool wake = false;
|
bool wake = false;
|
||||||
LG_LOCK(input->lock);
|
LG_LOCK(input->lock);
|
||||||
|
if (!input->available ||
|
||||||
|
!(input->capabilities & KVMFR_INPUT_CAP_MOUSE_ABSOLUTE))
|
||||||
|
{
|
||||||
|
LG_UNLOCK(input->lock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const uint16_t previousX = input->absoluteX;
|
const uint16_t previousX = input->absoluteX;
|
||||||
const uint16_t previousY = input->absoluteY;
|
const uint16_t previousY = input->absoluteY;
|
||||||
input->absoluteX = absoluteX;
|
input->absoluteX = absoluteX;
|
||||||
input->absoluteY = absoluteY;
|
input->absoluteY = absoluteY;
|
||||||
|
if (input->ownerBlocked)
|
||||||
|
{
|
||||||
|
input->mouseMode = LGMP_INPUT_MOUSE_ABSOLUTE;
|
||||||
|
LG_UNLOCK(input->lock);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool result = input->connected && claim(input, &wake) &&
|
bool result = input->connected && claim(input, &wake) &&
|
||||||
queueMouse(input, LGMP_INPUT_MOUSE_ABSOLUTE,
|
queueMouse(input, LGMP_INPUT_MOUSE_ABSOLUTE,
|
||||||
0, 0, 0, input->mouseButtons, true, &wake);
|
0, 0, 0, input->mouseButtons, true, &wake);
|
||||||
@@ -784,10 +1113,17 @@ static bool updateMouseButton(void * opaque, unsigned int button,
|
|||||||
LGMPInput * input = opaque;
|
LGMPInput * input = opaque;
|
||||||
bool wake = false;
|
bool wake = false;
|
||||||
LG_LOCK(input->lock);
|
LG_LOCK(input->lock);
|
||||||
|
if (input->connected && input->available && input->ownerBlocked)
|
||||||
|
{
|
||||||
|
LG_UNLOCK(input->lock);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const enum LGMPInputMouseMode mode =
|
const enum LGMPInputMouseMode mode =
|
||||||
input->mouseMode == LGMP_INPUT_MOUSE_ABSOLUTE ?
|
input->mouseMode == LGMP_INPUT_MOUSE_ABSOLUTE ?
|
||||||
LGMP_INPUT_MOUSE_ABSOLUTE : LGMP_INPUT_MOUSE_RELATIVE;
|
LGMP_INPUT_MOUSE_ABSOLUTE : LGMP_INPUT_MOUSE_RELATIVE;
|
||||||
const bool result = input->connected && claim(input, &wake) &&
|
const bool result = input->connected && input->available &&
|
||||||
|
claim(input, &wake) &&
|
||||||
queueMouse(input, mode, 0, 0, button == 4 ? 1 : -1,
|
queueMouse(input, mode, 0, 0, button == 4 ? 1 : -1,
|
||||||
input->mouseButtons, false, &wake);
|
input->mouseButtons, false, &wake);
|
||||||
LG_UNLOCK(input->lock);
|
LG_UNLOCK(input->lock);
|
||||||
@@ -808,7 +1144,16 @@ static bool updateMouseButton(void * opaque, unsigned int button,
|
|||||||
const enum LGMPInputMouseMode mode =
|
const enum LGMPInputMouseMode mode =
|
||||||
input->mouseMode == LGMP_INPUT_MOUSE_ABSOLUTE ?
|
input->mouseMode == LGMP_INPUT_MOUSE_ABSOLUTE ?
|
||||||
LGMP_INPUT_MOUSE_ABSOLUTE : LGMP_INPUT_MOUSE_RELATIVE;
|
LGMP_INPUT_MOUSE_ABSOLUTE : LGMP_INPUT_MOUSE_RELATIVE;
|
||||||
bool result = input->connected && claim(input, &wake) &&
|
if (input->connected && input->available && input->ownerBlocked)
|
||||||
|
{
|
||||||
|
input->mouseButtons = buttons;
|
||||||
|
input->mouseMode = mode;
|
||||||
|
LG_UNLOCK(input->lock);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool result = input->connected && input->available &&
|
||||||
|
claim(input, &wake) &&
|
||||||
queueMouse(input, mode, 0, 0, 0, buttons, false, &wake);
|
queueMouse(input, mode, 0, 0, 0, buttons, false, &wake);
|
||||||
bool reset = false;
|
bool reset = false;
|
||||||
if (!result && !pressed && input->connected)
|
if (!result && !pressed && input->connected)
|
||||||
@@ -854,6 +1199,7 @@ static const LG_InputOps INPUT_OPS =
|
|||||||
{
|
{
|
||||||
.name = "LGMP",
|
.name = "LGMP",
|
||||||
.supports = inputSupports,
|
.supports = inputSupports,
|
||||||
|
.setStatusListener = inputSetStatusListener,
|
||||||
.keyDown = inputKeyDown,
|
.keyDown = inputKeyDown,
|
||||||
.keyUp = inputKeyUp,
|
.keyUp = inputKeyUp,
|
||||||
.keyboardLEDs = NULL,
|
.keyboardLEDs = NULL,
|
||||||
|
|||||||
@@ -26,13 +26,14 @@
|
|||||||
#include <lgmp/client.h>
|
#include <lgmp/client.h>
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
typedef struct LGMPInput LGMPInput;
|
typedef struct LGMPInput LGMPInput;
|
||||||
|
|
||||||
bool lgmpInput_create(PLGMPClient client, LGMPInput ** result);
|
bool lgmpInput_create(PLGMPClient client, LGMPInput ** result);
|
||||||
void lgmpInput_destroy(LGMPInput ** input);
|
void lgmpInput_destroy(LGMPInput ** input);
|
||||||
|
|
||||||
bool lgmpInput_connect(LGMPInput * input);
|
bool lgmpInput_connect(LGMPInput * input, uint32_t clientID);
|
||||||
void lgmpInput_disconnect(LGMPInput * input);
|
void lgmpInput_disconnect(LGMPInput * input);
|
||||||
|
|
||||||
const LG_InputOps * lgmpInput_getOps(void);
|
const LG_InputOps * lgmpInput_getOps(void);
|
||||||
|
|||||||
@@ -1353,7 +1353,7 @@ static const LG_InputOps * lgmp_getInputOps(LG_Transport * this,
|
|||||||
{
|
{
|
||||||
*opaque = NULL;
|
*opaque = NULL;
|
||||||
if (!this->connected || !this->inputSupported ||
|
if (!this->connected || !this->inputSupported ||
|
||||||
!lgmpInput_connect(this->input))
|
!lgmpInput_connect(this->input, this->clientID))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
*opaque = this->input;
|
*opaque = this->input;
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ bool CLGMPInputTransport::Initialize()
|
|||||||
if (m_queue)
|
if (m_queue)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
const LGMP_STATUS status = m_host.CreateQueue(
|
LGMP_STATUS status = m_host.CreateQueue(
|
||||||
INPUT_QUEUE_CONFIG, &m_queue);
|
INPUT_QUEUE_CONFIG, &m_queue);
|
||||||
if (status != LGMP_OK)
|
if (status != LGMP_OK)
|
||||||
{
|
{
|
||||||
@@ -88,15 +88,95 @@ bool CLGMPInputTransport::Initialize()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (PLGMPMemory& memory : m_statusMemory)
|
||||||
|
{
|
||||||
|
status = m_host.Allocate(sizeof(KVMFRInputStatus), &memory);
|
||||||
|
if (status != LGMP_OK)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("lgmpHostMemAlloc Failed (Input Status): %s",
|
||||||
|
lgmpStatusString(status));
|
||||||
|
DeInit();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
memset(lgmpHostMemPtr(memory), 0, sizeof(KVMFRInputStatus));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_statusDirty = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CLGMPInputTransport::DeInit()
|
void CLGMPInputTransport::DeInit()
|
||||||
{
|
{
|
||||||
Stop();
|
Stop();
|
||||||
|
for (PLGMPMemory& memory : m_statusMemory)
|
||||||
|
lgmpHostMemFree(&memory);
|
||||||
m_queue = nullptr;
|
m_queue = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CLGMPInputTransport::UpdateSinkState(uint64_t state)
|
||||||
|
{
|
||||||
|
if (state == m_sinkState)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_sinkState = state;
|
||||||
|
if (++m_endpointGeneration == 0)
|
||||||
|
++m_endpointGeneration;
|
||||||
|
m_statusDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLGMPInputTransport::PublishStatus()
|
||||||
|
{
|
||||||
|
if (!m_queue)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (lgmpHostQueueNewSubs(m_queue))
|
||||||
|
m_statusDirty = true;
|
||||||
|
if (!m_statusDirty || !lgmpHostQueueHasSubs(m_queue))
|
||||||
|
return;
|
||||||
|
|
||||||
|
PLGMPMemory memory = nullptr;
|
||||||
|
for (PLGMPMemory candidate : m_statusMemory)
|
||||||
|
if (!lgmpHostQueuePayloadPending(m_queue, candidate))
|
||||||
|
{
|
||||||
|
memory = candidate;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!memory)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const bool available = (m_sinkState & 1) != 0;
|
||||||
|
KVMFRInputStatus status = {};
|
||||||
|
status.version = KVMFR_INPUT_VERSION;
|
||||||
|
status.capabilities = available ?
|
||||||
|
KVMFR_INPUT_CAP_MOUSE_RELATIVE |
|
||||||
|
KVMFR_INPUT_CAP_MOUSE_ABSOLUTE |
|
||||||
|
KVMFR_INPUT_CAP_KEYBOARD : 0;
|
||||||
|
status.flags = available ? KVMFR_INPUT_STATUS_AVAILABLE : 0;
|
||||||
|
if (m_ownerClientID)
|
||||||
|
{
|
||||||
|
status.flags |= KVMFR_INPUT_STATUS_HAS_OWNER;
|
||||||
|
status.ownerClientID = m_ownerClientID;
|
||||||
|
status.ownerGeneration = m_ownerGeneration;
|
||||||
|
}
|
||||||
|
status.generation = m_endpointGeneration;
|
||||||
|
status.lease = static_cast<uint32_t>(OWNER_LEASE_MS);
|
||||||
|
status.maxButtons = KVMFR_INPUT_MOUSE_BUTTON_COUNT;
|
||||||
|
memcpy(lgmpHostMemPtr(memory), &status, sizeof(status));
|
||||||
|
|
||||||
|
uint32_t serial = m_statusSerial + 1;
|
||||||
|
if (!serial)
|
||||||
|
++serial;
|
||||||
|
const LGMP_STATUS result = lgmpHostQueuePost(m_queue, serial, memory);
|
||||||
|
if (result == LGMP_OK)
|
||||||
|
{
|
||||||
|
m_statusSerial = serial;
|
||||||
|
m_statusDirty = false;
|
||||||
|
}
|
||||||
|
else if (result != LGMP_ERR_QUEUE_FULL)
|
||||||
|
DEBUG_WARN("lgmpHostQueuePost Failed (Input Status): %s",
|
||||||
|
lgmpStatusString(result));
|
||||||
|
}
|
||||||
|
|
||||||
bool CLGMPInputTransport::Start(IInputSink& sink)
|
bool CLGMPInputTransport::Start(IInputSink& sink)
|
||||||
{
|
{
|
||||||
CSRWExclusiveLock lock(&m_lifecycleLock);
|
CSRWExclusiveLock lock(&m_lifecycleLock);
|
||||||
@@ -145,7 +225,12 @@ bool CLGMPInputTransport::Start(IInputSink& sink)
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_sink = &sink;
|
m_sink = &sink;
|
||||||
m_sinkState = sink.GetState();
|
UpdateSinkState(sink.GetState());
|
||||||
|
if (!m_endpointGeneration)
|
||||||
|
{
|
||||||
|
m_endpointGeneration = 1;
|
||||||
|
m_statusDirty = true;
|
||||||
|
}
|
||||||
m_thread = CreateThread(nullptr, 0, ThreadProc, this, 0, nullptr);
|
m_thread = CreateThread(nullptr, 0, ThreadProc, this, 0, nullptr);
|
||||||
if (!m_thread)
|
if (!m_thread)
|
||||||
{
|
{
|
||||||
@@ -216,6 +301,7 @@ bool CLGMPInputTransport::Claim(
|
|||||||
m_ownerGeneration = message.generation;
|
m_ownerGeneration = message.generation;
|
||||||
m_ownerSequence = message.sequence;
|
m_ownerSequence = message.sequence;
|
||||||
RenewLease();
|
RenewLease();
|
||||||
|
m_statusDirty = true;
|
||||||
DEBUG_INFO("Input owner %u generation %u acquired",
|
DEBUG_INFO("Input owner %u generation %u acquired",
|
||||||
m_ownerClientID, m_ownerGeneration);
|
m_ownerClientID, m_ownerGeneration);
|
||||||
return true;
|
return true;
|
||||||
@@ -241,6 +327,7 @@ void CLGMPInputTransport::ReleaseOwner(
|
|||||||
m_ownerGeneration = 0;
|
m_ownerGeneration = 0;
|
||||||
m_ownerSequence = 0;
|
m_ownerSequence = 0;
|
||||||
m_ownerDeadline = 0;
|
m_ownerDeadline = 0;
|
||||||
|
m_statusDirty = true;
|
||||||
DEBUG_INFO("Input owner %u generation %u released (%s)",
|
DEBUG_INFO("Input owner %u generation %u released (%s)",
|
||||||
clientID, generation, reason);
|
clientID, generation, reason);
|
||||||
}
|
}
|
||||||
@@ -253,7 +340,7 @@ void CLGMPInputTransport::CheckOwner()
|
|||||||
const uint64_t state = m_sink->GetState();
|
const uint64_t state = m_sink->GetState();
|
||||||
if (state != m_sinkState)
|
if (state != m_sinkState)
|
||||||
{
|
{
|
||||||
m_sinkState = state;
|
UpdateSinkState(state);
|
||||||
ReleaseOwner(true, "input endpoint changed");
|
ReleaseOwner(true, "input endpoint changed");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -322,7 +409,7 @@ bool CLGMPInputTransport::ProcessMessage(
|
|||||||
m_sink->GetState() : m_sinkState;
|
m_sink->GetState() : m_sinkState;
|
||||||
if (sinkState != m_sinkState)
|
if (sinkState != m_sinkState)
|
||||||
{
|
{
|
||||||
m_sinkState = sinkState;
|
UpdateSinkState(sinkState);
|
||||||
if (m_ownerClientID)
|
if (m_ownerClientID)
|
||||||
{
|
{
|
||||||
ReleaseOwner(true, "input endpoint changed");
|
ReleaseOwner(true, "input endpoint changed");
|
||||||
@@ -415,7 +502,7 @@ bool CLGMPInputTransport::ProcessMessage(
|
|||||||
const uint64_t deliveredState = m_sink->GetState();
|
const uint64_t deliveredState = m_sink->GetState();
|
||||||
if (deliveredState != m_sinkState)
|
if (deliveredState != m_sinkState)
|
||||||
{
|
{
|
||||||
m_sinkState = deliveredState;
|
UpdateSinkState(deliveredState);
|
||||||
ReleaseOwner(true, "input endpoint changed");
|
ReleaseOwner(true, "input endpoint changed");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -486,6 +573,7 @@ void CLGMPInputTransport::Thread()
|
|||||||
CheckOwner();
|
CheckOwner();
|
||||||
if (DrainMessages())
|
if (DrainMessages())
|
||||||
activeUntil = GetTickCount64() + ACTIVE_POLL_MS;
|
activeUntil = GetTickCount64() + ACTIVE_POLL_MS;
|
||||||
|
PublishStatus();
|
||||||
|
|
||||||
const bool active = GetTickCount64() < activeUntil;
|
const bool active = GetTickCount64() < activeUntil;
|
||||||
if (!ArmPollTimer(m_pollTimer, active))
|
if (!ArmPollTimer(m_pollTimer, active))
|
||||||
@@ -508,6 +596,8 @@ void CLGMPInputTransport::Thread()
|
|||||||
}
|
}
|
||||||
|
|
||||||
ReleaseOwner(true, "transport stopped");
|
ReleaseOwner(true, "transport stopped");
|
||||||
|
UpdateSinkState(0);
|
||||||
|
PublishStatus();
|
||||||
if (avTaskHandle)
|
if (avTaskHandle)
|
||||||
AvRevertMmThreadCharacteristics(avTaskHandle);
|
AvRevertMmThreadCharacteristics(avTaskHandle);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "transport/IInputTransport.h"
|
#include "transport/IInputTransport.h"
|
||||||
|
#include "common/LGMPConfig.h"
|
||||||
|
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
|
||||||
@@ -43,6 +44,7 @@ private:
|
|||||||
CLGMPHost& m_host;
|
CLGMPHost& m_host;
|
||||||
|
|
||||||
PLGMPHostQueue m_queue = nullptr;
|
PLGMPHostQueue m_queue = nullptr;
|
||||||
|
PLGMPMemory m_statusMemory[LGMP_Q_INPUT_LEN] = {};
|
||||||
IInputSink * m_sink = nullptr;
|
IInputSink * m_sink = nullptr;
|
||||||
|
|
||||||
SRWLOCK m_lifecycleLock = SRWLOCK_INIT;
|
SRWLOCK m_lifecycleLock = SRWLOCK_INIT;
|
||||||
@@ -55,9 +57,14 @@ private:
|
|||||||
uint32_t m_ownerSequence = 0;
|
uint32_t m_ownerSequence = 0;
|
||||||
ULONGLONG m_ownerDeadline = 0;
|
ULONGLONG m_ownerDeadline = 0;
|
||||||
uint64_t m_sinkState = 0;
|
uint64_t m_sinkState = 0;
|
||||||
|
uint32_t m_endpointGeneration = 0;
|
||||||
|
uint32_t m_statusSerial = 0;
|
||||||
|
bool m_statusDirty = false;
|
||||||
|
|
||||||
bool Initialize();
|
bool Initialize();
|
||||||
void DeInit();
|
void DeInit();
|
||||||
|
void UpdateSinkState(uint64_t state);
|
||||||
|
void PublishStatus();
|
||||||
bool DrainMessages();
|
bool DrainMessages();
|
||||||
bool ProcessMessage(uint32_t sourceClientID,
|
bool ProcessMessage(uint32_t sourceClientID,
|
||||||
const KVMFRInputMessage& message);
|
const KVMFRInputMessage& message);
|
||||||
|
|||||||
Reference in New Issue
Block a user