Add session input policy control stream support

This commit is contained in:
Joey Yakimowich-Payne 2026-02-12 00:37:54 -07:00
commit 611a2e7f8f
3 changed files with 69 additions and 1 deletions

View file

@ -83,6 +83,12 @@ typedef struct _QUEUED_ASYNC_CALLBACK {
uint8_t left[DS_EFFECT_PAYLOAD_SIZE]; uint8_t left[DS_EFFECT_PAYLOAD_SIZE];
uint8_t right[DS_EFFECT_PAYLOAD_SIZE]; uint8_t right[DS_EFFECT_PAYLOAD_SIZE];
} dsAdaptiveTrigger; } dsAdaptiveTrigger;
struct {
uint8_t allowKeyboard;
uint8_t allowMouse;
uint8_t allowGamepad;
uint8_t reason;
} setInputPolicy;
} data; } data;
LINKED_BLOCKING_QUEUE_ENTRY entry; LINKED_BLOCKING_QUEUE_ENTRY entry;
} QUEUED_ASYNC_CALLBACK, *PQUEUED_ASYNC_CALLBACK; } QUEUED_ASYNC_CALLBACK, *PQUEUED_ASYNC_CALLBACK;
@ -140,6 +146,7 @@ static PPLT_CRYPTO_CONTEXT decryptionCtx;
#define IDX_SET_MOTION_EVENT 10 #define IDX_SET_MOTION_EVENT 10
#define IDX_SET_RGB_LED 11 #define IDX_SET_RGB_LED 11
#define IDX_DS_ADAPTIVE_TRIGGERS 12 #define IDX_DS_ADAPTIVE_TRIGGERS 12
#define IDX_SET_INPUT_POLICY 13
#define CONTROL_STREAM_TIMEOUT_SEC 10 #define CONTROL_STREAM_TIMEOUT_SEC 10
#define CONTROL_STREAM_LINGER_TIMEOUT_SEC 2 #define CONTROL_STREAM_LINGER_TIMEOUT_SEC 2
@ -157,6 +164,7 @@ static const short packetTypesGen3[] = {
-1, // Rumble triggers (unused) -1, // Rumble triggers (unused)
-1, // Set motion event (unused) -1, // Set motion event (unused)
-1, // Set RGB LED (unused) -1, // Set RGB LED (unused)
-1,
}; };
static const short packetTypesGen4[] = { static const short packetTypesGen4[] = {
0x0606, // Request IDR frame 0x0606, // Request IDR frame
@ -171,6 +179,7 @@ static const short packetTypesGen4[] = {
-1, // Rumble triggers (unused) -1, // Rumble triggers (unused)
-1, // Set motion event (unused) -1, // Set motion event (unused)
-1, // Set RGB LED (unused) -1, // Set RGB LED (unused)
-1,
}; };
static const short packetTypesGen5[] = { static const short packetTypesGen5[] = {
0x0305, // Start A 0x0305, // Start A
@ -185,6 +194,7 @@ static const short packetTypesGen5[] = {
-1, // Rumble triggers (unused) -1, // Rumble triggers (unused)
-1, // Set motion event (unused) -1, // Set motion event (unused)
-1, // Set RGB LED (unused) -1, // Set RGB LED (unused)
-1,
}; };
static const short packetTypesGen7[] = { static const short packetTypesGen7[] = {
0x0305, // Start A 0x0305, // Start A
@ -199,6 +209,7 @@ static const short packetTypesGen7[] = {
-1, // Rumble triggers (unused) -1, // Rumble triggers (unused)
-1, // Set motion event (unused) -1, // Set motion event (unused)
-1, // Set RGB LED (unused) -1, // Set RGB LED (unused)
-1,
}; };
static const short packetTypesGen7Enc[] = { static const short packetTypesGen7Enc[] = {
0x0302, // Request IDR frame 0x0302, // Request IDR frame
@ -214,6 +225,7 @@ static const short packetTypesGen7Enc[] = {
0x5501, // Set motion event (Sunshine protocol extension) 0x5501, // Set motion event (Sunshine protocol extension)
0x5502, // Set RGB LED (Sunshine protocol extension) 0x5502, // Set RGB LED (Sunshine protocol extension)
0x5503, // Set Adaptive Triggers (Sunshine protocol extension) 0x5503, // Set Adaptive Triggers (Sunshine protocol extension)
0x5504,
}; };
static const char requestIdrFrameGen3[] = { 0, 0 }; static const char requestIdrFrameGen3[] = { 0, 0 };
@ -1010,6 +1022,12 @@ static void asyncCallbackThreadFunc(void* context) {
queuedCb->data.dsAdaptiveTrigger.left, queuedCb->data.dsAdaptiveTrigger.left,
queuedCb->data.dsAdaptiveTrigger.right); queuedCb->data.dsAdaptiveTrigger.right);
break; break;
case IDX_SET_INPUT_POLICY:
ListenerCallbacks.setInputPolicy(queuedCb->data.setInputPolicy.allowKeyboard,
queuedCb->data.setInputPolicy.allowMouse,
queuedCb->data.setInputPolicy.allowGamepad,
queuedCb->data.setInputPolicy.reason);
break;
default: default:
// Unhandled packet type from queueAsyncCallback() // Unhandled packet type from queueAsyncCallback()
LC_ASSERT(false); LC_ASSERT(false);
@ -1026,7 +1044,8 @@ static bool needsAsyncCallback(unsigned short packetType) {
packetType == packetTypes[IDX_SET_MOTION_EVENT] || packetType == packetTypes[IDX_SET_MOTION_EVENT] ||
packetType == packetTypes[IDX_SET_RGB_LED] || packetType == packetTypes[IDX_SET_RGB_LED] ||
packetType == packetTypes[IDX_HDR_INFO] || packetType == packetTypes[IDX_HDR_INFO] ||
packetType == packetTypes[IDX_DS_ADAPTIVE_TRIGGERS]; packetType == packetTypes[IDX_DS_ADAPTIVE_TRIGGERS] ||
packetType == packetTypes[IDX_SET_INPUT_POLICY];
} }
static void queueAsyncCallback(PNVCTL_ENET_PACKET_HEADER_V1 ctlHdr, int packetLength) { static void queueAsyncCallback(PNVCTL_ENET_PACKET_HEADER_V1 ctlHdr, int packetLength) {
@ -1087,6 +1106,13 @@ static void queueAsyncCallback(PNVCTL_ENET_PACKET_HEADER_V1 ctlHdr, int packetLe
BbGetBytes(&bb, queuedCb->data.dsAdaptiveTrigger.right, DS_EFFECT_PAYLOAD_SIZE); BbGetBytes(&bb, queuedCb->data.dsAdaptiveTrigger.right, DS_EFFECT_PAYLOAD_SIZE);
queuedCb->typeIndex = IDX_DS_ADAPTIVE_TRIGGERS; queuedCb->typeIndex = IDX_DS_ADAPTIVE_TRIGGERS;
} }
else if (ctlHdr->type == packetTypes[IDX_SET_INPUT_POLICY]) {
BbGet8(&bb, &queuedCb->data.setInputPolicy.allowKeyboard);
BbGet8(&bb, &queuedCb->data.setInputPolicy.allowMouse);
BbGet8(&bb, &queuedCb->data.setInputPolicy.allowGamepad);
BbGet8(&bb, &queuedCb->data.setInputPolicy.reason);
queuedCb->typeIndex = IDX_SET_INPUT_POLICY;
}
else { else {
// Unhandled packet type from needsAsyncCallback() // Unhandled packet type from needsAsyncCallback()
LC_ASSERT(false); LC_ASSERT(false);
@ -1701,6 +1727,34 @@ int sendInputPacketOnControlStream(unsigned char* data, int length, uint8_t chan
return 0; return 0;
} }
int LiSendSessionInputPolicy(bool allowKeyboard, bool allowMouse, bool allowGamepad, uint8_t reason) {
struct {
uint8_t allowKeyboard;
uint8_t allowMouse;
uint8_t allowGamepad;
uint8_t reason;
} payload;
if (!IS_SUNSHINE() || AppVersionQuad[0] < 7) {
return LI_ERR_UNSUPPORTED;
}
if (client == NULL || peer == NULL || stopping) {
return -2;
}
payload.allowKeyboard = allowKeyboard ? 1 : 0;
payload.allowMouse = allowMouse ? 1 : 0;
payload.allowGamepad = allowGamepad ? 1 : 0;
payload.reason = reason;
if (sendMessageAndForget(0x5504, sizeof(payload), &payload, CTRL_CHANNEL_GENERIC, ENET_PACKET_FLAG_RELIABLE, false) == 0) {
return -1;
}
return 0;
}
// Called by the input stream to flush queued packets before a batching wait // Called by the input stream to flush queued packets before a batching wait
void flushInputOnControlStream(void) { void flushInputOnControlStream(void) {
if (AppVersionQuad[0] >= 5) { if (AppVersionQuad[0] >= 5) {

View file

@ -41,6 +41,7 @@ static void fakeClRumbleTriggers(uint16_t controllerNumber, uint16_t leftTrigger
static void fakeClSetMotionEventState(uint16_t controllerNumber, uint8_t motionType, uint16_t reportRateHz) {} static void fakeClSetMotionEventState(uint16_t controllerNumber, uint8_t motionType, uint16_t reportRateHz) {}
static void fakeClSetAdaptiveTriggers(uint16_t controllerNumber, uint8_t eventFlags, uint8_t typeLeft, uint8_t typeRight, uint8_t *left, uint8_t *right) {}; static void fakeClSetAdaptiveTriggers(uint16_t controllerNumber, uint8_t eventFlags, uint8_t typeLeft, uint8_t typeRight, uint8_t *left, uint8_t *right) {};
static void fakeClSetControllerLED(uint16_t controllerNumber, uint8_t r, uint8_t g, uint8_t b) {} static void fakeClSetControllerLED(uint16_t controllerNumber, uint8_t r, uint8_t g, uint8_t b) {}
static void fakeClSetInputPolicy(uint8_t allowKeyboard, uint8_t allowMouse, uint8_t allowGamepad, uint8_t reason) {}
static CONNECTION_LISTENER_CALLBACKS fakeClCallbacks = { static CONNECTION_LISTENER_CALLBACKS fakeClCallbacks = {
.stageStarting = fakeClStageStarting, .stageStarting = fakeClStageStarting,
@ -56,6 +57,7 @@ static CONNECTION_LISTENER_CALLBACKS fakeClCallbacks = {
.setMotionEventState = fakeClSetMotionEventState, .setMotionEventState = fakeClSetMotionEventState,
.setControllerLED = fakeClSetControllerLED, .setControllerLED = fakeClSetControllerLED,
.setAdaptiveTriggers = fakeClSetAdaptiveTriggers, .setAdaptiveTriggers = fakeClSetAdaptiveTriggers,
.setInputPolicy = fakeClSetInputPolicy,
}; };
void fixupMissingCallbacks(PDECODER_RENDERER_CALLBACKS* drCallbacks, PAUDIO_RENDERER_CALLBACKS* arCallbacks, void fixupMissingCallbacks(PDECODER_RENDERER_CALLBACKS* drCallbacks, PAUDIO_RENDERER_CALLBACKS* arCallbacks,
@ -146,5 +148,8 @@ void fixupMissingCallbacks(PDECODER_RENDERER_CALLBACKS* drCallbacks, PAUDIO_REND
if ((*clCallbacks)->setAdaptiveTriggers == NULL) { if ((*clCallbacks)->setAdaptiveTriggers == NULL) {
(*clCallbacks)->setAdaptiveTriggers = fakeClSetAdaptiveTriggers; (*clCallbacks)->setAdaptiveTriggers = fakeClSetAdaptiveTriggers;
} }
if ((*clCallbacks)->setInputPolicy == NULL) {
(*clCallbacks)->setInputPolicy = fakeClSetInputPolicy;
}
} }
} }

View file

@ -483,6 +483,8 @@ typedef void(*ConnListenerSetAdaptiveTriggers)(uint16_t controllerNumber, uint8_
// This callback is invoked to set a controller's RGB LED (if present). // This callback is invoked to set a controller's RGB LED (if present).
typedef void(*ConnListenerSetControllerLED)(uint16_t controllerNumber, uint8_t r, uint8_t g, uint8_t b); typedef void(*ConnListenerSetControllerLED)(uint16_t controllerNumber, uint8_t r, uint8_t g, uint8_t b);
typedef void(*ConnListenerSetInputPolicy)(uint8_t allowKeyboard, uint8_t allowMouse, uint8_t allowGamepad, uint8_t reason);
typedef struct _CONNECTION_LISTENER_CALLBACKS { typedef struct _CONNECTION_LISTENER_CALLBACKS {
ConnListenerStageStarting stageStarting; ConnListenerStageStarting stageStarting;
ConnListenerStageComplete stageComplete; ConnListenerStageComplete stageComplete;
@ -497,6 +499,7 @@ typedef struct _CONNECTION_LISTENER_CALLBACKS {
ConnListenerSetMotionEventState setMotionEventState; ConnListenerSetMotionEventState setMotionEventState;
ConnListenerSetControllerLED setControllerLED; ConnListenerSetControllerLED setControllerLED;
ConnListenerSetAdaptiveTriggers setAdaptiveTriggers; ConnListenerSetAdaptiveTriggers setAdaptiveTriggers;
ConnListenerSetInputPolicy setInputPolicy;
} CONNECTION_LISTENER_CALLBACKS, *PCONNECTION_LISTENER_CALLBACKS; } CONNECTION_LISTENER_CALLBACKS, *PCONNECTION_LISTENER_CALLBACKS;
// Use this function to zero the connection callbacks when allocated on the stack or heap // Use this function to zero the connection callbacks when allocated on the stack or heap
@ -837,6 +840,12 @@ int LiSendHighResScrollEvent(short scrollAmount);
int LiSendHScrollEvent(signed char scrollClicks); int LiSendHScrollEvent(signed char scrollClicks);
int LiSendHighResHScrollEvent(short scrollAmount); int LiSendHighResHScrollEvent(short scrollAmount);
#define LI_SESSION_INPUT_POLICY_REASON_STREAM_START 0x00
#define LI_SESSION_INPUT_POLICY_REASON_USER_TOGGLE 0x01
#define LI_SESSION_INPUT_POLICY_REASON_HOST_ACK 0x02
#define LI_SESSION_INPUT_POLICY_REASON_HOST_OVERRIDE 0x03
int LiSendSessionInputPolicy(bool allowKeyboard, bool allowMouse, bool allowGamepad, uint8_t reason);
// This function returns a time in microseconds with an implementation-defined epoch. // This function returns a time in microseconds with an implementation-defined epoch.
// It should only ever be compared with the return value from a previous call to itself. // It should only ever be compared with the return value from a previous call to itself.
uint64_t LiGetMicroseconds(void); uint64_t LiGetMicroseconds(void);