--- a/src/InputStream.c +++ b/src/InputStream.c @@ -86,11 +86,16 @@ int deltaX, deltaY; bool dirty; // Update ready to send (queued packet holder in packetQueue) } currentRelativeMouseState; -static struct { - int x, y; - int width, height; - bool dirty; // Update ready to send (queued packet holder in packetQueue) -} currentAbsoluteMouseState; +// Only this packet may absorb newer positions. A button event seals the batch. +static PPACKET_HOLDER currentQueuedAbsoluteMousePacket; + +static void latchAbsoluteMousePacket(PPACKET_HOLDER holder) { + PltLockMutex(&batchedInputMutex); + if (currentQueuedAbsoluteMousePacket == holder) { + currentQueuedAbsoluteMousePacket = NULL; + } + PltUnlockMutex(&batchedInputMutex); +} // Initializes the input stream int initializeInputStream(void) { @@ -120,7 +125,7 @@ memset(currentGamepadSensorState, 0, sizeof(currentGamepadSensorState)); memset(¤tRelativeMouseState, 0, sizeof(currentRelativeMouseState)); - memset(¤tAbsoluteMouseState, 0, sizeof(currentAbsoluteMouseState)); + currentQueuedAbsoluteMousePacket = NULL; PltCreateMutex(&batchedInputMutex); return 0; @@ -444,24 +449,9 @@ now = PltGetMillis(); } - PltLockMutex(&batchedInputMutex); - - // Populate the packet with the latest state - holder->packet.mouseMoveAbs.x = BE16(currentAbsoluteMouseState.x); - holder->packet.mouseMoveAbs.y = BE16(currentAbsoluteMouseState.y); - - // There appears to be a rounding error in GFE's scaling calculation which prevents - // the cursor from reaching the far edge of the screen when streaming at smaller - // resolutions with a higher desktop resolution (like streaming 720p with a desktop - // resolution of 1080p, or streaming 720p/1080p with a desktop resolution of 4K). - // Subtracting one from the reference dimensions seems to work around this issue. - holder->packet.mouseMoveAbs.width = BE16(currentAbsoluteMouseState.width - 1); - holder->packet.mouseMoveAbs.height = BE16(currentAbsoluteMouseState.height - 1); - - // The state change is no longer pending - currentAbsoluteMouseState.dirty = false; - - PltUnlockMutex(&batchedInputMutex); + // Payload was filled by the producer under the same mutex. Latch + // only our packet; a later batch may already be queued after a click. + latchAbsoluteMousePacket(holder); lastMousePacketTime = now; } @@ -780,52 +770,37 @@ } PltLockMutex(&batchedInputMutex); - - // Overwrite the previous mouse location with the new one - currentAbsoluteMouseState.x = x; - currentAbsoluteMouseState.y = y; - currentAbsoluteMouseState.width = referenceWidth; - currentAbsoluteMouseState.height = referenceHeight; - - // Queue a packet holder if this is the only pending absolute mouse event - if (!currentAbsoluteMouseState.dirty) { - // Set the dirty flag to claim ownership of inserting the packet holder - // and unlock to allow other threads to enqueue or process input. - currentAbsoluteMouseState.dirty = true; - PltUnlockMutex(&batchedInputMutex); - + holder = currentQueuedAbsoluteMousePacket; + bool newPacket = holder == NULL; + if (newPacket) { holder = allocatePacketHolder(0); if (holder == NULL) { - currentAbsoluteMouseState.dirty = false; + PltUnlockMutex(&batchedInputMutex); return -1; } - holder->channelId = CTRL_CHANNEL_MOUSE; - - // TODO: Send this as unreliable sequenced when we have a delayed reliable retransmission thread holder->enetPacketFlags = ENET_PACKET_FLAG_RELIABLE; - holder->packet.mouseMoveAbs.header.size = BE32(sizeof(NV_ABS_MOUSE_MOVE_PACKET) - sizeof(uint32_t)); holder->packet.mouseMoveAbs.header.magic = LE32(MOUSE_MOVE_ABS_MAGIC); holder->packet.mouseMoveAbs.unused = 0; - - // Remaining fields are set in the input thread based on the latest currentAbsoluteMouseState values - + } + holder->packet.mouseMoveAbs.x = BE16(x); + holder->packet.mouseMoveAbs.y = BE16(y); + // Preserve upstream's far-edge rounding workaround. + holder->packet.mouseMoveAbs.width = BE16(referenceWidth - 1); + holder->packet.mouseMoveAbs.height = BE16(referenceHeight - 1); + err = 0; + if (newPacket) { + currentQueuedAbsoluteMousePacket = holder; err = LbqOfferQueueItem(&packetQueue, holder, &holder->entry); if (err != LBQ_SUCCESS) { + currentQueuedAbsoluteMousePacket = NULL; LC_ASSERT(err == LBQ_BOUND_EXCEEDED); Limelog("Input queue reached maximum size limit\n"); freePacketHolder(holder); - - // We weren't able to insert the entry, so let the next call try again - currentAbsoluteMouseState.dirty = false; - } - } - else { - // There's already a packet holder queued to send this event - PltUnlockMutex(&batchedInputMutex); - err = 0; - } + } + } + PltUnlockMutex(&batchedInputMutex); // This is not thread safe, but it's not a big deal because callers that want to // use LiSendRelativeMotionAsMousePositionEvent() must not mix these function @@ -872,7 +847,11 @@ holder->packet.mouseButton.header.magic = LE32(holder->packet.mouseButton.header.magic); holder->packet.mouseButton.button = (uint8_t)button; + PltLockMutex(&batchedInputMutex); + // Do not let a later position overwrite the move preceding this click. + currentQueuedAbsoluteMousePacket = NULL; err = LbqOfferQueueItem(&packetQueue, holder, &holder->entry); + PltUnlockMutex(&batchedInputMutex); if (err != LBQ_SUCCESS) { LC_ASSERT(err == LBQ_BOUND_EXCEEDED); Limelog("Input queue reached maximum size limit\n");