mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-11-12 14:57:20 +00:00
Isolate input sends from shared transport accounting and reserve reliable capacity. Prioritize QUIC execution and bound stale motion through the Windows pipe and HID queues.
1804 lines
54 KiB
C
1804 lines
54 KiB
C
/**
|
|
* Looking Glass
|
|
* Copyright © 2017-2026 The Looking Glass Authors
|
|
* https://looking-glass.io
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the Free
|
|
* Software Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
|
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "input.h"
|
|
|
|
#include "kb.h"
|
|
|
|
#include "common/event.h"
|
|
#include "common/locking.h"
|
|
#include "common/time.h"
|
|
|
|
#include <LGProtocol/NetworkServices.h>
|
|
|
|
#include <limits.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#define NETWORK_INPUT_TIMER_MS 50U
|
|
#define NETWORK_INPUT_LEASE_MS 500U
|
|
#define NETWORK_INPUT_KEEPALIVE_MS 200U
|
|
#define NETWORK_INPUT_CLAIM_RETRY_MS 500U
|
|
#define NETWORK_INPUT_LED_SYNC_DELAY_MS 20U
|
|
#define NETWORK_INPUT_LED_ACK_TIMEOUT_MS 1000U
|
|
#define NETWORK_INPUT_USAGE_PAGE_KEYBOARD 0x07U
|
|
#define NETWORK_INPUT_KEYBOARD_USAGE_MAX 231U
|
|
#define NETWORK_INPUT_QUEUE_SIZE 128U
|
|
#define NETWORK_INPUT_QUEUE_RESERVE 2U
|
|
#define NETWORK_INPUT_QUEUE_CAPACITY \
|
|
(NETWORK_INPUT_QUEUE_SIZE + NETWORK_INPUT_QUEUE_RESERVE)
|
|
#define NETWORK_INPUT_QUEUE_PAYLOAD_MAX LG_NET_INPUT_ABSOLUTE_WIRE_SIZE
|
|
|
|
#define NETWORK_INPUT_CLAIM_FLAGS \
|
|
(LG_NET_INPUT_CLAIM_KEYBOARD | LG_NET_INPUT_CLAIM_POINTER | \
|
|
LG_NET_INPUT_CLAIM_EXCLUSIVE)
|
|
|
|
#define NETWORK_INPUT_LED_MASK \
|
|
(LG_NET_KEYBOARD_LED_NUM_LOCK | LG_NET_KEYBOARD_LED_CAPS_LOCK | \
|
|
LG_NET_KEYBOARD_LED_SCROLL_LOCK)
|
|
|
|
struct NetworkInputLockKey
|
|
{
|
|
uint32_t led;
|
|
uint16_t usage;
|
|
};
|
|
|
|
typedef struct NetworkInputPkt
|
|
{
|
|
uint8_t payload[NETWORK_INPUT_QUEUE_PAYLOAD_MAX];
|
|
uint64_t componentEpoch;
|
|
uint64_t requestID;
|
|
uint32_t payloadLength;
|
|
LGNetEnvelopeFlags flags;
|
|
uint16_t messageType;
|
|
}
|
|
NetworkInputPkt;
|
|
|
|
_Static_assert(LG_NET_INPUT_CLAIM_WIRE_SIZE <=
|
|
NETWORK_INPUT_QUEUE_PAYLOAD_MAX &&
|
|
LG_NET_INPUT_RELATIVE_WIRE_SIZE <=
|
|
NETWORK_INPUT_QUEUE_PAYLOAD_MAX &&
|
|
LG_NET_INPUT_ABSOLUTE_WIRE_SIZE <=
|
|
NETWORK_INPUT_QUEUE_PAYLOAD_MAX &&
|
|
LG_NET_INPUT_KEYBOARD_WIRE_SIZE <=
|
|
NETWORK_INPUT_QUEUE_PAYLOAD_MAX &&
|
|
LG_NET_INPUT_CONTROL_WIRE_SIZE <=
|
|
NETWORK_INPUT_QUEUE_PAYLOAD_MAX,
|
|
"Network input reliable queue payloads are too small");
|
|
|
|
static const struct NetworkInputLockKey NETWORK_INPUT_LOCK_KEYS[] =
|
|
{
|
|
{ LG_NET_KEYBOARD_LED_NUM_LOCK, 83 },
|
|
{ LG_NET_KEYBOARD_LED_CAPS_LOCK, 57 },
|
|
{ LG_NET_KEYBOARD_LED_SCROLL_LOCK, 71 },
|
|
};
|
|
|
|
struct NetworkInput
|
|
{
|
|
NetworkConnection * connection;
|
|
LGTimer * timer;
|
|
LGEvent * statusIdle;
|
|
|
|
LG_Lock stateLock;
|
|
|
|
bool destroying;
|
|
bool connected;
|
|
bool serviceAvailable;
|
|
bool datagramAvailable;
|
|
bool claimPending;
|
|
bool claimed;
|
|
bool available;
|
|
uint64_t claimantID;
|
|
uint64_t claimEpoch;
|
|
uint64_t sequence;
|
|
uint64_t pointerSequence;
|
|
uint64_t nextClaimNs;
|
|
uint64_t nextKeepaliveNs;
|
|
|
|
uint32_t claimFlags;
|
|
uint16_t serviceVersion;
|
|
uint32_t mouseButtons;
|
|
bool mouseAbsolute;
|
|
uint32_t absoluteX;
|
|
uint32_t absoluteY;
|
|
uint32_t absoluteWidth;
|
|
uint32_t absoluteHeight;
|
|
int64_t cumulativeX;
|
|
int64_t cumulativeY;
|
|
bool pendingRelative;
|
|
bool pendingAbsolute;
|
|
bool pendingMotionReliable;
|
|
bool motionBarrier;
|
|
uint64_t reliableBarrierSequence;
|
|
NetworkInputPkt reliableQueue[NETWORK_INPUT_QUEUE_CAPACITY];
|
|
size_t reliableHead;
|
|
size_t reliableCount;
|
|
/* Preserve every held usage. The receiver reconstructs a six-key boot
|
|
* report and emits ErrorRollOver while more than six keys are held. */
|
|
bool keyState[NETWORK_INPUT_KEYBOARD_USAGE_MAX + 1U];
|
|
uint8_t modifiers;
|
|
bool keyboardLEDsValid;
|
|
uint8_t keyboardLEDs;
|
|
uint32_t keyboardLEDsValidMask;
|
|
uint32_t pendingKeyboardLEDs;
|
|
uint16_t pendingKeyboardLEDCounts[
|
|
sizeof(NETWORK_INPUT_LOCK_KEYS) / sizeof(*NETWORK_INPUT_LOCK_KEYS)];
|
|
uint64_t pendingKeyboardLEDsUntil;
|
|
uint64_t keyboardLEDsSyncAfter;
|
|
uint32_t staleKeyboardLEDs;
|
|
uint64_t staleKeyboardLEDsUntil;
|
|
uint64_t keyboardLEDSequence;
|
|
bool desiredKeyboardLEDsValid;
|
|
uint8_t desiredKeyboardLEDs;
|
|
|
|
uint32_t statusGeneration;
|
|
bool statusNotifyPending;
|
|
bool statusDispatching;
|
|
bool sendReadyPending;
|
|
LG_InputStatusFn statusCallback;
|
|
void * statusOpaque;
|
|
};
|
|
|
|
static _Thread_local NetworkInput * l_networkInputCallback;
|
|
static _Thread_local NetworkInput * l_networkInputSending;
|
|
|
|
static void networkInputResumeSendsNL(NetworkInput * input, bool * notify);
|
|
|
|
static uint64_t networkInputNext64(uint64_t value)
|
|
{
|
|
++value;
|
|
return value ? value : 1U;
|
|
}
|
|
|
|
static uint32_t networkInputNext32(uint32_t value)
|
|
{
|
|
++value;
|
|
return value ? value : 1U;
|
|
}
|
|
|
|
static uint64_t networkInputDeadline(uint32_t milliseconds)
|
|
{
|
|
return nanotime() + (uint64_t)milliseconds * UINT64_C(1000000);
|
|
}
|
|
|
|
static bool networkInputKeyboardHeldNL(const NetworkInput * input)
|
|
{
|
|
for (size_t usage = 1;
|
|
usage <= NETWORK_INPUT_KEYBOARD_USAGE_MAX; ++usage)
|
|
if (input->keyState[usage])
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
static bool networkInputLEDTransitionsNL(const NetworkInput * input)
|
|
{
|
|
for (size_t i = 0;
|
|
i < sizeof(NETWORK_INPUT_LOCK_KEYS) /
|
|
sizeof(*NETWORK_INPUT_LOCK_KEYS); ++i)
|
|
if (input->pendingKeyboardLEDCounts[i])
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
static uint32_t networkInputProjectedLEDsNL(const NetworkInput * input)
|
|
{
|
|
return input->keyboardLEDs ^ input->pendingKeyboardLEDs;
|
|
}
|
|
|
|
static void networkInputClearLEDProjectionNL(NetworkInput * input)
|
|
{
|
|
input->pendingKeyboardLEDs = 0;
|
|
input->pendingKeyboardLEDsUntil = 0;
|
|
memset(input->pendingKeyboardLEDCounts, 0,
|
|
sizeof(input->pendingKeyboardLEDCounts));
|
|
}
|
|
|
|
static void networkInputInvalidateLEDsNL(NetworkInput * input)
|
|
{
|
|
input->keyboardLEDsValid = false;
|
|
input->keyboardLEDs = 0;
|
|
input->keyboardLEDsValidMask = 0;
|
|
input->keyboardLEDSequence = 0;
|
|
input->keyboardLEDsSyncAfter = 0;
|
|
input->staleKeyboardLEDs = 0;
|
|
input->staleKeyboardLEDsUntil = 0;
|
|
networkInputClearLEDProjectionNL(input);
|
|
}
|
|
|
|
static void networkInputRecordLEDToggleNL(NetworkInput * input,
|
|
uint32_t led, bool physical)
|
|
{
|
|
for (size_t i = 0;
|
|
i < sizeof(NETWORK_INPUT_LOCK_KEYS) /
|
|
sizeof(*NETWORK_INPUT_LOCK_KEYS); ++i)
|
|
{
|
|
if (NETWORK_INPUT_LOCK_KEYS[i].led != led)
|
|
continue;
|
|
|
|
if (input->pendingKeyboardLEDCounts[i] != UINT16_MAX)
|
|
++input->pendingKeyboardLEDCounts[i];
|
|
input->pendingKeyboardLEDs ^= led;
|
|
input->pendingKeyboardLEDsUntil = networkInputDeadline(
|
|
NETWORK_INPUT_LED_ACK_TIMEOUT_MS);
|
|
break;
|
|
}
|
|
|
|
if (physical)
|
|
{
|
|
if (!input->desiredKeyboardLEDsValid ||
|
|
((networkInputProjectedLEDsNL(input) ^
|
|
input->desiredKeyboardLEDs) & led))
|
|
input->staleKeyboardLEDs |= led;
|
|
else
|
|
input->staleKeyboardLEDs &= ~led;
|
|
|
|
input->staleKeyboardLEDsUntil = input->staleKeyboardLEDs ?
|
|
networkInputDeadline(NETWORK_INPUT_LED_ACK_TIMEOUT_MS) : 0;
|
|
}
|
|
|
|
input->keyboardLEDsSyncAfter = networkInputDeadline(
|
|
NETWORK_INPUT_LED_SYNC_DELAY_MS);
|
|
}
|
|
|
|
static void networkInputAcknowledgeLEDsNL(NetworkInput * input,
|
|
uint32_t changed)
|
|
{
|
|
for (size_t i = 0;
|
|
i < sizeof(NETWORK_INPUT_LOCK_KEYS) /
|
|
sizeof(*NETWORK_INPUT_LOCK_KEYS); ++i)
|
|
{
|
|
const uint32_t led = NETWORK_INPUT_LOCK_KEYS[i].led;
|
|
if (!(changed & led) || !input->pendingKeyboardLEDCounts[i])
|
|
continue;
|
|
|
|
--input->pendingKeyboardLEDCounts[i];
|
|
input->pendingKeyboardLEDs ^= led;
|
|
}
|
|
|
|
if (!networkInputLEDTransitionsNL(input))
|
|
input->pendingKeyboardLEDsUntil = 0;
|
|
}
|
|
|
|
static LG_InputStatus networkInputStatusNL(const NetworkInput * input)
|
|
{
|
|
return (LG_InputStatus)
|
|
{
|
|
.available = input->available,
|
|
.generation = input->statusGeneration,
|
|
.keyboardLEDsValid = input->available && input->keyboardLEDsValid,
|
|
.keyboardLEDs = input->keyboardLEDs,
|
|
};
|
|
}
|
|
|
|
static void networkInputDispatchStatus(NetworkInput * input,
|
|
LG_InputStatusFn callback, void * opaque, LG_InputStatus status)
|
|
{
|
|
while (callback)
|
|
{
|
|
NetworkInput * previous = l_networkInputCallback;
|
|
l_networkInputCallback = input;
|
|
callback(opaque, &status);
|
|
l_networkInputCallback = previous;
|
|
|
|
LG_LOCK(input->stateLock);
|
|
if (input->statusNotifyPending && input->statusCallback)
|
|
{
|
|
input->statusNotifyPending = false;
|
|
callback = input->statusCallback;
|
|
opaque = input->statusOpaque;
|
|
status = networkInputStatusNL(input);
|
|
LG_UNLOCK(input->stateLock);
|
|
continue;
|
|
}
|
|
|
|
input->statusDispatching = false;
|
|
lgSignalEvent(input->statusIdle);
|
|
LG_UNLOCK(input->stateLock);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void networkInputNotifyStatus(NetworkInput * input)
|
|
{
|
|
LG_InputStatusFn callback;
|
|
void * opaque;
|
|
LG_InputStatus status;
|
|
|
|
LG_LOCK(input->stateLock);
|
|
if (!input->statusNotifyPending || input->statusDispatching)
|
|
{
|
|
LG_UNLOCK(input->stateLock);
|
|
return;
|
|
}
|
|
|
|
input->statusNotifyPending = false;
|
|
callback = input->statusCallback;
|
|
opaque = input->statusOpaque;
|
|
status = networkInputStatusNL(input);
|
|
if (!callback)
|
|
{
|
|
LG_UNLOCK(input->stateLock);
|
|
return;
|
|
}
|
|
|
|
input->statusDispatching = true;
|
|
lgResetEvent(input->statusIdle);
|
|
LG_UNLOCK(input->stateLock);
|
|
|
|
networkInputDispatchStatus(input, callback, opaque, status);
|
|
}
|
|
|
|
static bool networkInputSetAvailableNL(NetworkInput * input, bool available)
|
|
{
|
|
if (input->available == available)
|
|
return false;
|
|
|
|
input->available = available;
|
|
input->statusGeneration =
|
|
networkInputNext32(input->statusGeneration);
|
|
input->statusNotifyPending = true;
|
|
return true;
|
|
}
|
|
|
|
static void networkInputClearStateNL(NetworkInput * input)
|
|
{
|
|
input->mouseButtons = 0;
|
|
input->mouseAbsolute = false;
|
|
input->absoluteX = 0;
|
|
input->absoluteY = 0;
|
|
input->absoluteWidth = 0;
|
|
input->absoluteHeight = 0;
|
|
input->cumulativeX = 0;
|
|
input->cumulativeY = 0;
|
|
input->pointerSequence = 0;
|
|
input->pendingRelative = false;
|
|
input->pendingAbsolute = false;
|
|
input->pendingMotionReliable = false;
|
|
input->motionBarrier = true;
|
|
input->reliableBarrierSequence = 0;
|
|
input->sendReadyPending = false;
|
|
memset(input->keyState, 0, sizeof(input->keyState));
|
|
input->modifiers = 0;
|
|
networkInputClearLEDProjectionNL(input);
|
|
}
|
|
|
|
static void networkInputClearReliableNL(NetworkInput * input)
|
|
{
|
|
input->reliableHead = 0;
|
|
input->reliableCount = 0;
|
|
}
|
|
|
|
static LG_TransportStatus networkInputSendReliableNL(NetworkInput * input,
|
|
uint16_t messageType, LGNetEnvelopeFlags flags, uint64_t componentEpoch,
|
|
uint64_t requestID, const void * payload, uint32_t payloadLength)
|
|
{
|
|
NetworkInput * previous;
|
|
LG_TransportStatus status;
|
|
|
|
previous = l_networkInputSending;
|
|
l_networkInputSending = input;
|
|
status = networkConnectionSend(
|
|
input->connection, LG_NET_SERVICE_INPUT,
|
|
messageType, input->serviceVersion,
|
|
flags | LG_NET_ENVELOPE_FINAL, componentEpoch, requestID,
|
|
payload, payloadLength);
|
|
l_networkInputSending = previous;
|
|
return status;
|
|
}
|
|
|
|
static LG_TransportStatus networkInputSendDatagramNL(NetworkInput * input,
|
|
uint16_t messageType, uint64_t applicationSequence,
|
|
const void * payload, uint32_t payloadLength)
|
|
{
|
|
NetworkInput * previous;
|
|
LG_TransportStatus status;
|
|
|
|
previous = l_networkInputSending;
|
|
l_networkInputSending = input;
|
|
status = networkConnectionSendDatagram(
|
|
input->connection, LG_NET_SERVICE_INPUT, messageType,
|
|
input->serviceVersion, 0, input->claimEpoch, 0,
|
|
payload, payloadLength, applicationSequence, true);
|
|
l_networkInputSending = previous;
|
|
return status;
|
|
}
|
|
|
|
static bool networkInputTransportFatal(LG_TransportStatus status)
|
|
{
|
|
return status != LG_TRANSPORT_OK && status != LG_TRANSPORT_UNAVAILABLE &&
|
|
status != LG_TRANSPORT_TIMEOUT;
|
|
}
|
|
|
|
static bool networkInputQueueReliableNL(NetworkInput * input,
|
|
uint16_t messageType, LGNetEnvelopeFlags flags, uint64_t requestID,
|
|
const void * payload, uint32_t payloadLength)
|
|
{
|
|
const bool emergency = messageType == LG_NET_INPUT_MESSAGE_RESET ||
|
|
messageType == LG_NET_INPUT_MESSAGE_RELEASE;
|
|
const size_t limit = emergency ?
|
|
NETWORK_INPUT_QUEUE_CAPACITY : NETWORK_INPUT_QUEUE_SIZE;
|
|
if (input->reliableCount >= limit ||
|
|
payloadLength > NETWORK_INPUT_QUEUE_PAYLOAD_MAX ||
|
|
(payloadLength && !payload))
|
|
return false;
|
|
|
|
const size_t index =
|
|
(input->reliableHead + input->reliableCount) %
|
|
NETWORK_INPUT_QUEUE_CAPACITY;
|
|
NetworkInputPkt * packet = &input->reliableQueue[index];
|
|
if (payloadLength)
|
|
memcpy(packet->payload, payload, payloadLength);
|
|
packet->componentEpoch = input->claimEpoch;
|
|
packet->requestID = requestID;
|
|
packet->payloadLength = payloadLength;
|
|
packet->flags = flags;
|
|
packet->messageType = messageType;
|
|
++input->reliableCount;
|
|
return true;
|
|
}
|
|
|
|
static LG_TransportStatus networkInputDrainReliableNL(NetworkInput * input)
|
|
{
|
|
if (!input->reliableCount)
|
|
return LG_TRANSPORT_OK;
|
|
|
|
const NetworkInputPkt * packet =
|
|
&input->reliableQueue[input->reliableHead];
|
|
const LG_TransportStatus status = networkInputSendReliableNL(input,
|
|
packet->messageType, packet->flags, packet->componentEpoch,
|
|
packet->requestID, packet->payload, packet->payloadLength);
|
|
if (status != LG_TRANSPORT_OK)
|
|
return status;
|
|
|
|
input->reliableHead =
|
|
(input->reliableHead + 1U) % NETWORK_INPUT_QUEUE_CAPACITY;
|
|
if (!--input->reliableCount)
|
|
input->reliableHead = 0;
|
|
return LG_TRANSPORT_OK;
|
|
}
|
|
|
|
static LG_TransportStatus networkInputSubmitReliableNL(NetworkInput * input,
|
|
uint16_t messageType, LGNetEnvelopeFlags flags, uint64_t requestID,
|
|
const void * payload, uint32_t payloadLength)
|
|
{
|
|
if (!input->reliableCount)
|
|
{
|
|
const LG_TransportStatus status = networkInputSendReliableNL(input,
|
|
messageType, flags, input->claimEpoch, requestID,
|
|
payload, payloadLength);
|
|
if (status == LG_TRANSPORT_OK || networkInputTransportFatal(status))
|
|
return status;
|
|
}
|
|
|
|
return networkInputQueueReliableNL(input, messageType, flags, requestID,
|
|
payload, payloadLength) ? LG_TRANSPORT_OK : LG_TRANSPORT_ERROR;
|
|
}
|
|
|
|
static bool networkInputSendClaimNL(NetworkInput * input)
|
|
{
|
|
if (!input->connected || !input->serviceAvailable || input->destroying ||
|
|
input->reliableCount)
|
|
return false;
|
|
|
|
networkInputClearReliableNL(input);
|
|
networkInputClearStateNL(input);
|
|
networkInputInvalidateLEDsNL(input);
|
|
const uint64_t claimEpoch = networkInputNext64(input->claimEpoch);
|
|
const LGNetInputClaim claim =
|
|
{
|
|
.claimantID = input->claimantID,
|
|
.claimEpoch = claimEpoch,
|
|
.leaseMs = NETWORK_INPUT_LEASE_MS,
|
|
.flags = NETWORK_INPUT_CLAIM_FLAGS,
|
|
};
|
|
uint8_t payload[LG_NET_INPUT_CLAIM_WIRE_SIZE];
|
|
if (!lgNetInputClaimEncode(payload, sizeof(payload), &claim))
|
|
return false;
|
|
|
|
input->claimEpoch = claimEpoch;
|
|
input->sequence = 0;
|
|
const LG_TransportStatus status = networkInputSendReliableNL(input,
|
|
LG_NET_INPUT_MESSAGE_CLAIM,
|
|
LG_NET_ENVELOPE_ACK_REQUIRED, claimEpoch, claimEpoch,
|
|
payload, sizeof(payload));
|
|
if (status != LG_TRANSPORT_OK)
|
|
{
|
|
input->nextClaimNs = networkInputDeadline(
|
|
NETWORK_INPUT_CLAIM_RETRY_MS);
|
|
return false;
|
|
}
|
|
|
|
input->claimPending = true;
|
|
input->claimed = false;
|
|
input->claimFlags = 0;
|
|
input->nextClaimNs = networkInputDeadline(NETWORK_INPUT_LEASE_MS);
|
|
input->nextKeepaliveNs = 0;
|
|
return true;
|
|
}
|
|
|
|
static LG_TransportStatus networkInputSendControlNL(NetworkInput * input,
|
|
uint16_t messageType, uint32_t flags, uint32_t leaseMs)
|
|
{
|
|
const uint64_t sequence = networkInputNext64(input->sequence);
|
|
const LGNetInputControl control =
|
|
{
|
|
.claimantID = input->claimantID,
|
|
.claimEpoch = input->claimEpoch,
|
|
.sequence = sequence,
|
|
.flags = flags,
|
|
.leaseMs = leaseMs,
|
|
};
|
|
uint8_t payload[LG_NET_INPUT_CONTROL_WIRE_SIZE];
|
|
if (!lgNetInputControlEncode(payload, sizeof(payload), &control))
|
|
return LG_TRANSPORT_ERROR;
|
|
|
|
const LG_TransportStatus status = networkInputSubmitReliableNL(input,
|
|
messageType, 0, 0, payload, sizeof(payload));
|
|
if (status != LG_TRANSPORT_OK)
|
|
return status;
|
|
|
|
input->sequence = sequence;
|
|
return LG_TRANSPORT_OK;
|
|
}
|
|
|
|
static bool networkInputAbandonClaimNL(NetworkInput * input,
|
|
bool tryReset)
|
|
{
|
|
networkInputClearReliableNL(input);
|
|
networkInputClearStateNL(input);
|
|
networkInputInvalidateLEDsNL(input);
|
|
if (input->claimed && networkConnectionValid(input->connection))
|
|
{
|
|
if (tryReset)
|
|
networkInputSendControlNL(input, LG_NET_INPUT_MESSAGE_RESET,
|
|
LG_NET_INPUT_CLAIM_KEYBOARD | LG_NET_INPUT_CLAIM_POINTER, 0);
|
|
networkInputSendControlNL(input, LG_NET_INPUT_MESSAGE_RELEASE,
|
|
input->claimFlags, 0);
|
|
}
|
|
|
|
input->claimPending = false;
|
|
input->claimed = false;
|
|
input->claimFlags = 0;
|
|
input->claimEpoch = networkInputNext64(input->claimEpoch);
|
|
input->nextKeepaliveNs = 0;
|
|
input->nextClaimNs = networkInputDeadline(
|
|
NETWORK_INPUT_CLAIM_RETRY_MS);
|
|
return networkInputSetAvailableNL(input, false);
|
|
}
|
|
|
|
static bool networkInputSendResetNL(NetworkInput * input)
|
|
{
|
|
LG_TransportStatus status = LG_TRANSPORT_OK;
|
|
if (input->claimed)
|
|
{
|
|
networkInputClearReliableNL(input);
|
|
networkInputClearStateNL(input);
|
|
status = networkConnectionValid(input->connection) ?
|
|
networkInputSendControlNL(input, LG_NET_INPUT_MESSAGE_RESET,
|
|
LG_NET_INPUT_CLAIM_KEYBOARD | LG_NET_INPUT_CLAIM_POINTER, 0) :
|
|
LG_TRANSPORT_DISCONNECTED;
|
|
}
|
|
else
|
|
networkInputClearStateNL(input);
|
|
if (status == LG_TRANSPORT_DISCONNECTED)
|
|
networkInputClearReliableNL(input);
|
|
if (!networkInputTransportFatal(status))
|
|
return false;
|
|
return networkInputAbandonClaimNL(input, false);
|
|
}
|
|
|
|
static bool networkInputSendReleaseNL(NetworkInput * input)
|
|
{
|
|
bool sent = true;
|
|
if ((input->claimed || input->claimPending) &&
|
|
networkConnectionValid(input->connection))
|
|
sent = networkInputSendControlNL(input, LG_NET_INPUT_MESSAGE_RELEASE,
|
|
input->claimFlags, 0) == LG_TRANSPORT_OK;
|
|
|
|
input->claimPending = false;
|
|
input->claimed = false;
|
|
input->claimFlags = 0;
|
|
input->nextKeepaliveNs = 0;
|
|
return sent;
|
|
}
|
|
|
|
static bool networkInputNextSequenceNL(NetworkInput * input,
|
|
uint64_t * sequence)
|
|
{
|
|
if (!input->available || !input->claimed)
|
|
return false;
|
|
|
|
*sequence = networkInputNext64(input->sequence);
|
|
return true;
|
|
}
|
|
|
|
static LG_TransportStatus networkInputSendKeyboardNL(NetworkInput * input,
|
|
uint16_t usage, bool down, bool repeat)
|
|
{
|
|
uint64_t sequence;
|
|
if (!networkInputNextSequenceNL(input, &sequence))
|
|
return LG_TRANSPORT_UNAVAILABLE;
|
|
|
|
LGNetInputKeyboard keyboard =
|
|
{
|
|
.sequence = sequence,
|
|
.timestampNs = nanotime(),
|
|
.usagePage = NETWORK_INPUT_USAGE_PAGE_KEYBOARD,
|
|
.usage = usage,
|
|
.scanCode = 0,
|
|
.flags = (down ? LG_NET_KEYBOARD_DOWN : 0) |
|
|
(repeat ? LG_NET_KEYBOARD_REPEAT : 0),
|
|
.modifiers = input->modifiers,
|
|
};
|
|
if (!keyboard.timestampNs)
|
|
keyboard.timestampNs = 1;
|
|
|
|
uint8_t payload[LG_NET_INPUT_KEYBOARD_WIRE_SIZE];
|
|
if (!lgNetInputKeyboardEncode(payload, sizeof(payload), &keyboard))
|
|
return LG_TRANSPORT_ERROR;
|
|
|
|
const LG_TransportStatus status = networkInputSubmitReliableNL(input,
|
|
LG_NET_INPUT_MESSAGE_KEYBOARD, 0, 0, payload, sizeof(payload));
|
|
if (status != LG_TRANSPORT_OK)
|
|
return status;
|
|
|
|
input->sequence = sequence;
|
|
return LG_TRANSPORT_OK;
|
|
}
|
|
|
|
static uint32_t networkInputLEDForUsage(uint16_t usage)
|
|
{
|
|
switch (usage)
|
|
{
|
|
case 83:
|
|
return LG_NET_KEYBOARD_LED_NUM_LOCK;
|
|
|
|
case 57:
|
|
return LG_NET_KEYBOARD_LED_CAPS_LOCK;
|
|
|
|
case 71:
|
|
return LG_NET_KEYBOARD_LED_SCROLL_LOCK;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static bool networkInputTapLockKeyNL(NetworkInput * input,
|
|
const struct NetworkInputLockKey * key, bool * notify)
|
|
{
|
|
if (key->usage > NETWORK_INPUT_KEYBOARD_USAGE_MAX ||
|
|
input->keyState[key->usage])
|
|
return false;
|
|
|
|
if (networkInputSendKeyboardNL(input, key->usage, true, false) !=
|
|
LG_TRANSPORT_OK)
|
|
return false;
|
|
networkInputRecordLEDToggleNL(input, key->led, false);
|
|
if (networkInputSendKeyboardNL(input, key->usage, false, false) ==
|
|
LG_TRANSPORT_OK)
|
|
return true;
|
|
|
|
*notify |= networkInputSendResetNL(input);
|
|
input->keyboardLEDsSyncAfter = networkInputDeadline(
|
|
NETWORK_INPUT_LED_ACK_TIMEOUT_MS);
|
|
return false;
|
|
}
|
|
|
|
static void networkInputSyncKeyboardLEDsNL(NetworkInput * input,
|
|
bool * notify)
|
|
{
|
|
const uint64_t now = nanotime();
|
|
if (networkInputLEDTransitionsNL(input) &&
|
|
now >= input->pendingKeyboardLEDsUntil)
|
|
{
|
|
networkInputClearLEDProjectionNL(input);
|
|
input->keyboardLEDsSyncAfter = now +
|
|
(uint64_t)NETWORK_INPUT_LED_SYNC_DELAY_MS * UINT64_C(1000000);
|
|
}
|
|
|
|
if (input->staleKeyboardLEDs &&
|
|
now >= input->staleKeyboardLEDsUntil)
|
|
{
|
|
input->desiredKeyboardLEDsValid = false;
|
|
input->staleKeyboardLEDs = 0;
|
|
input->staleKeyboardLEDsUntil = 0;
|
|
}
|
|
|
|
if (!input->available || !input->keyboardLEDsValid ||
|
|
!input->desiredKeyboardLEDsValid ||
|
|
networkInputLEDTransitionsNL(input) ||
|
|
now < input->keyboardLEDsSyncAfter)
|
|
return;
|
|
|
|
const uint32_t difference =
|
|
(networkInputProjectedLEDsNL(input) ^
|
|
input->desiredKeyboardLEDs) &
|
|
NETWORK_INPUT_LED_MASK & ~input->staleKeyboardLEDs;
|
|
if (!difference)
|
|
{
|
|
if (!input->staleKeyboardLEDs)
|
|
input->desiredKeyboardLEDsValid = false;
|
|
return;
|
|
}
|
|
if (networkInputKeyboardHeldNL(input))
|
|
return;
|
|
|
|
for (size_t i = 0;
|
|
i < sizeof(NETWORK_INPUT_LOCK_KEYS) /
|
|
sizeof(*NETWORK_INPUT_LOCK_KEYS); ++i)
|
|
{
|
|
if (!(difference & NETWORK_INPUT_LOCK_KEYS[i].led))
|
|
continue;
|
|
if (!networkInputTapLockKeyNL(input,
|
|
&NETWORK_INPUT_LOCK_KEYS[i], notify))
|
|
break;
|
|
}
|
|
}
|
|
|
|
static bool networkInputTimer(void * opaque)
|
|
{
|
|
NetworkInput * input = opaque;
|
|
bool notify = false;
|
|
LG_LOCK(input->stateLock);
|
|
if (input->destroying)
|
|
{
|
|
LG_UNLOCK(input->stateLock);
|
|
return false;
|
|
}
|
|
|
|
const uint64_t now = nanotime();
|
|
|
|
if (input->connected && input->serviceAvailable)
|
|
{
|
|
if (input->sendReadyPending || input->reliableCount ||
|
|
input->pendingRelative || input->pendingAbsolute)
|
|
networkInputResumeSendsNL(input, ¬ify);
|
|
if (input->claimPending && now >= input->nextClaimNs)
|
|
input->claimPending = false;
|
|
if (!input->claimPending && !input->claimed &&
|
|
now >= input->nextClaimNs)
|
|
networkInputSendClaimNL(input);
|
|
else if (input->claimed && now >= input->nextKeepaliveNs)
|
|
{
|
|
const LG_TransportStatus status = networkInputSendControlNL(input,
|
|
LG_NET_INPUT_MESSAGE_KEEPALIVE,
|
|
input->claimFlags, NETWORK_INPUT_LEASE_MS);
|
|
if (status == LG_TRANSPORT_OK)
|
|
input->nextKeepaliveNs = networkInputDeadline(
|
|
NETWORK_INPUT_KEEPALIVE_MS);
|
|
else if (networkInputTransportFatal(status))
|
|
notify |= networkInputAbandonClaimNL(input, true);
|
|
}
|
|
|
|
networkInputSyncKeyboardLEDsNL(input, ¬ify);
|
|
if (input->sendReadyPending)
|
|
networkInputResumeSendsNL(input, ¬ify);
|
|
}
|
|
LG_UNLOCK(input->stateLock);
|
|
|
|
networkInputNotifyStatus(input);
|
|
return true;
|
|
}
|
|
|
|
static bool networkInputSupports(void * opaque, LG_InputSupport support)
|
|
{
|
|
NetworkInput * input = opaque;
|
|
if (support != LG_INPUT_SUPPORT_MOUSE_ABSOLUTE)
|
|
return false;
|
|
|
|
LG_LOCK(input->stateLock);
|
|
const bool supported = input->available &&
|
|
(input->claimFlags & LG_NET_INPUT_CLAIM_POINTER);
|
|
LG_UNLOCK(input->stateLock);
|
|
return supported;
|
|
}
|
|
|
|
static void networkInputSetStatusListener(void * opaque,
|
|
LG_InputStatusFn callback, void * callbackOpaque)
|
|
{
|
|
NetworkInput * input = opaque;
|
|
const bool reentrant = l_networkInputCallback == input;
|
|
|
|
LG_LOCK(input->stateLock);
|
|
input->statusCallback = NULL;
|
|
input->statusOpaque = NULL;
|
|
input->statusNotifyPending = false;
|
|
const bool dispatching = input->statusDispatching;
|
|
LG_UNLOCK(input->stateLock);
|
|
|
|
if (dispatching && !reentrant)
|
|
lgWaitEvent(input->statusIdle, TIMEOUT_INFINITE);
|
|
if (!callback)
|
|
return;
|
|
|
|
LG_LOCK(input->stateLock);
|
|
input->statusCallback = callback;
|
|
input->statusOpaque = callbackOpaque;
|
|
input->statusNotifyPending = false;
|
|
const LG_InputStatus status = networkInputStatusNL(input);
|
|
const bool startDispatch = !input->statusDispatching;
|
|
if (startDispatch)
|
|
{
|
|
input->statusDispatching = true;
|
|
lgResetEvent(input->statusIdle);
|
|
}
|
|
LG_UNLOCK(input->stateLock);
|
|
|
|
if (startDispatch)
|
|
networkInputDispatchStatus(input, callback, callbackOpaque, status);
|
|
else
|
|
{
|
|
NetworkInput * previous = l_networkInputCallback;
|
|
l_networkInputCallback = input;
|
|
callback(callbackOpaque, &status);
|
|
l_networkInputCallback = previous;
|
|
}
|
|
}
|
|
|
|
static bool networkInputUpdateKey(void * opaque, int key, bool down)
|
|
{
|
|
if (key < 0 || key >= LG_KEY_MAX)
|
|
return false;
|
|
|
|
const uint16_t usage = lg_keycode_to_hid[key];
|
|
if (!usage || usage > NETWORK_INPUT_KEYBOARD_USAGE_MAX)
|
|
return true;
|
|
|
|
NetworkInput * input = opaque;
|
|
bool notify = false;
|
|
LG_LOCK(input->stateLock);
|
|
if (!input->available ||
|
|
!(input->claimFlags & LG_NET_INPUT_CLAIM_KEYBOARD))
|
|
{
|
|
LG_UNLOCK(input->stateLock);
|
|
return false;
|
|
}
|
|
|
|
const bool previous = input->keyState[usage];
|
|
if (previous == down)
|
|
{
|
|
LG_UNLOCK(input->stateLock);
|
|
return true;
|
|
}
|
|
|
|
input->keyState[usage] = down;
|
|
if (usage >= 224 && usage <= 231)
|
|
{
|
|
const uint8_t mask = UINT8_C(1) << (usage - 224);
|
|
if (down)
|
|
input->modifiers |= mask;
|
|
else
|
|
input->modifiers &= (uint8_t)~mask;
|
|
}
|
|
|
|
const LG_TransportStatus status =
|
|
networkInputSendKeyboardNL(input, usage, down, false);
|
|
if (status != LG_TRANSPORT_OK)
|
|
{
|
|
input->keyState[usage] = previous;
|
|
if (usage >= 224 && usage <= 231)
|
|
{
|
|
const uint8_t mask = UINT8_C(1) << (usage - 224);
|
|
if (previous)
|
|
input->modifiers |= mask;
|
|
else
|
|
input->modifiers &= (uint8_t)~mask;
|
|
}
|
|
if (!down && networkInputTransportFatal(status))
|
|
notify = networkInputAbandonClaimNL(input, true);
|
|
if (input->sendReadyPending)
|
|
networkInputResumeSendsNL(input, ¬ify);
|
|
LG_UNLOCK(input->stateLock);
|
|
if (notify)
|
|
networkInputNotifyStatus(input);
|
|
return false;
|
|
}
|
|
|
|
const uint32_t led = networkInputLEDForUsage(usage);
|
|
if (down && led)
|
|
{
|
|
if (input->keyboardLEDsValid)
|
|
networkInputRecordLEDToggleNL(input, led, true);
|
|
else
|
|
{
|
|
input->desiredKeyboardLEDsValid = false;
|
|
input->keyboardLEDsSyncAfter = networkInputDeadline(
|
|
NETWORK_INPUT_LED_SYNC_DELAY_MS);
|
|
}
|
|
}
|
|
if (input->sendReadyPending)
|
|
networkInputResumeSendsNL(input, ¬ify);
|
|
LG_UNLOCK(input->stateLock);
|
|
if (notify)
|
|
networkInputNotifyStatus(input);
|
|
return true;
|
|
}
|
|
|
|
static bool networkInputKeyDown(void * opaque, int key)
|
|
{
|
|
return networkInputUpdateKey(opaque, key, true);
|
|
}
|
|
|
|
static bool networkInputKeyUp(void * opaque, int key)
|
|
{
|
|
return networkInputUpdateKey(opaque, key, false);
|
|
}
|
|
|
|
static bool networkInputKeyboardLEDs(void * opaque, bool numLock,
|
|
bool capsLock, bool scrollLock)
|
|
{
|
|
NetworkInput * input = opaque;
|
|
bool notify = false;
|
|
const uint8_t desired =
|
|
(numLock ? LG_NET_KEYBOARD_LED_NUM_LOCK : 0) |
|
|
(capsLock ? LG_NET_KEYBOARD_LED_CAPS_LOCK : 0) |
|
|
(scrollLock ? LG_NET_KEYBOARD_LED_SCROLL_LOCK : 0);
|
|
|
|
LG_LOCK(input->stateLock);
|
|
input->desiredKeyboardLEDs = desired;
|
|
input->desiredKeyboardLEDsValid = true;
|
|
input->staleKeyboardLEDs &=
|
|
networkInputProjectedLEDsNL(input) ^ desired;
|
|
if (!input->staleKeyboardLEDs)
|
|
input->staleKeyboardLEDsUntil = 0;
|
|
const uint64_t syncAfter = networkInputDeadline(
|
|
NETWORK_INPUT_LED_SYNC_DELAY_MS);
|
|
if (input->keyboardLEDsSyncAfter < syncAfter)
|
|
input->keyboardLEDsSyncAfter = syncAfter;
|
|
networkInputSyncKeyboardLEDsNL(input, ¬ify);
|
|
if (input->sendReadyPending)
|
|
networkInputResumeSendsNL(input, ¬ify);
|
|
const bool available = input->available;
|
|
LG_UNLOCK(input->stateLock);
|
|
if (notify)
|
|
networkInputNotifyStatus(input);
|
|
return available;
|
|
}
|
|
|
|
static void networkInputKeyboardLEDsReset(void * opaque)
|
|
{
|
|
NetworkInput * input = opaque;
|
|
LG_LOCK(input->stateLock);
|
|
input->desiredKeyboardLEDsValid = false;
|
|
input->keyboardLEDsSyncAfter = 0;
|
|
input->staleKeyboardLEDs = 0;
|
|
input->staleKeyboardLEDsUntil = 0;
|
|
LG_UNLOCK(input->stateLock);
|
|
}
|
|
|
|
static int64_t networkInputSaturatingAdd(int64_t value, int32_t delta)
|
|
{
|
|
if (delta > 0 && value > INT64_MAX - delta)
|
|
return INT64_MAX;
|
|
if (delta < 0 && value < INT64_MIN - delta)
|
|
return INT64_MIN;
|
|
return value + delta;
|
|
}
|
|
|
|
static LG_TransportStatus networkInputTransmitRelativeNL(
|
|
NetworkInput * input, int32_t wheelX, int32_t wheelY,
|
|
uint32_t buttons, uint32_t changedButtons, bool datagram,
|
|
bool acknowledge)
|
|
{
|
|
uint64_t sequence;
|
|
if (!networkInputNextSequenceNL(input, &sequence))
|
|
return LG_TRANSPORT_UNAVAILABLE;
|
|
|
|
LGNetInputRelative relative =
|
|
{
|
|
.sequence = sequence,
|
|
.timestampNs = nanotime(),
|
|
.cumulativeX = input->cumulativeX,
|
|
.cumulativeY = input->cumulativeY,
|
|
.wheelX = wheelX,
|
|
.wheelY = wheelY,
|
|
.buttons = buttons,
|
|
.changedButtons = changedButtons,
|
|
};
|
|
if (!relative.timestampNs)
|
|
relative.timestampNs = 1;
|
|
|
|
uint8_t payload[LG_NET_INPUT_RELATIVE_WIRE_SIZE];
|
|
if (!lgNetInputRelativeEncode(payload, sizeof(payload), &relative))
|
|
return LG_TRANSPORT_ERROR;
|
|
|
|
const LG_TransportStatus status = datagram ?
|
|
networkInputSendDatagramNL(input,
|
|
LG_NET_INPUT_MESSAGE_MOUSE_RELATIVE, sequence,
|
|
payload, sizeof(payload)) :
|
|
networkInputSubmitReliableNL(input,
|
|
LG_NET_INPUT_MESSAGE_MOUSE_RELATIVE,
|
|
acknowledge ? LG_NET_ENVELOPE_ACK_REQUIRED : 0,
|
|
acknowledge ? sequence : 0,
|
|
payload, sizeof(payload));
|
|
if (status != LG_TRANSPORT_OK)
|
|
return status;
|
|
|
|
input->sequence = sequence;
|
|
input->pointerSequence = sequence;
|
|
if (acknowledge)
|
|
input->reliableBarrierSequence = sequence;
|
|
return LG_TRANSPORT_OK;
|
|
}
|
|
|
|
static LG_TransportStatus networkInputTransmitAbsoluteNL(
|
|
NetworkInput * input, uint32_t x, uint32_t y,
|
|
uint32_t width, uint32_t height,
|
|
int32_t wheelX, int32_t wheelY, uint32_t buttons,
|
|
uint32_t changedButtons, bool datagram, bool acknowledge)
|
|
{
|
|
uint64_t sequence;
|
|
if (!networkInputNextSequenceNL(input, &sequence))
|
|
return LG_TRANSPORT_UNAVAILABLE;
|
|
|
|
LGNetInputAbsolute absolute =
|
|
{
|
|
.sequence = sequence,
|
|
.timestampNs = nanotime(),
|
|
.cumulativeX = input->cumulativeX,
|
|
.cumulativeY = input->cumulativeY,
|
|
.x = (int32_t)x,
|
|
.y = (int32_t)y,
|
|
.width = width,
|
|
.height = height,
|
|
.wheelX = wheelX,
|
|
.wheelY = wheelY,
|
|
.buttons = buttons,
|
|
.changedButtons = changedButtons,
|
|
};
|
|
if (!absolute.timestampNs)
|
|
absolute.timestampNs = 1;
|
|
|
|
uint8_t payload[LG_NET_INPUT_ABSOLUTE_WIRE_SIZE];
|
|
if (!lgNetInputAbsoluteEncode(payload, sizeof(payload), &absolute))
|
|
return LG_TRANSPORT_ERROR;
|
|
|
|
const LG_TransportStatus status = datagram ?
|
|
networkInputSendDatagramNL(input,
|
|
LG_NET_INPUT_MESSAGE_MOUSE_ABSOLUTE, sequence,
|
|
payload, sizeof(payload)) :
|
|
networkInputSubmitReliableNL(input,
|
|
LG_NET_INPUT_MESSAGE_MOUSE_ABSOLUTE,
|
|
acknowledge ? LG_NET_ENVELOPE_ACK_REQUIRED : 0,
|
|
acknowledge ? sequence : 0,
|
|
payload, sizeof(payload));
|
|
if (status != LG_TRANSPORT_OK)
|
|
return status;
|
|
|
|
input->sequence = sequence;
|
|
input->pointerSequence = sequence;
|
|
if (acknowledge)
|
|
input->reliableBarrierSequence = sequence;
|
|
return LG_TRANSPORT_OK;
|
|
}
|
|
|
|
static LG_TransportStatus networkInputFlushMotionNL(NetworkInput * input)
|
|
{
|
|
if (input->reliableCount)
|
|
return LG_TRANSPORT_UNAVAILABLE;
|
|
|
|
if (input->pendingAbsolute)
|
|
{
|
|
const bool acknowledge = input->motionBarrier ||
|
|
!input->mouseAbsolute;
|
|
const bool datagram = input->datagramAvailable &&
|
|
!input->pendingMotionReliable && !input->motionBarrier &&
|
|
!input->reliableBarrierSequence;
|
|
const LG_TransportStatus status = networkInputTransmitAbsoluteNL(input,
|
|
input->absoluteX, input->absoluteY,
|
|
input->absoluteWidth, input->absoluteHeight,
|
|
0, 0, input->mouseButtons, 0, datagram, acknowledge);
|
|
if (status != LG_TRANSPORT_OK)
|
|
return status;
|
|
input->mouseAbsolute = true;
|
|
input->pendingRelative = false;
|
|
input->pendingAbsolute = false;
|
|
input->pendingMotionReliable = false;
|
|
input->motionBarrier = false;
|
|
return LG_TRANSPORT_OK;
|
|
}
|
|
|
|
if (input->pendingRelative)
|
|
{
|
|
const bool acknowledge = input->motionBarrier ||
|
|
input->mouseAbsolute;
|
|
const bool datagram = input->datagramAvailable &&
|
|
!input->pendingMotionReliable && !input->motionBarrier &&
|
|
!input->reliableBarrierSequence;
|
|
const LG_TransportStatus status = networkInputTransmitRelativeNL(input,
|
|
0, 0, input->mouseButtons, 0, datagram, acknowledge);
|
|
if (status != LG_TRANSPORT_OK)
|
|
return status;
|
|
input->mouseAbsolute = false;
|
|
input->pendingRelative = false;
|
|
input->pendingAbsolute = false;
|
|
input->pendingMotionReliable = false;
|
|
input->motionBarrier = false;
|
|
}
|
|
return LG_TRANSPORT_OK;
|
|
}
|
|
|
|
static LG_TransportStatus networkInputSendRelativeNL(NetworkInput * input,
|
|
int32_t x, int32_t y, int32_t wheelX, int32_t wheelY,
|
|
uint32_t buttons, uint32_t changedButtons, bool motion)
|
|
{
|
|
if (motion)
|
|
{
|
|
const bool preserveReliable = input->pendingRelative &&
|
|
input->pendingMotionReliable;
|
|
if (!input->pendingRelative &&
|
|
(input->mouseAbsolute || input->pendingAbsolute))
|
|
{
|
|
const LG_TransportStatus baselineStatus =
|
|
networkInputTransmitAbsoluteNL(input,
|
|
input->absoluteX, input->absoluteY,
|
|
input->absoluteWidth, input->absoluteHeight,
|
|
0, 0, buttons, 0, false, true);
|
|
if (baselineStatus != LG_TRANSPORT_OK)
|
|
return baselineStatus;
|
|
|
|
input->pendingAbsolute = false;
|
|
input->mouseAbsolute = true;
|
|
}
|
|
input->cumulativeX =
|
|
networkInputSaturatingAdd(input->cumulativeX, x);
|
|
input->cumulativeY =
|
|
networkInputSaturatingAdd(input->cumulativeY, y);
|
|
input->pendingRelative = true;
|
|
input->pendingAbsolute = false;
|
|
input->pendingMotionReliable = preserveReliable || input->mouseAbsolute ||
|
|
input->motionBarrier || input->reliableCount;
|
|
return networkInputFlushMotionNL(input);
|
|
}
|
|
|
|
const LG_TransportStatus status = networkInputTransmitRelativeNL(input,
|
|
wheelX, wheelY, buttons, changedButtons, false, true);
|
|
if (status == LG_TRANSPORT_OK)
|
|
{
|
|
input->mouseAbsolute = false;
|
|
input->pendingRelative = false;
|
|
input->pendingAbsolute = false;
|
|
input->pendingMotionReliable = false;
|
|
input->motionBarrier = false;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
static LG_TransportStatus networkInputSendAbsoluteNL(NetworkInput * input,
|
|
uint32_t x, uint32_t y, uint32_t width, uint32_t height,
|
|
int32_t wheelX, int32_t wheelY, uint32_t buttons,
|
|
uint32_t changedButtons, bool motion)
|
|
{
|
|
input->absoluteX = x;
|
|
input->absoluteY = y;
|
|
input->absoluteWidth = width;
|
|
input->absoluteHeight = height;
|
|
|
|
if (motion)
|
|
{
|
|
const bool preserveReliable = input->pendingAbsolute &&
|
|
input->pendingMotionReliable;
|
|
const bool rebaseline = input->pendingRelative;
|
|
input->pendingRelative = false;
|
|
input->pendingAbsolute = true;
|
|
input->pendingMotionReliable = preserveReliable || rebaseline ||
|
|
!input->mouseAbsolute || input->motionBarrier || input->reliableCount;
|
|
return networkInputFlushMotionNL(input);
|
|
}
|
|
|
|
const LG_TransportStatus status = networkInputTransmitAbsoluteNL(input,
|
|
x, y, width, height, wheelX, wheelY,
|
|
buttons, changedButtons, false, true);
|
|
if (status == LG_TRANSPORT_OK)
|
|
{
|
|
input->mouseAbsolute = true;
|
|
input->pendingRelative = false;
|
|
input->pendingAbsolute = false;
|
|
input->pendingMotionReliable = false;
|
|
input->motionBarrier = false;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
static void networkInputResumeSendsNL(NetworkInput * input, bool * notify)
|
|
{
|
|
do
|
|
{
|
|
input->sendReadyPending = false;
|
|
if (!input->connected || !input->serviceAvailable || input->destroying)
|
|
return;
|
|
|
|
const LG_TransportStatus reliableStatus =
|
|
networkInputDrainReliableNL(input);
|
|
if (networkInputTransportFatal(reliableStatus))
|
|
{
|
|
*notify |= networkInputAbandonClaimNL(input, true);
|
|
return;
|
|
}
|
|
if (reliableStatus != LG_TRANSPORT_OK)
|
|
return;
|
|
if (input->reliableCount)
|
|
{
|
|
if (input->sendReadyPending)
|
|
continue;
|
|
if (networkConnectionAwaitInputSend(input->connection,
|
|
NETWORK_PACKET_DELIVERY_RELIABLE))
|
|
{
|
|
input->sendReadyPending = true;
|
|
continue;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!input->claimPending && !input->claimed)
|
|
{
|
|
networkInputSendClaimNL(input);
|
|
continue;
|
|
}
|
|
if (!input->claimed ||
|
|
(!input->pendingRelative && !input->pendingAbsolute))
|
|
continue;
|
|
|
|
const LG_TransportStatus status = networkInputFlushMotionNL(input);
|
|
if (networkInputTransportFatal(status))
|
|
{
|
|
*notify |= networkInputAbandonClaimNL(input, true);
|
|
return;
|
|
}
|
|
}
|
|
while (input->sendReadyPending);
|
|
}
|
|
|
|
void networkInputSendReady(NetworkInput * input,
|
|
NetworkPacketDelivery delivery)
|
|
{
|
|
if (!input || delivery == NETWORK_PACKET_DELIVERY_BULK)
|
|
return;
|
|
if (l_networkInputSending == input)
|
|
{
|
|
input->sendReadyPending = true;
|
|
return;
|
|
}
|
|
|
|
bool notify = false;
|
|
LG_LOCK(input->stateLock);
|
|
const bool motionReliable = input->reliableCount ||
|
|
input->pendingMotionReliable ||
|
|
input->motionBarrier || input->reliableBarrierSequence ||
|
|
!input->datagramAvailable;
|
|
const bool relevant = delivery == (motionReliable ?
|
|
NETWORK_PACKET_DELIVERY_RELIABLE :
|
|
NETWORK_PACKET_DELIVERY_DATAGRAM);
|
|
if (relevant || (!input->claimPending && !input->claimed))
|
|
networkInputResumeSendsNL(input, ¬ify);
|
|
LG_UNLOCK(input->stateLock);
|
|
if (notify)
|
|
networkInputNotifyStatus(input);
|
|
}
|
|
|
|
void networkInputDatagramLost(NetworkInput * input,
|
|
uint64_t claimEpoch, uint64_t pointerSequence)
|
|
{
|
|
if (!input)
|
|
return;
|
|
|
|
bool notify = false;
|
|
LG_LOCK(input->stateLock);
|
|
if (!input->destroying && input->connected &&
|
|
input->serviceAvailable && input->claimed &&
|
|
input->claimEpoch == claimEpoch && pointerSequence &&
|
|
input->pointerSequence == pointerSequence &&
|
|
!input->motionBarrier && !input->reliableBarrierSequence)
|
|
{
|
|
if (!input->pendingRelative && !input->pendingAbsolute)
|
|
{
|
|
input->pendingRelative = !input->mouseAbsolute;
|
|
input->pendingAbsolute = input->mouseAbsolute;
|
|
}
|
|
input->pendingMotionReliable = true;
|
|
input->motionBarrier = true;
|
|
networkInputResumeSendsNL(input, ¬ify);
|
|
}
|
|
LG_UNLOCK(input->stateLock);
|
|
|
|
if (notify)
|
|
networkInputNotifyStatus(input);
|
|
}
|
|
|
|
static bool networkInputMouseMotion(void * opaque, int32_t x, int32_t y)
|
|
{
|
|
NetworkInput * input = opaque;
|
|
LG_LOCK(input->stateLock);
|
|
const bool eligible = input->available &&
|
|
(input->claimFlags & LG_NET_INPUT_CLAIM_POINTER);
|
|
LG_TransportStatus status = LG_TRANSPORT_UNAVAILABLE;
|
|
if (eligible)
|
|
status = networkInputSendRelativeNL(input, x, y, 0, 0,
|
|
input->mouseButtons, 0, true);
|
|
bool result = eligible &&
|
|
!networkInputTransportFatal(status);
|
|
bool notify = false;
|
|
if (input->sendReadyPending)
|
|
networkInputResumeSendsNL(input, ¬ify);
|
|
result &= input->available;
|
|
LG_UNLOCK(input->stateLock);
|
|
if (notify)
|
|
networkInputNotifyStatus(input);
|
|
return result;
|
|
}
|
|
|
|
static bool networkInputMousePosition(void * opaque, uint32_t x,
|
|
uint32_t y, uint32_t width, uint32_t height)
|
|
{
|
|
if (!width || !height ||
|
|
width > LG_NET_INPUT_ABSOLUTE_MAX_WIDTH ||
|
|
height > LG_NET_INPUT_ABSOLUTE_MAX_HEIGHT ||
|
|
x >= width || y >= height)
|
|
return false;
|
|
|
|
NetworkInput * input = opaque;
|
|
LG_LOCK(input->stateLock);
|
|
const bool eligible = input->available &&
|
|
(input->claimFlags & LG_NET_INPUT_CLAIM_POINTER);
|
|
LG_TransportStatus status = LG_TRANSPORT_UNAVAILABLE;
|
|
if (eligible)
|
|
status = networkInputSendAbsoluteNL(input, x, y, width, height, 0, 0,
|
|
input->mouseButtons, 0, true);
|
|
bool result = eligible &&
|
|
!networkInputTransportFatal(status);
|
|
bool notify = false;
|
|
if (input->sendReadyPending)
|
|
networkInputResumeSendsNL(input, ¬ify);
|
|
result &= input->available;
|
|
LG_UNLOCK(input->stateLock);
|
|
if (notify)
|
|
networkInputNotifyStatus(input);
|
|
return result;
|
|
}
|
|
|
|
static uint32_t networkInputButtonMask(unsigned int button)
|
|
{
|
|
switch (button)
|
|
{
|
|
case 1:
|
|
return LG_NET_POINTER_BUTTON_LEFT;
|
|
|
|
case 2:
|
|
return LG_NET_POINTER_BUTTON_MIDDLE;
|
|
|
|
case 3:
|
|
return LG_NET_POINTER_BUTTON_RIGHT;
|
|
|
|
case 6:
|
|
return LG_NET_POINTER_BUTTON_BACK;
|
|
|
|
case 7:
|
|
return LG_NET_POINTER_BUTTON_FORWARD;
|
|
|
|
default:
|
|
if (button >= 8 && button < 32)
|
|
return UINT32_C(1) << (button - 3U);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static bool networkInputUpdateButton(void * opaque,
|
|
unsigned int button, bool down)
|
|
{
|
|
NetworkInput * input = opaque;
|
|
bool notify = false;
|
|
LG_LOCK(input->stateLock);
|
|
if (!input->available ||
|
|
!(input->claimFlags & LG_NET_INPUT_CLAIM_POINTER))
|
|
{
|
|
LG_UNLOCK(input->stateLock);
|
|
return false;
|
|
}
|
|
|
|
int32_t wheelY = 0;
|
|
if (button == 4 || button == 5)
|
|
{
|
|
if (!down)
|
|
{
|
|
LG_UNLOCK(input->stateLock);
|
|
return true;
|
|
}
|
|
wheelY = button == 4 ? 1 : -1;
|
|
}
|
|
|
|
const uint32_t mask = networkInputButtonMask(button);
|
|
if (!mask && !wheelY)
|
|
{
|
|
LG_UNLOCK(input->stateLock);
|
|
return false;
|
|
}
|
|
|
|
const uint32_t buttons = down ?
|
|
input->mouseButtons | mask : input->mouseButtons & ~mask;
|
|
LG_TransportStatus status;
|
|
const bool absolute = input->pendingAbsolute ||
|
|
(input->mouseAbsolute && !input->pendingRelative);
|
|
if (absolute && input->absoluteWidth &&
|
|
input->absoluteHeight)
|
|
status = networkInputSendAbsoluteNL(input,
|
|
input->absoluteX, input->absoluteY,
|
|
input->absoluteWidth, input->absoluteHeight,
|
|
0, wheelY, buttons, mask, false);
|
|
else
|
|
status = networkInputSendRelativeNL(input, 0, 0, 0, wheelY,
|
|
buttons, mask, false);
|
|
if (status == LG_TRANSPORT_OK && mask)
|
|
input->mouseButtons = buttons;
|
|
else if (networkInputTransportFatal(status) && !down && mask)
|
|
notify = networkInputAbandonClaimNL(input, true);
|
|
if (input->sendReadyPending)
|
|
networkInputResumeSendsNL(input, ¬ify);
|
|
LG_UNLOCK(input->stateLock);
|
|
if (notify)
|
|
networkInputNotifyStatus(input);
|
|
return status == LG_TRANSPORT_OK;
|
|
}
|
|
|
|
static bool networkInputMousePress(void * opaque, unsigned int button)
|
|
{
|
|
return networkInputUpdateButton(opaque, button, true);
|
|
}
|
|
|
|
static bool networkInputMouseRelease(void * opaque, unsigned int button)
|
|
{
|
|
return networkInputUpdateButton(opaque, button, false);
|
|
}
|
|
|
|
static void networkInputReset(void * opaque)
|
|
{
|
|
NetworkInput * input = opaque;
|
|
bool notify = false;
|
|
LG_LOCK(input->stateLock);
|
|
notify = networkInputSendResetNL(input);
|
|
if (input->sendReadyPending)
|
|
networkInputResumeSendsNL(input, ¬ify);
|
|
LG_UNLOCK(input->stateLock);
|
|
if (notify)
|
|
networkInputNotifyStatus(input);
|
|
}
|
|
|
|
static const LG_InputOps NETWORK_INPUT_OPS =
|
|
{
|
|
.name = "Network",
|
|
.supports = networkInputSupports,
|
|
.setStatusListener = networkInputSetStatusListener,
|
|
.keyDown = networkInputKeyDown,
|
|
.keyUp = networkInputKeyUp,
|
|
.keyboardLEDs = networkInputKeyboardLEDs,
|
|
.keyboardLEDsReset = networkInputKeyboardLEDsReset,
|
|
.mouseMotion = networkInputMouseMotion,
|
|
.mousePosition = networkInputMousePosition,
|
|
.mousePress = networkInputMousePress,
|
|
.mouseRelease = networkInputMouseRelease,
|
|
.reset = networkInputReset,
|
|
};
|
|
|
|
bool networkInputCreate(NetworkInput ** result,
|
|
NetworkConnection * connection)
|
|
{
|
|
if (!result || !connection)
|
|
return false;
|
|
|
|
NetworkInput * input = calloc(1, sizeof(*input));
|
|
if (!input)
|
|
return false;
|
|
|
|
LG_LOCK_INIT(input->stateLock);
|
|
input->connection = connection;
|
|
input->claimantID = nanotime() ^
|
|
((uint64_t)(uintptr_t)input << 17);
|
|
input->statusGeneration = 1;
|
|
if (!input->claimantID)
|
|
input->claimantID = 1;
|
|
|
|
input->statusIdle = lgCreateEvent(false, 0);
|
|
if (!input->statusIdle)
|
|
{
|
|
LG_LOCK_FREE(input->stateLock);
|
|
free(input);
|
|
return false;
|
|
}
|
|
lgSignalEvent(input->statusIdle);
|
|
|
|
if (!lgCreateTimer(NETWORK_INPUT_TIMER_MS,
|
|
networkInputTimer, input, &input->timer))
|
|
{
|
|
lgFreeEvent(input->statusIdle);
|
|
LG_LOCK_FREE(input->stateLock);
|
|
free(input);
|
|
return false;
|
|
}
|
|
|
|
*result = input;
|
|
return true;
|
|
}
|
|
|
|
void networkInputDestroy(NetworkInput ** handle)
|
|
{
|
|
if (!handle || !*handle)
|
|
return;
|
|
|
|
NetworkInput * input = *handle;
|
|
LG_LOCK(input->stateLock);
|
|
input->destroying = true;
|
|
LG_UNLOCK(input->stateLock);
|
|
lgTimerDestroy(input->timer);
|
|
|
|
networkInputSetStatusListener(input, NULL, NULL);
|
|
LG_LOCK(input->stateLock);
|
|
networkInputSendResetNL(input);
|
|
networkInputSendReleaseNL(input);
|
|
networkInputClearReliableNL(input);
|
|
input->connected = false;
|
|
input->serviceAvailable = false;
|
|
input->datagramAvailable = false;
|
|
networkInputSetAvailableNL(input, false);
|
|
LG_UNLOCK(input->stateLock);
|
|
|
|
lgFreeEvent(input->statusIdle);
|
|
LG_LOCK_FREE(input->stateLock);
|
|
free(input);
|
|
*handle = NULL;
|
|
}
|
|
|
|
const LG_InputOps * networkInputGetOps(void)
|
|
{
|
|
return &NETWORK_INPUT_OPS;
|
|
}
|
|
|
|
void networkInputConnectionState(NetworkInput * input, bool connected)
|
|
{
|
|
if (!input)
|
|
return;
|
|
|
|
uint16_t serviceVersion = 0;
|
|
const bool serviceAvailable = connected &&
|
|
networkConnectionServiceAvailable(input->connection,
|
|
LG_NET_SERVICE_INPUT, LG_NET_CAPABILITY_RELIABLE) &&
|
|
networkConnectionServiceVersion(input->connection,
|
|
LG_NET_SERVICE_INPUT, &serviceVersion);
|
|
const bool datagramAvailable = serviceAvailable &&
|
|
networkConnectionServiceAvailable(input->connection,
|
|
LG_NET_SERVICE_INPUT, LG_NET_CAPABILITY_DATAGRAM);
|
|
bool notify = false;
|
|
|
|
LG_LOCK(input->stateLock);
|
|
if (!connected || !serviceAvailable)
|
|
{
|
|
notify |= networkInputSendResetNL(input);
|
|
networkInputSendReleaseNL(input);
|
|
networkInputClearReliableNL(input);
|
|
input->connected = connected;
|
|
input->serviceAvailable = false;
|
|
input->datagramAvailable = false;
|
|
input->serviceVersion = 0;
|
|
networkInputInvalidateLEDsNL(input);
|
|
notify |=
|
|
networkInputSetAvailableNL(input, false);
|
|
}
|
|
else
|
|
{
|
|
input->connected = true;
|
|
input->serviceAvailable = true;
|
|
input->datagramAvailable = datagramAvailable;
|
|
input->serviceVersion = serviceVersion;
|
|
input->claimPending = false;
|
|
input->claimed = false;
|
|
input->claimFlags = 0;
|
|
input->sequence = 0;
|
|
input->nextClaimNs = 0;
|
|
networkInputClearReliableNL(input);
|
|
networkInputClearStateNL(input);
|
|
networkInputInvalidateLEDsNL(input);
|
|
notify |= networkInputSetAvailableNL(input, false);
|
|
networkInputSendClaimNL(input);
|
|
}
|
|
if (input->sendReadyPending)
|
|
networkInputResumeSendsNL(input, ¬ify);
|
|
LG_UNLOCK(input->stateLock);
|
|
|
|
if (notify)
|
|
networkInputNotifyStatus(input);
|
|
}
|
|
|
|
static bool networkInputHandleStatus(NetworkInput * input,
|
|
const LGNetPacketView * packet)
|
|
{
|
|
LGNetInputStatus status;
|
|
if (lgNetInputStatusDecode(&status,
|
|
packet->payload, packet->payloadLength) != LG_NET_PARSE_OK)
|
|
return false;
|
|
if (packet->envelope.componentEpoch != status.claimEpoch)
|
|
return false;
|
|
const bool barrierAck =
|
|
packet->envelope.messageType == LG_NET_INPUT_MESSAGE_STATUS &&
|
|
(packet->envelope.flags & LG_NET_ENVELOPE_RESPONSE) &&
|
|
packet->envelope.requestID &&
|
|
packet->envelope.requestID <= status.lastSequence;
|
|
|
|
bool notify = false;
|
|
LG_LOCK(input->stateLock);
|
|
if (!input->connected || status.claimEpoch != input->claimEpoch)
|
|
{
|
|
LG_UNLOCK(input->stateLock);
|
|
return true;
|
|
}
|
|
|
|
switch (status.status)
|
|
{
|
|
case LG_NET_INPUT_STATUS_ACCEPTED:
|
|
input->claimPending = false;
|
|
input->claimed =
|
|
(status.flags & NETWORK_INPUT_CLAIM_FLAGS) ==
|
|
NETWORK_INPUT_CLAIM_FLAGS;
|
|
input->claimFlags = input->claimed ? status.flags : 0;
|
|
input->nextKeepaliveNs = networkInputDeadline(
|
|
status.leaseRemainingMs > NETWORK_INPUT_KEEPALIVE_MS ?
|
|
NETWORK_INPUT_KEEPALIVE_MS :
|
|
status.leaseRemainingMs > 1U ?
|
|
status.leaseRemainingMs / 2U : 1U);
|
|
if (!input->claimed)
|
|
{
|
|
input->nextClaimNs = networkInputDeadline(
|
|
NETWORK_INPUT_CLAIM_RETRY_MS);
|
|
networkInputClearReliableNL(input);
|
|
networkInputClearStateNL(input);
|
|
networkInputInvalidateLEDsNL(input);
|
|
}
|
|
notify = networkInputSetAvailableNL(input, input->claimed);
|
|
break;
|
|
|
|
case LG_NET_INPUT_STATUS_UNSUPPORTED:
|
|
input->claimPending = false;
|
|
input->claimed = false;
|
|
input->claimFlags = 0;
|
|
input->serviceAvailable = false;
|
|
input->datagramAvailable = false;
|
|
networkInputClearReliableNL(input);
|
|
networkInputClearStateNL(input);
|
|
networkInputInvalidateLEDsNL(input);
|
|
notify = networkInputSetAvailableNL(input, false);
|
|
break;
|
|
|
|
case LG_NET_INPUT_STATUS_REJECTED:
|
|
case LG_NET_INPUT_STATUS_RELEASED:
|
|
case LG_NET_INPUT_STATUS_EXPIRED:
|
|
case LG_NET_INPUT_STATUS_ERROR:
|
|
input->claimPending = false;
|
|
input->claimed = false;
|
|
input->claimFlags = 0;
|
|
networkInputClearReliableNL(input);
|
|
networkInputClearStateNL(input);
|
|
networkInputInvalidateLEDsNL(input);
|
|
input->nextClaimNs = networkInputDeadline(
|
|
NETWORK_INPUT_CLAIM_RETRY_MS);
|
|
notify = networkInputSetAvailableNL(input, false);
|
|
break;
|
|
}
|
|
if (status.status == LG_NET_INPUT_STATUS_ACCEPTED && barrierAck &&
|
|
input->reliableBarrierSequence &&
|
|
packet->envelope.requestID >= input->reliableBarrierSequence)
|
|
{
|
|
input->reliableBarrierSequence = 0;
|
|
networkInputResumeSendsNL(input, ¬ify);
|
|
}
|
|
if (input->sendReadyPending)
|
|
networkInputResumeSendsNL(input, ¬ify);
|
|
LG_UNLOCK(input->stateLock);
|
|
|
|
if (notify)
|
|
networkInputNotifyStatus(input);
|
|
return true;
|
|
}
|
|
|
|
static bool networkInputHandleLEDs(NetworkInput * input,
|
|
const LGNetPacketView * packet)
|
|
{
|
|
LGNetInputLEDs leds;
|
|
if (lgNetInputLEDsDecode(&leds,
|
|
packet->payload, packet->payloadLength) != LG_NET_PARSE_OK)
|
|
return false;
|
|
|
|
bool notify = false;
|
|
LG_LOCK(input->stateLock);
|
|
if (!input->connected || !input->claimed ||
|
|
packet->envelope.componentEpoch != input->claimEpoch ||
|
|
leds.sequence <= input->keyboardLEDSequence)
|
|
{
|
|
LG_UNLOCK(input->stateLock);
|
|
return true;
|
|
}
|
|
|
|
const bool oldValid = input->keyboardLEDsValid;
|
|
const uint8_t oldState = input->keyboardLEDs;
|
|
const uint32_t oldValidMask = input->keyboardLEDsValidMask;
|
|
const uint32_t mergedState =
|
|
(input->keyboardLEDs & ~leds.validMask) | leds.state;
|
|
const uint32_t changed =
|
|
(input->keyboardLEDs ^ mergedState) &
|
|
leds.validMask & oldValidMask;
|
|
|
|
input->keyboardLEDSequence = leds.sequence;
|
|
input->keyboardLEDsValidMask |= leds.validMask;
|
|
input->keyboardLEDs = (uint8_t)mergedState;
|
|
input->keyboardLEDsValid =
|
|
(input->keyboardLEDsValidMask & NETWORK_INPUT_LED_MASK) ==
|
|
NETWORK_INPUT_LED_MASK;
|
|
networkInputAcknowledgeLEDsNL(input, changed);
|
|
networkInputSyncKeyboardLEDsNL(input, ¬ify);
|
|
if (input->sendReadyPending)
|
|
networkInputResumeSendsNL(input, ¬ify);
|
|
notify |= oldValid != input->keyboardLEDsValid ||
|
|
oldState != input->keyboardLEDs;
|
|
if (notify)
|
|
input->statusNotifyPending = true;
|
|
LG_UNLOCK(input->stateLock);
|
|
|
|
if (notify)
|
|
networkInputNotifyStatus(input);
|
|
return true;
|
|
}
|
|
|
|
bool networkInputPacket(NetworkInput * input,
|
|
const LGNetPacketView * packet, bool datagram)
|
|
{
|
|
if (!input || !packet || datagram ||
|
|
packet->envelope.service != LG_NET_SERVICE_INPUT)
|
|
return false;
|
|
|
|
const LGNetEnvelopeFlags baseFlags =
|
|
LG_NET_ENVELOPE_RELIABLE | LG_NET_ENVELOPE_FINAL;
|
|
switch (packet->envelope.messageType)
|
|
{
|
|
case LG_NET_INPUT_MESSAGE_CLAIM_RESULT:
|
|
if (packet->envelope.flags !=
|
|
(baseFlags | LG_NET_ENVELOPE_RESPONSE) ||
|
|
packet->envelope.requestID != packet->envelope.componentEpoch)
|
|
return false;
|
|
return networkInputHandleStatus(input, packet);
|
|
|
|
case LG_NET_INPUT_MESSAGE_STATUS:
|
|
{
|
|
const LGNetEnvelopeFlags expectedFlags = packet->envelope.requestID ?
|
|
baseFlags | LG_NET_ENVELOPE_RESPONSE : baseFlags;
|
|
if (packet->envelope.flags != expectedFlags)
|
|
return false;
|
|
return networkInputHandleStatus(input, packet);
|
|
}
|
|
|
|
case LG_NET_INPUT_MESSAGE_KEYBOARD_LEDS:
|
|
if (packet->envelope.flags != baseFlags ||
|
|
packet->envelope.requestID)
|
|
return false;
|
|
return networkInputHandleLEDs(input, packet);
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|