mirror of
https://github.com/moonlight-stream/moonlight-common-c.git
synced 2025-07-01 23:35:58 +00:00
Introduce new input extension functions for touch, pen, motion, and controller arrival
This commit is contained in:
parent
7970925fe4
commit
4a48024dc8
@ -31,6 +31,7 @@ uint16_t AudioPortNumber;
|
||||
uint16_t VideoPortNumber;
|
||||
SS_PING AudioPingPayload;
|
||||
SS_PING VideoPingPayload;
|
||||
uint16_t SunshineFeatureFlags;
|
||||
|
||||
// Connection stages
|
||||
static const char* stageNames[STAGE_MAX] = {
|
||||
|
64
src/Input.h
64
src/Input.h
@ -1,7 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
// netfloat is just a little-endian float in byte form
|
||||
// for network transmission.
|
||||
typedef uint8_t netfloat[4];
|
||||
|
||||
typedef struct _NV_INPUT_HEADER {
|
||||
uint32_t size; // Size of packet (excluding this field) - Big Endian
|
||||
uint32_t magic; // Packet type - Little Endian
|
||||
@ -116,4 +122,62 @@ typedef struct _SS_HSCROLL_PACKET {
|
||||
short scrollAmount;
|
||||
} SS_HSCROLL_PACKET, *PSS_HSCROLL_PACKET;
|
||||
|
||||
#define SS_TOUCH_MAGIC 0x55000002
|
||||
typedef struct _SS_TOUCH_PACKET {
|
||||
NV_INPUT_HEADER header;
|
||||
uint8_t eventType;
|
||||
uint8_t touchIndex;
|
||||
uint8_t zero[2]; // Alignment/reserved
|
||||
netfloat x;
|
||||
netfloat y;
|
||||
netfloat pressure;
|
||||
} SS_TOUCH_PACKET, *PSS_TOUCH_PACKET;
|
||||
|
||||
#define SS_PEN_MAGIC 0x55000003
|
||||
typedef struct _SS_PEN_PACKET {
|
||||
NV_INPUT_HEADER header;
|
||||
uint8_t eventType;
|
||||
uint8_t toolType;
|
||||
uint8_t penButtons;
|
||||
uint8_t zero[1]; // Alignment/reserved
|
||||
netfloat x;
|
||||
netfloat y;
|
||||
netfloat pressure;
|
||||
uint16_t rotation;
|
||||
uint8_t tiltX;
|
||||
uint8_t tiltY;
|
||||
} SS_PEN_PACKET, *PSS_PEN_PACKET;
|
||||
|
||||
#define SS_CONTROLLER_ARRIVAL_MAGIC 0x55000004
|
||||
typedef struct _SS_CONTROLLER_ARRIVAL_PACKET {
|
||||
NV_INPUT_HEADER header;
|
||||
uint8_t type;
|
||||
uint8_t zero[1]; // Alignment/reserved
|
||||
uint16_t capabilities;
|
||||
uint32_t supportedButtonFlags;
|
||||
} SS_CONTROLLER_ARRIVAL_PACKET, *PSS_CONTROLLER_ARRIVAL_PACKET;
|
||||
|
||||
#define SS_CONTROLLER_TOUCH_MAGIC 0x55000005
|
||||
typedef struct _SS_CONTROLLER_TOUCH_PACKET {
|
||||
NV_INPUT_HEADER header;
|
||||
uint8_t controllerNumber;
|
||||
uint8_t eventType;
|
||||
uint8_t touchIndex;
|
||||
uint8_t zero[1]; // Alignment/reserved
|
||||
netfloat x;
|
||||
netfloat y;
|
||||
netfloat pressure;
|
||||
} SS_CONTROLLER_TOUCH_PACKET, *PSS_CONTROLLER_TOUCH_PACKET;
|
||||
|
||||
#define SS_CONTROLLER_MOTION_MAGIC 0x55000006
|
||||
typedef struct _SS_CONTROLLER_MOTION_PACKET {
|
||||
NV_INPUT_HEADER header;
|
||||
uint8_t controllerNumber;
|
||||
uint8_t motionType;
|
||||
uint8_t zero[2]; // Alignment/reserved
|
||||
netfloat x;
|
||||
netfloat y;
|
||||
netfloat z;
|
||||
} SS_CONTROLLER_MOTION_PACKET, *PSS_CONTROLLER_MOTION_PACKET;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
@ -53,6 +53,11 @@ typedef struct _PACKET_HOLDER {
|
||||
NV_SCROLL_PACKET scroll;
|
||||
SS_HSCROLL_PACKET hscroll;
|
||||
NV_HAPTICS_PACKET haptics;
|
||||
SS_TOUCH_PACKET touch;
|
||||
SS_PEN_PACKET pen;
|
||||
SS_CONTROLLER_ARRIVAL_PACKET controllerArrival;
|
||||
SS_CONTROLLER_TOUCH_PACKET controllerTouch;
|
||||
SS_CONTROLLER_MOTION_PACKET controllerMotion;
|
||||
NV_UNICODE_PACKET unicode;
|
||||
} packet;
|
||||
} PACKET_HOLDER, *PPACKET_HOLDER;
|
||||
@ -586,6 +591,19 @@ int stopInputStream(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void floatToNetfloat(float in, netfloat out) {
|
||||
if (IS_LITTLE_ENDIAN()) {
|
||||
memcpy(out, &in, sizeof(in));
|
||||
}
|
||||
else {
|
||||
uint8_t* inb = (uint8_t*)∈
|
||||
out[0] = inb[3];
|
||||
out[1] = inb[2];
|
||||
out[2] = inb[1];
|
||||
out[3] = inb[0];
|
||||
}
|
||||
}
|
||||
|
||||
// Send a mouse move event to the streaming machine
|
||||
int LiSendMouseMoveEvent(short deltaX, short deltaY) {
|
||||
PPACKET_HOLDER holder;
|
||||
@ -1008,7 +1026,7 @@ int LiSendHighResHScrollEvent(short scrollAmount) {
|
||||
|
||||
// This is a protocol extension only supported with Sunshine
|
||||
if (!IS_SUNSHINE()) {
|
||||
return -3;
|
||||
return LI_ERR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
if (scrollAmount == 0) {
|
||||
@ -1037,3 +1055,193 @@ int LiSendHighResHScrollEvent(short scrollAmount) {
|
||||
int LiSendHScrollEvent(signed char scrollClicks) {
|
||||
return LiSendHighResHScrollEvent(scrollClicks * LI_WHEEL_DELTA);
|
||||
}
|
||||
|
||||
int LiSendTouchEvent(uint8_t eventType, uint8_t touchIndex, float x, float y, float pressure) {
|
||||
PPACKET_HOLDER holder;
|
||||
int err;
|
||||
|
||||
if (!initialized) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
// This is a protocol extension only supported with Sunshine
|
||||
if (!(SunshineFeatureFlags & SS_FF_PEN_TOUCH_EVENTS)) {
|
||||
return LI_ERR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
holder = allocatePacketHolder(0);
|
||||
if (holder == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
holder->packet.touch.header.size = BE32(sizeof(SS_TOUCH_PACKET) - sizeof(uint32_t));
|
||||
holder->packet.touch.header.magic = LE32(SS_TOUCH_MAGIC);
|
||||
holder->packet.touch.eventType = eventType;
|
||||
holder->packet.touch.touchIndex = touchIndex;
|
||||
memset(holder->packet.touch.zero, 0, sizeof(holder->packet.touch.zero));
|
||||
floatToNetfloat(x, holder->packet.touch.x);
|
||||
floatToNetfloat(y, holder->packet.touch.y);
|
||||
floatToNetfloat(pressure, holder->packet.touch.pressure);
|
||||
|
||||
err = LbqOfferQueueItem(&packetQueue, holder, &holder->entry);
|
||||
if (err != LBQ_SUCCESS) {
|
||||
LC_ASSERT(err == LBQ_BOUND_EXCEEDED);
|
||||
Limelog("Input queue reached maximum size limit\n");
|
||||
freePacketHolder(holder);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int LiSendPenEvent(uint8_t eventType, uint8_t toolType, uint8_t penButtons,
|
||||
float x, float y, float pressure,
|
||||
uint16_t rotation, uint8_t tiltX, uint8_t tiltY) {
|
||||
PPACKET_HOLDER holder;
|
||||
int err;
|
||||
|
||||
if (!initialized) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
// This is a protocol extension only supported with Sunshine
|
||||
if (!(SunshineFeatureFlags & SS_FF_PEN_TOUCH_EVENTS)) {
|
||||
return LI_ERR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
holder = allocatePacketHolder(0);
|
||||
if (holder == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
holder->packet.pen.header.size = BE32(sizeof(SS_PEN_PACKET) - sizeof(uint32_t));
|
||||
holder->packet.pen.header.magic = LE32(SS_PEN_MAGIC);
|
||||
holder->packet.pen.eventType = eventType;
|
||||
holder->packet.pen.toolType = toolType;
|
||||
holder->packet.pen.penButtons = penButtons;
|
||||
memset(holder->packet.pen.zero, 0, sizeof(holder->packet.pen.zero));
|
||||
floatToNetfloat(x, holder->packet.touch.x);
|
||||
floatToNetfloat(y, holder->packet.touch.y);
|
||||
floatToNetfloat(pressure, holder->packet.touch.pressure);
|
||||
holder->packet.pen.rotation = rotation;
|
||||
holder->packet.pen.tiltX = tiltX;
|
||||
holder->packet.pen.tiltY = tiltY;
|
||||
|
||||
err = LbqOfferQueueItem(&packetQueue, holder, &holder->entry);
|
||||
if (err != LBQ_SUCCESS) {
|
||||
LC_ASSERT(err == LBQ_BOUND_EXCEEDED);
|
||||
Limelog("Input queue reached maximum size limit\n");
|
||||
freePacketHolder(holder);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int LiSendControllerArrivalEvent(uint8_t controllerNumber, uint16_t activeGamepadMask, uint8_t type,
|
||||
uint32_t supportedButtonFlags, uint16_t capabilities) {
|
||||
PPACKET_HOLDER holder;
|
||||
int err;
|
||||
|
||||
if (!initialized) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
// The arrival event is only supported by Sunshine
|
||||
if (IS_SUNSHINE()) {
|
||||
holder = allocatePacketHolder(0);
|
||||
if (holder == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
holder->packet.controllerArrival.header.size = BE32(sizeof(SS_CONTROLLER_ARRIVAL_PACKET) - sizeof(uint32_t));
|
||||
holder->packet.controllerArrival.header.magic = LE32(SS_CONTROLLER_ARRIVAL_MAGIC);
|
||||
holder->packet.controllerArrival.type = type;
|
||||
memset(holder->packet.controllerArrival.zero, 0, sizeof(holder->packet.controllerArrival.zero));
|
||||
holder->packet.controllerArrival.capabilities = capabilities;
|
||||
holder->packet.controllerArrival.supportedButtonFlags = supportedButtonFlags;
|
||||
|
||||
err = LbqOfferQueueItem(&packetQueue, holder, &holder->entry);
|
||||
if (err != LBQ_SUCCESS) {
|
||||
LC_ASSERT(err == LBQ_BOUND_EXCEEDED);
|
||||
Limelog("Input queue reached maximum size limit\n");
|
||||
freePacketHolder(holder);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
// Send a MC event just in case the host software doesn't support arrival events.
|
||||
return LiSendMultiControllerEvent(controllerNumber, activeGamepadMask, 0, 0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
int LiSendControllerTouchEvent(uint8_t controllerNumber, uint8_t eventType, uint8_t touchIndex, float x, float y, float pressure) {
|
||||
PPACKET_HOLDER holder;
|
||||
int err;
|
||||
|
||||
if (!initialized) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
// This is a protocol extension only supported with Sunshine
|
||||
if (!(SunshineFeatureFlags & SS_FF_CONTROLLER_TOUCH_EVENTS)) {
|
||||
return LI_ERR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
holder = allocatePacketHolder(0);
|
||||
if (holder == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
holder->packet.controllerTouch.header.size = BE32(sizeof(SS_CONTROLLER_TOUCH_PACKET) - sizeof(uint32_t));
|
||||
holder->packet.controllerTouch.header.magic = LE32(SS_CONTROLLER_TOUCH_MAGIC);
|
||||
holder->packet.controllerTouch.controllerNumber = controllerNumber;
|
||||
holder->packet.controllerTouch.eventType = eventType;
|
||||
holder->packet.controllerTouch.touchIndex = touchIndex;
|
||||
memset(holder->packet.controllerTouch.zero, 0, sizeof(holder->packet.controllerTouch.zero));
|
||||
floatToNetfloat(x, holder->packet.controllerTouch.x);
|
||||
floatToNetfloat(y, holder->packet.controllerTouch.y);
|
||||
floatToNetfloat(pressure, holder->packet.controllerTouch.pressure);
|
||||
|
||||
err = LbqOfferQueueItem(&packetQueue, holder, &holder->entry);
|
||||
if (err != LBQ_SUCCESS) {
|
||||
LC_ASSERT(err == LBQ_BOUND_EXCEEDED);
|
||||
Limelog("Input queue reached maximum size limit\n");
|
||||
freePacketHolder(holder);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int LiSendControllerMotionEvent(uint8_t controllerNumber, uint8_t motionType, float x, float y, float z) {
|
||||
PPACKET_HOLDER holder;
|
||||
int err;
|
||||
|
||||
if (!initialized) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
// This is a protocol extension only supported with Sunshine
|
||||
if (!(SunshineFeatureFlags & SS_FF_CONTROLLER_TOUCH_EVENTS)) {
|
||||
return LI_ERR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
holder = allocatePacketHolder(0);
|
||||
if (holder == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
holder->packet.controllerMotion.header.size = BE32(sizeof(SS_CONTROLLER_MOTION_PACKET) - sizeof(uint32_t));
|
||||
holder->packet.controllerMotion.header.magic = LE32(SS_CONTROLLER_MOTION_MAGIC);
|
||||
holder->packet.controllerMotion.controllerNumber = controllerNumber;
|
||||
memset(holder->packet.controllerMotion.zero, 0, sizeof(holder->packet.controllerMotion.zero));
|
||||
floatToNetfloat(x, holder->packet.controllerMotion.x);
|
||||
floatToNetfloat(y, holder->packet.controllerMotion.y);
|
||||
floatToNetfloat(z, holder->packet.controllerMotion.z);
|
||||
|
||||
err = LbqOfferQueueItem(&packetQueue, holder, &holder->entry);
|
||||
if (err != LBQ_SUCCESS) {
|
||||
LC_ASSERT(err == LBQ_BOUND_EXCEEDED);
|
||||
Limelog("Input queue reached maximum size limit\n");
|
||||
freePacketHolder(holder);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -41,6 +41,10 @@ extern uint16_t VideoPortNumber;
|
||||
extern SS_PING AudioPingPayload;
|
||||
extern SS_PING VideoPingPayload;
|
||||
|
||||
#define SS_FF_PEN_TOUCH_EVENTS 0x01
|
||||
#define SS_FF_CONTROLLER_TOUCH_EVENTS 0x02
|
||||
extern uint16_t SunshineFeatureFlags;
|
||||
|
||||
#ifndef UINT24_MAX
|
||||
#define UINT24_MAX 0xFFFFFF
|
||||
#endif
|
||||
|
@ -553,6 +553,38 @@ int LiSendMousePositionEvent(short x, short y, short referenceWidth, short refer
|
||||
// Using this function avoids double-acceleration in cases when the client motion is also accelerated.
|
||||
int LiSendMouseMoveAsMousePositionEvent(short deltaX, short deltaY, short referenceWidth, short referenceHeight);
|
||||
|
||||
// Error return value to indicate that the requested functionality is not supported by the host
|
||||
#define LI_ERR_UNSUPPORTED -5501
|
||||
|
||||
// This function allows multi-touch input to be sent directly to Sunshine hosts. The x and y values
|
||||
// are normalized device coordinates stretching top-left corner (0.0, 0.0) to bottom-right corner
|
||||
// (1.0, 1.0) of the video area. Pressure is a 0.0 to 1.0 range value from min to max pressure.
|
||||
//
|
||||
// Sending a down/move event with a pressure of 0.0 indicates the actual pressure is unknown.
|
||||
//
|
||||
// If unsupported by the host, this will return LI_ERR_UNSUPPORTED and the caller should consider
|
||||
// falling back to other functions to send this input (such as LiSendMousePositionEvent()).
|
||||
#define LI_TOUCH_EVENT_HOVER 0x00
|
||||
#define LI_TOUCH_EVENT_DOWN 0x01
|
||||
#define LI_TOUCH_EVENT_UP 0x02
|
||||
#define LI_TOUCH_EVENT_MOVE 0x03
|
||||
#define LI_TOUCH_EVENT_CANCEL 0x04
|
||||
int LiSendTouchEvent(uint8_t eventType, uint8_t touchIndex, float x, float y, float pressure);
|
||||
|
||||
// This function is similar to LiSendTouchEvent() but allows additional parameters relevant for pen
|
||||
// input, including rotation, tilt, and buttons. Rotation is in degrees from vertical in Y dimension
|
||||
// (parallel to screen) and tilt is in degrees from vertical in Z dimension (perpendicular to screen).
|
||||
#define LI_TOOL_TYPE_PEN 0x01
|
||||
#define LI_TOOL_TYPE_ERASER 0x02
|
||||
#define LI_PEN_BUTTON_PRIMARY 0x01
|
||||
#define LI_PEN_BUTTON_SECONDARY 0x02
|
||||
#define LI_PEN_BUTTON_TERTIARY 0x04
|
||||
#define LI_TILT_UNKNOWN 0xFF
|
||||
#define LI_ROT_UNKNOWN 0xFF
|
||||
int LiSendPenEvent(uint8_t eventType, uint8_t toolType, uint8_t penButtons,
|
||||
float x, float y, float pressure,
|
||||
uint16_t rotation, uint8_t tiltX, uint8_t tiltY);
|
||||
|
||||
// This function queues a mouse button event to be sent to the remote server.
|
||||
#define BUTTON_ACTION_PRESS 0x07
|
||||
#define BUTTON_ACTION_RELEASE 0x08
|
||||
@ -614,6 +646,43 @@ int LiSendMultiControllerEvent(short controllerNumber, short activeGamepadMask,
|
||||
short buttonFlags, unsigned char leftTrigger, unsigned char rightTrigger,
|
||||
short leftStickX, short leftStickY, short rightStickX, short rightStickY);
|
||||
|
||||
// This function provides a method of informing the host of the available buttons and capabilities
|
||||
// on a new controller. This can be used as an alternative to calling LiSendMultiControllerEvent()
|
||||
// to indicate the arrival of a new controller.
|
||||
//
|
||||
// This can allow the host to make better decisions about what type of controller to emulate and what
|
||||
// capabilities to advertise to the OS on the virtual controller.
|
||||
//
|
||||
// If controller arrival events are unsupported by the host, this will fall back to indicating
|
||||
// arrival via LiSendMultiControllerEvent().
|
||||
#define LI_CTYPE_UNKNOWN 0x00
|
||||
#define LI_CTYPE_XBOX 0x01
|
||||
#define LI_CTYPE_PS 0x02
|
||||
#define LI_CTYPE_NINTENDO 0x03
|
||||
#define LI_CCAP_ANALOG_TRIGGERS 0x01
|
||||
#define LI_CCAP_RUMBLE 0x02
|
||||
#define LI_CCAP_TRIGGER_RUMBLE 0x04
|
||||
#define LI_CCAP_TOUCHPAD 0x08
|
||||
#define LI_CCAP_ACCEL 0x10
|
||||
#define LI_CCAP_GYRO 0x20
|
||||
int LiSendControllerArrivalEvent(uint8_t controllerNumber, uint16_t activeGamepadMask, uint8_t type,
|
||||
uint32_t supportedButtonFlags, uint16_t capabilities);
|
||||
|
||||
// This function is similar to LiSendTouchEvent(), but the touch events are associated with a
|
||||
// touchpad device present on a game controller instead of a touchscreen.
|
||||
//
|
||||
// If unsupported by the host, this will return LI_ERR_UNSUPPORTED and the caller should consider
|
||||
// using this touch input to simulate trackpad input.
|
||||
int LiSendControllerTouchEvent(uint8_t controllerNumber, uint8_t eventType, uint8_t touchIndex, float x, float y, float pressure);
|
||||
|
||||
// This function allows clients to send controller-associated motion events to a supported host.
|
||||
//
|
||||
// For power and performance reasons, motion sensors should not be enabled unless the host has
|
||||
// explicitly asked for motion event reports via ConnListenerSetMotionEventState().
|
||||
#define LI_MOTION_TYPE_ACCEL 0x01
|
||||
#define LI_MOTION_TYPE_GYRO 0x02
|
||||
int LiSendControllerMotionEvent(uint8_t controllerNumber, uint8_t motionType, float x, float y, float z);
|
||||
|
||||
// This function queues a vertical scroll event to the remote server.
|
||||
// The number of "clicks" is multiplied by WHEEL_DELTA (120) before
|
||||
// being sent to the PC.
|
||||
|
Loading…
x
Reference in New Issue
Block a user