mirror of
https://github.com/moonlight-stream/moonlight-common-c.git
synced 2025-08-17 17:05:50 +00:00
Add keyboard and mouse input support
This commit is contained in:
parent
c553b14da6
commit
761f324465
@ -4,16 +4,18 @@
|
||||
static int stage = STAGE_NONE;
|
||||
static CONNECTION_LISTENER_CALLBACKS ListenerCallbacks;
|
||||
|
||||
static const char* stageNames [] = {
|
||||
static const char* stageNames[STAGE_MAX] = {
|
||||
"none",
|
||||
"platform initialization",
|
||||
"handshake",
|
||||
"control stream initialization",
|
||||
"video stream initialization",
|
||||
"audio stream initialization",
|
||||
"input stream initialization",
|
||||
"control stream establishment",
|
||||
"video stream establishment",
|
||||
"audio stream establishment"
|
||||
"audio stream establishment",
|
||||
"input stream establishment"
|
||||
};
|
||||
|
||||
const char* LiGetStageName(int stage) {
|
||||
@ -21,6 +23,12 @@ const char* LiGetStageName(int stage) {
|
||||
}
|
||||
|
||||
void LiStopConnection(void) {
|
||||
if (stage == STAGE_INPUT_STREAM_START) {
|
||||
Limelog("Stopping input stream...");
|
||||
stopInputStream();
|
||||
stage--;
|
||||
Limelog("done\n");
|
||||
}
|
||||
if (stage == STAGE_AUDIO_STREAM_START) {
|
||||
Limelog("Stopping audio stream...");
|
||||
stopAudioStream();
|
||||
@ -39,6 +47,12 @@ void LiStopConnection(void) {
|
||||
stage--;
|
||||
Limelog("done\n");
|
||||
}
|
||||
if (stage == STAGE_INPUT_STREAM_INIT) {
|
||||
Limelog("Cleaning up input stream...");
|
||||
destroyInputStream();
|
||||
stage--;
|
||||
Limelog("done\n");
|
||||
}
|
||||
if (stage == STAGE_AUDIO_STREAM_INIT) {
|
||||
Limelog("Cleaning up audio stream...");
|
||||
destroyAudioStream();
|
||||
@ -83,7 +97,7 @@ int LiStartConnection(IP_ADDRESS host, PSTREAM_CONFIGURATION streamConfig, PCONN
|
||||
err = initializePlatformSockets();
|
||||
if (err != 0) {
|
||||
Limelog("failed: %d\n", err);
|
||||
ListenerCallbacks.stageFailed(STAGE_PLATFORM_INIT);
|
||||
ListenerCallbacks.stageFailed(STAGE_PLATFORM_INIT, err);
|
||||
goto Cleanup;
|
||||
}
|
||||
stage++;
|
||||
@ -96,7 +110,7 @@ int LiStartConnection(IP_ADDRESS host, PSTREAM_CONFIGURATION streamConfig, PCONN
|
||||
err = performHandshake(host);
|
||||
if (err != 0) {
|
||||
Limelog("failed: %d\n", err);
|
||||
ListenerCallbacks.stageFailed(STAGE_HANDSHAKE);
|
||||
ListenerCallbacks.stageFailed(STAGE_HANDSHAKE, err);
|
||||
goto Cleanup;
|
||||
}
|
||||
stage++;
|
||||
@ -109,7 +123,7 @@ int LiStartConnection(IP_ADDRESS host, PSTREAM_CONFIGURATION streamConfig, PCONN
|
||||
err = initializeControlStream(host, streamConfig, &ListenerCallbacks);
|
||||
if (err != 0) {
|
||||
Limelog("failed: %d\n", err);
|
||||
ListenerCallbacks.stageFailed(STAGE_CONTROL_STREAM_INIT);
|
||||
ListenerCallbacks.stageFailed(STAGE_CONTROL_STREAM_INIT, err);
|
||||
goto Cleanup;
|
||||
}
|
||||
stage++;
|
||||
@ -133,12 +147,20 @@ int LiStartConnection(IP_ADDRESS host, PSTREAM_CONFIGURATION streamConfig, PCONN
|
||||
ListenerCallbacks.stageComplete(STAGE_AUDIO_STREAM_INIT);
|
||||
Limelog("done\n");
|
||||
|
||||
Limelog("Initializing input stream...");
|
||||
ListenerCallbacks.stageStarting(STAGE_INPUT_STREAM_INIT);
|
||||
initializeInputStream(host, &ListenerCallbacks);
|
||||
stage++;
|
||||
LC_ASSERT(stage == STAGE_INPUT_STREAM_INIT);
|
||||
ListenerCallbacks.stageComplete(STAGE_INPUT_STREAM_INIT);
|
||||
Limelog("done\n");
|
||||
|
||||
Limelog("Starting control stream...");
|
||||
ListenerCallbacks.stageStarting(STAGE_CONTROL_STREAM_START);
|
||||
err = startControlStream();
|
||||
if (err != 0) {
|
||||
Limelog("failed: %d\n", err);
|
||||
ListenerCallbacks.stageFailed(STAGE_CONTROL_STREAM_START);
|
||||
ListenerCallbacks.stageFailed(STAGE_CONTROL_STREAM_START, err);
|
||||
goto Cleanup;
|
||||
}
|
||||
stage++;
|
||||
@ -151,7 +173,7 @@ int LiStartConnection(IP_ADDRESS host, PSTREAM_CONFIGURATION streamConfig, PCONN
|
||||
err = startVideoStream(renderContext, drFlags);
|
||||
if (err != 0) {
|
||||
Limelog("Video stream start failed: %d\n", err);
|
||||
ListenerCallbacks.stageFailed(STAGE_VIDEO_STREAM_START);
|
||||
ListenerCallbacks.stageFailed(STAGE_VIDEO_STREAM_START, err);
|
||||
goto Cleanup;
|
||||
}
|
||||
stage++;
|
||||
@ -164,7 +186,7 @@ int LiStartConnection(IP_ADDRESS host, PSTREAM_CONFIGURATION streamConfig, PCONN
|
||||
err = startAudioStream();
|
||||
if (err != 0) {
|
||||
Limelog("Audio stream start failed: %d\n", err);
|
||||
ListenerCallbacks.stageFailed(STAGE_AUDIO_STREAM_START);
|
||||
ListenerCallbacks.stageFailed(STAGE_AUDIO_STREAM_START, err);
|
||||
goto Cleanup;
|
||||
}
|
||||
stage++;
|
||||
@ -172,6 +194,21 @@ int LiStartConnection(IP_ADDRESS host, PSTREAM_CONFIGURATION streamConfig, PCONN
|
||||
ListenerCallbacks.stageComplete(STAGE_AUDIO_STREAM_START);
|
||||
Limelog("done\n");
|
||||
|
||||
Limelog("Starting input stream...");
|
||||
ListenerCallbacks.stageStarting(STAGE_INPUT_STREAM_START);
|
||||
err = startInputStream();
|
||||
if (err != 0) {
|
||||
Limelog("Input stream start failed: %d\n", err);
|
||||
ListenerCallbacks.stageFailed(STAGE_INPUT_STREAM_START, err);
|
||||
goto Cleanup;
|
||||
}
|
||||
stage++;
|
||||
LC_ASSERT(stage == STAGE_INPUT_STREAM_START);
|
||||
ListenerCallbacks.stageComplete(STAGE_INPUT_STREAM_START);
|
||||
Limelog("done\n");
|
||||
|
||||
ListenerCallbacks.connectionStarted();
|
||||
|
||||
Cleanup:
|
||||
return err;
|
||||
}
|
35
limelight-common/Input.h
Normal file
35
limelight-common/Input.h
Normal file
@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
typedef struct _NV_INPUT_HEADER {
|
||||
int packetType;
|
||||
} NV_INPUT_HEADER, PNV_INPUT_HEADER;
|
||||
|
||||
#define PACKET_TYPE_KEYBOARD 0x0A
|
||||
typedef struct _NV_KEYBOARD_PACKET {
|
||||
NV_INPUT_HEADER header;
|
||||
char keyAction;
|
||||
int zero1;
|
||||
short keyCode;
|
||||
char modifiers;
|
||||
short zero2;
|
||||
} NV_KEYBOARD_PACKET, *PNV_KEYBOARD_PACKET;
|
||||
|
||||
#define PACKET_TYPE_MOUSE_MOVE 0x08
|
||||
#define MOUSE_MOVE_MAGIC 0x06000000
|
||||
typedef struct _NV_MOUSE_MOVE_PACKET {
|
||||
NV_INPUT_HEADER header;
|
||||
int magic;
|
||||
short deltaX;
|
||||
short deltaY;
|
||||
} NV_MOUSE_MOVE_PACKET, *PNV_MOUSE_MOVE_PACKET;
|
||||
|
||||
#define PACKET_TYPE_MOUSE_BUTTON 0x05
|
||||
typedef struct _NV_MOUSE_BUTTON_PACKET {
|
||||
NV_INPUT_HEADER header;
|
||||
char action;
|
||||
int button;
|
||||
} NV_MOUSE_BUTTON_PACKET, *PNV_MOUSE_BUTTON_PACKET;
|
||||
|
||||
#pragma pack(pop)
|
168
limelight-common/InputStream.c
Normal file
168
limelight-common/InputStream.c
Normal file
@ -0,0 +1,168 @@
|
||||
#include "Limelight-internal.h"
|
||||
#include "PlatformSockets.h"
|
||||
#include "PlatformThreads.h"
|
||||
#include "LinkedBlockingQueue.h"
|
||||
#include "Input.h"
|
||||
|
||||
static IP_ADDRESS host;
|
||||
static SOCKET inputSock = INVALID_SOCKET;
|
||||
static PCONNECTION_LISTENER_CALLBACKS listenerCallbacks;
|
||||
|
||||
static LINKED_BLOCKING_QUEUE packetQueue;
|
||||
static PLT_THREAD inputSendThread;
|
||||
|
||||
typedef struct _PACKET_HOLDER {
|
||||
int packetLength;
|
||||
union {
|
||||
NV_KEYBOARD_PACKET keyboard;
|
||||
NV_MOUSE_MOVE_PACKET mouseMove;
|
||||
NV_MOUSE_BUTTON_PACKET mouseButton;
|
||||
} packet;
|
||||
} PACKET_HOLDER, *PPACKET_HOLDER;
|
||||
|
||||
int initializeInputStream(IP_ADDRESS addr, PCONNECTION_LISTENER_CALLBACKS clCallbacks) {
|
||||
host = addr;
|
||||
listenerCallbacks = clCallbacks;
|
||||
|
||||
LbqInitializeLinkedBlockingQueue(&packetQueue, 30);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void destroyInputStream(void) {
|
||||
PLINKED_BLOCKING_QUEUE_ENTRY entry, nextEntry;
|
||||
|
||||
entry = LbqDestroyLinkedBlockingQueue(&packetQueue);
|
||||
|
||||
while (entry != NULL) {
|
||||
nextEntry = entry->next;
|
||||
free(entry->data);
|
||||
free(entry);
|
||||
entry = nextEntry;
|
||||
}
|
||||
}
|
||||
|
||||
static void inputSendThreadProc(void* context) {
|
||||
int err;
|
||||
PPACKET_HOLDER holder;
|
||||
|
||||
while (!PltIsThreadInterrupted(&inputSendThread)) {
|
||||
err = LbqWaitForQueueElement(&packetQueue, (void**) &holder);
|
||||
if (err != LBQ_SUCCESS) {
|
||||
Limelog("Input thread terminating #1\n");
|
||||
listenerCallbacks->connectionTerminated(err);
|
||||
return;
|
||||
}
|
||||
|
||||
err = send(inputSock, (const char*) &holder->packet, holder->packetLength, 0);
|
||||
free(holder);
|
||||
if (err <= 0) {
|
||||
Limelog("Input thread terminating #2\n");
|
||||
listenerCallbacks->connectionTerminated(err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int startInputStream(void) {
|
||||
int err;
|
||||
|
||||
inputSock = connectTcpSocket(host, 35043);
|
||||
if (inputSock == INVALID_SOCKET) {
|
||||
return LastSocketError();
|
||||
}
|
||||
|
||||
enableNoDelay(inputSock);
|
||||
|
||||
err = PltCreateThread(inputSendThreadProc, NULL, &inputSendThread);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int stopInputStream(void) {
|
||||
PltInterruptThread(&inputSendThread);
|
||||
|
||||
if (inputSock != INVALID_SOCKET) {
|
||||
closesocket(inputSock);
|
||||
inputSock = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
PltJoinThread(&inputSendThread);
|
||||
PltCloseThread(&inputSendThread);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LiSendMouseMoveEvent(short deltaX, short deltaY) {
|
||||
PPACKET_HOLDER holder;
|
||||
int err;
|
||||
|
||||
holder = malloc(sizeof(*holder));
|
||||
if (holder == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
holder->packetLength = sizeof(NV_MOUSE_MOVE_PACKET);
|
||||
holder->packet.mouseMove.header.packetType = htonl(PACKET_TYPE_MOUSE_MOVE);
|
||||
holder->packet.mouseMove.magic = htonl(MOUSE_MOVE_MAGIC);
|
||||
holder->packet.mouseMove.deltaX = htons(deltaX);
|
||||
holder->packet.mouseMove.deltaY = htons(deltaY);
|
||||
|
||||
err = LbqOfferQueueItem(&packetQueue, holder);
|
||||
if (err != LBQ_SUCCESS) {
|
||||
free(holder);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int LiSendMouseButtonEvent(char action, int button) {
|
||||
PPACKET_HOLDER holder;
|
||||
int err;
|
||||
|
||||
holder = malloc(sizeof(*holder));
|
||||
if (holder == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
holder->packetLength = sizeof(NV_MOUSE_BUTTON_PACKET);
|
||||
holder->packet.mouseButton.header.packetType = htonl(PACKET_TYPE_MOUSE_BUTTON);
|
||||
holder->packet.mouseButton.action = action;
|
||||
holder->packet.mouseButton.button = htonl(button);
|
||||
|
||||
err = LbqOfferQueueItem(&packetQueue, holder);
|
||||
if (err != LBQ_SUCCESS) {
|
||||
free(holder);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int LiSendKeyboardEvent(short keyCode, char keyAction, char modifiers) {
|
||||
PPACKET_HOLDER holder;
|
||||
int err;
|
||||
|
||||
holder = malloc(sizeof(*holder));
|
||||
if (holder == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
holder->packetLength = sizeof(NV_KEYBOARD_PACKET);
|
||||
holder->packet.keyboard.header.packetType = htonl(PACKET_TYPE_KEYBOARD);
|
||||
holder->packet.keyboard.keyAction = keyAction;
|
||||
holder->packet.keyboard.zero1 = 0;
|
||||
holder->packet.keyboard.keyCode = htons(keyCode);
|
||||
holder->packet.keyboard.modifiers = modifiers;
|
||||
holder->packet.keyboard.zero2 = 0;
|
||||
|
||||
err = LbqOfferQueueItem(&packetQueue, holder);
|
||||
if (err != LBQ_SUCCESS) {
|
||||
free(holder);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -34,3 +34,8 @@ void initializeAudioStream(IP_ADDRESS host, PAUDIO_RENDERER_CALLBACKS arCallback
|
||||
void destroyAudioStream(void);
|
||||
int startAudioStream(void);
|
||||
void stopAudioStream(void);
|
||||
|
||||
int initializeInputStream(IP_ADDRESS addr, PCONNECTION_LISTENER_CALLBACKS clCallbacks);
|
||||
void destroyInputStream(void);
|
||||
int startInputStream(void);
|
||||
int stopInputStream(void);
|
||||
|
@ -59,13 +59,16 @@ typedef struct _AUDIO_RENDERER_CALLBACKS {
|
||||
#define STAGE_CONTROL_STREAM_INIT 3
|
||||
#define STAGE_VIDEO_STREAM_INIT 4
|
||||
#define STAGE_AUDIO_STREAM_INIT 5
|
||||
#define STAGE_CONTROL_STREAM_START 6
|
||||
#define STAGE_VIDEO_STREAM_START 7
|
||||
#define STAGE_AUDIO_STREAM_START 8
|
||||
#define STAGE_INPUT_STREAM_INIT 6
|
||||
#define STAGE_CONTROL_STREAM_START 7
|
||||
#define STAGE_VIDEO_STREAM_START 8
|
||||
#define STAGE_AUDIO_STREAM_START 9
|
||||
#define STAGE_INPUT_STREAM_START 10
|
||||
#define STAGE_MAX 11
|
||||
|
||||
typedef void(*ConnListenerStageStarting)(int stage);
|
||||
typedef void(*ConnListenerStageComplete)(int stage);
|
||||
typedef void(*ConnListenerStageFailed)(int stage);
|
||||
typedef void(*ConnListenerStageFailed)(int stage, int errorCode);
|
||||
|
||||
typedef void(*ConnListenerConnectionStarted)(void);
|
||||
typedef void(*ConnListenerConnectionTerminated)(int errorCode);
|
||||
@ -88,6 +91,22 @@ int LiStartConnection(IP_ADDRESS host, PSTREAM_CONFIGURATION streamConfig, PCONN
|
||||
void LiStopConnection(void);
|
||||
const char* LiGetStageName(int stage);
|
||||
|
||||
int LiSendMouseMoveEvent(short deltaX, short deltaY);
|
||||
|
||||
#define BUTTON_ACTION_PRESS 0x07
|
||||
#define BUTTON_ACTION_RELEASE 0x08
|
||||
#define BUTTON_LEFT 0x01
|
||||
#define BUTTON_MIDDLE 0x02
|
||||
#define BUTTON_RIGHT 0x03
|
||||
int LiSendMouseButtonEvent(char action, int button);
|
||||
|
||||
#define KEY_ACTION_DOWN 0x03
|
||||
#define KEY_ACTION_UP 0x04
|
||||
#define MODIFIER_SHIFT 0x01
|
||||
#define MODIFIER_CTRL 0x02
|
||||
#define MODIFIER_ALT 0x04
|
||||
int LiSendKeyboardEvent(short keyCode, char keyAction, char modifiers);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -126,9 +126,6 @@
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="ReadMe.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="AudioStream.c" />
|
||||
<ClCompile Include="ByteBuffer.c" />
|
||||
@ -136,6 +133,7 @@
|
||||
<ClCompile Include="Connection.c" />
|
||||
<ClCompile Include="ControlStream.c" />
|
||||
<ClCompile Include="Handshake.c" />
|
||||
<ClCompile Include="InputStream.c" />
|
||||
<ClCompile Include="LinkedBlockingQueue.c" />
|
||||
<ClCompile Include="PlatformSockets.c" />
|
||||
<ClCompile Include="PlatformThreads.c" />
|
||||
@ -144,6 +142,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ByteBuffer.h" />
|
||||
<ClInclude Include="Input.h" />
|
||||
<ClInclude Include="Limelight-internal.h" />
|
||||
<ClInclude Include="Limelight.h" />
|
||||
<ClInclude Include="LinkedBlockingQueue.h" />
|
||||
|
@ -14,9 +14,6 @@
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="ReadMe.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ByteBuffer.c">
|
||||
<Filter>Source Files</Filter>
|
||||
@ -51,6 +48,9 @@
|
||||
<ClCompile Include="AudioStream.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="InputStream.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="PlatformSockets.h">
|
||||
@ -77,5 +77,8 @@
|
||||
<ClInclude Include="Limelight.h">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Input.h">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
Loading…
x
Reference in New Issue
Block a user