Skip to content

Add LiSendResolutionChangeRequest for client-driven dynamic resolution - #10

Open
AsafMah wants to merge 1 commit into
qiin2333:micfrom
AsafMah:feat/client-resolution-request
Open

AsafMah wants to merge 1 commit into
qiin2333:micfrom
AsafMah:feat/client-resolution-request

Conversation

@AsafMah

@AsafMah AsafMah commented Jun 14, 2026 •

Copy link
Copy Markdown

Summary

Adds LiSendResolutionChangeRequest(width, height), a small capability-gated API that lets a client ask a Sunshine host to change the stream resolution mid-session. It is the client→host half of client-driven dynamic resolution; the host→client half (0x5506/0x5507 → resolutionChanged) already exists on mic.

Purely additive: nothing changes for clients that don't call it.

Details

  • Sends control packet 0x5506 (IDX_DYNAMIC_PARAM_CHANGE) with param type 0 (RESOLUTION) and a little-endian [int32 0, int32 width, int32 height] payload, the same layout the receive path already parses.
  • Capability: new feature flag LI_FF_CLIENT_RESOLUTION_CHANGE (0x400), which the host advertises in the RTSP x-ss-general.featureFlags (Client-driven dynamic resolution: gate, advertise, and safely apply 0x5506 RESOLUTION requests AlkaidLab/foundation-sunshine#729). Clients check LiGetHostFeatureFlags() & LI_FF_CLIENT_RESOLUTION_CHANGE, and the sender also gates on it itself, the same way LiSetCursorMode() gates on LI_FF_CURSOR_SHAPE.
  • Placed next to LiSendClientSdrWhiteNits (the other 0x5506 sender), with the same named return codes: LI_RESOLUTION_CHANGE_OK (0), _ERR_INVALID (-1), _ERR_UNSUPPORTED (-2, flag not advertised), _ERR_NOT_CONNECTED (-3), _ERR_SEND_FAILED (-4).
  • Odd sizes are rounded down to even; anything < 2 or > 16384 after rounding is rejected before anything is sent.

Testing

  • New resolution-change-request-tests (built in static-library configurations, like the existing tests that include a source file): drives the real sender over a loopback ENet connection and checks the exact bytes the host receives and every return code. With the feature flag clear, the call returns -2 and nothing is sent.
  • Builds clean and all tests pass with MinGW-w64 gcc (static and shared) and MSVC 2022 (/W3 /WX, static). The touched files are also clean under -Wall -Wextra -Werror.

Consumers: qiin2333/moonlight-qt#104 and qiin2333/moonlight-vplus#354 (host: AlkaidLab/foundation-sunshine#729, issue AlkaidLab/foundation-sunshine#728). Teardown hardening for all public senders is split out into #13; this PR doesn't depend on it, and the two merge cleanly in either order.


This PR description was drafted with an AI assistant and reviewed by me before posting.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds an outbound client→host control-stream API for requesting mid-stream resolution changes (Sunshine dynamic parameter change 0x5506), and includes additional control-stream teardown race hardening to support safe external callers.

Changes:

  • Adds a new public API LiSendResolutionChangeRequest(width, height) to request a host-applied resolution change via control packet 0x5506.
  • Implements the request sender in ControlStream.c, including even-dimension rounding, dimension validation, and host-generation/packet-type gating.
  • Hardens ENet send/teardown concurrency by adding locked re-checks in sendMessageEnet() and tightening teardown ordering/locking in stopControlStream().

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/Limelight.h Adds the public API declaration and documents the wire format and error contract.
src/ControlStream.c Implements the new request sender and adjusts ENet send/teardown logic to reduce teardown races.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/ControlStream.c Outdated
Comment on lines +1022 to +1029
// Re-check teardown state after relock. stopControlStream() may have
// set stopping (and may now be running gracefullyDisconnectEnetPeer or
// have already nulled peer/client) while we were sleeping. Bail before
// any enet_host_service() or peer/client dereference. The packet was
// already queued via enet_peer_send(); the linger phase will handle it.
if (stopping || peer == NULL || client == NULL) {
break;
}
AsafMah added a commit to AsafMah/moonlight-common-c that referenced this pull request Jun 15, 2026
The reliable-send backpressure loop breaks when peer/client are nulled by a
concurrent stopControlStream(), but the post-loop latency-warning check still
dereferenced peer->state / peer->roundTripTime / peer->packetLoss without
re-confirming peer != NULL, so a teardown race could still crash there. Add the
NULL guard. Addresses Copilot review on qiin2333#10.
@AsafMah

AsafMah commented Jun 15, 2026

Copy link
Copy Markdown
Author

Good catch, fixed in e3e02d0. The backpressure loop bails on peer == NULL, but the post-loop latency-warning check still dereferenced peer->state/peer->roundTripTime/peer->packetLoss while err >= 0 remained true — so a teardown race could crash exactly there. Added a peer != NULL guard to that condition (peer is still read under enetMutex). Verified it still compiles clean (gcc -fsyntax-only -Wall).

@AsafMah

AsafMah commented Jun 28, 2026

Copy link
Copy Markdown
Author

@Yundi339 — addressed the moonlight-common-c concern directly: I've split this PR.

This PR is now just the minimal additive API — LiSendResolutionChangeRequest, one capability-gated function modeled on LiSendClipboardData (now +66/-0, no behavior change for any client that doesn't call it, so other Moonlight forks inherit nothing to reason about). That's the "new interface for quick adjustment of screen resolution" you suggested, with the smallest possible shared-lib footprint.

The ENet teardown-race hardening that touched the shared send path — the part I believe you were uncomfortable with — is moved out to its own PR (#13), to be reviewed independently. It's a general robustness fix, not required for this API (which rides the same baseline as the other LiSend* senders).

If you'd rather the interface be generic (e.g. LiSendDynamicParamChange(type, …) so future params reuse one entry point) instead of resolution-specific, I'm happy to reshape it — just say the word.


This comment was drafted with an AI assistant and reviewed by me before posting.

Adds a capability-gated outbound control-stream API that asks a Sunshine
host to switch the stream resolution: dynamic parameter change 0x5506 with
the RESOLUTION type and a little-endian [int32 0, int32 width, int32 height]
payload, the same layout the receive path already parses. The host reports
the mode it applied through the existing resolutionChanged callback.

Purely additive; nothing changes for clients that don't call it. Hosts
advertise support with the new LI_FF_CLIENT_RESOLUTION_CHANGE (0x400)
feature flag, and the sender gates on it the same way LiSetCursorMode()
gates on LI_FF_CURSOR_SHAPE. Return codes are named LI_RESOLUTION_CHANGE_*
(0, -1..-4).

resolution-change-request-tests (static-library builds) drives the sender
over a loopback ENet connection and checks the exact wire bytes and each
return code.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c4d1c1b8-da0b-4728-a65c-4fbd45805c38
@AsafMah
AsafMah force-pushed the feat/client-resolution-request branch from 6148787 to fcedd03 Compare October 19, 2026 19:12
@coderabbitai

coderabbitai Bot commented Oct 19, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: a7f94010-e96f-4d28-9773-e9c5f38e7779

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

AsafMah added a commit to AsafMah/moonlight-qt that referenced this pull request Oct 19, 2026
…ests

Point moonlight-common-c at the AsafMah fork's integration/client-resolution
branch (mic b91c7cd + qiin2333/moonlight-common-c#10 + qiin2333#13). It adds
LiSendResolutionChangeRequest() and LI_FF_CLIENT_RESOLUTION_CHANGE, and
hardens control-stream sends against concurrent teardown.

Temporary: switch the URL back to qiin2333/moonlight-common-c once those
changes land on mic.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: f59af104-6c3a-4e24-a2d4-f14be3792803
AsafMah added a commit to AsafMah/moonlight-vplus that referenced this pull request Oct 19, 2026
Temporarily points the submodule at the AsafMah fork (mic + qiin2333/moonlight-common-c#10
+ qiin2333#13, which add LiSendResolutionChangeRequest and LI_FF_CLIENT_RESOLUTION_CHANGE). Re-point
to qiin2333/moonlight-common-c mic once those land.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3725ad1b-ca0b-4b06-9103-f1d35408fa88
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants