Add keyboard and mouse input support

This commit is contained in:
Cameron Gutman 2014-03-29 15:45:31 -04:00
parent c553b14da6
commit 761f324465
7 changed files with 285 additions and 19 deletions

View File

@ -4,16 +4,18 @@
static int stage = STAGE_NONE; static int stage = STAGE_NONE;
static CONNECTION_LISTENER_CALLBACKS ListenerCallbacks; static CONNECTION_LISTENER_CALLBACKS ListenerCallbacks;
static const char* stageNames [] = { static const char* stageNames[STAGE_MAX] = {
"none", "none",
"platform initialization", "platform initialization",
"handshake", "handshake",
"control stream initialization", "control stream initialization",
"video stream initialization", "video stream initialization",
"audio stream initialization", "audio stream initialization",
"input stream initialization",
"control stream establishment", "control stream establishment",
"video stream establishment", "video stream establishment",
"audio stream establishment" "audio stream establishment",
"input stream establishment"
}; };
const char* LiGetStageName(int stage) { const char* LiGetStageName(int stage) {
@ -21,6 +23,12 @@ const char* LiGetStageName(int stage) {
} }
void LiStopConnection(void) { void LiStopConnection(void) {
if (stage == STAGE_INPUT_STREAM_START) {
Limelog("Stopping input stream...");
stopInputStream();
stage--;
Limelog("done\n");
}
if (stage == STAGE_AUDIO_STREAM_START) { if (stage == STAGE_AUDIO_STREAM_START) {
Limelog("Stopping audio stream..."); Limelog("Stopping audio stream...");
stopAudioStream(); stopAudioStream();
@ -39,6 +47,12 @@ void LiStopConnection(void) {
stage--; stage--;
Limelog("done\n"); 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) { if (stage == STAGE_AUDIO_STREAM_INIT) {
Limelog("Cleaning up audio stream..."); Limelog("Cleaning up audio stream...");
destroyAudioStream(); destroyAudioStream();
@ -83,7 +97,7 @@ int LiStartConnection(IP_ADDRESS host, PSTREAM_CONFIGURATION streamConfig, PCONN
err = initializePlatformSockets(); err = initializePlatformSockets();
if (err != 0) { if (err != 0) {
Limelog("failed: %d\n", err); Limelog("failed: %d\n", err);
ListenerCallbacks.stageFailed(STAGE_PLATFORM_INIT); ListenerCallbacks.stageFailed(STAGE_PLATFORM_INIT, err);
goto Cleanup; goto Cleanup;
} }
stage++; stage++;
@ -96,7 +110,7 @@ int LiStartConnection(IP_ADDRESS host, PSTREAM_CONFIGURATION streamConfig, PCONN
err = performHandshake(host); err = performHandshake(host);
if (err != 0) { if (err != 0) {
Limelog("failed: %d\n", err); Limelog("failed: %d\n", err);
ListenerCallbacks.stageFailed(STAGE_HANDSHAKE); ListenerCallbacks.stageFailed(STAGE_HANDSHAKE, err);
goto Cleanup; goto Cleanup;
} }
stage++; stage++;
@ -109,7 +123,7 @@ int LiStartConnection(IP_ADDRESS host, PSTREAM_CONFIGURATION streamConfig, PCONN
err = initializeControlStream(host, streamConfig, &ListenerCallbacks); err = initializeControlStream(host, streamConfig, &ListenerCallbacks);
if (err != 0) { if (err != 0) {
Limelog("failed: %d\n", err); Limelog("failed: %d\n", err);
ListenerCallbacks.stageFailed(STAGE_CONTROL_STREAM_INIT); ListenerCallbacks.stageFailed(STAGE_CONTROL_STREAM_INIT, err);
goto Cleanup; goto Cleanup;
} }
stage++; stage++;
@ -133,12 +147,20 @@ int LiStartConnection(IP_ADDRESS host, PSTREAM_CONFIGURATION streamConfig, PCONN
ListenerCallbacks.stageComplete(STAGE_AUDIO_STREAM_INIT); ListenerCallbacks.stageComplete(STAGE_AUDIO_STREAM_INIT);
Limelog("done\n"); 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..."); Limelog("Starting control stream...");
ListenerCallbacks.stageStarting(STAGE_CONTROL_STREAM_START); ListenerCallbacks.stageStarting(STAGE_CONTROL_STREAM_START);
err = startControlStream(); err = startControlStream();
if (err != 0) { if (err != 0) {
Limelog("failed: %d\n", err); Limelog("failed: %d\n", err);
ListenerCallbacks.stageFailed(STAGE_CONTROL_STREAM_START); ListenerCallbacks.stageFailed(STAGE_CONTROL_STREAM_START, err);
goto Cleanup; goto Cleanup;
} }
stage++; stage++;
@ -151,7 +173,7 @@ int LiStartConnection(IP_ADDRESS host, PSTREAM_CONFIGURATION streamConfig, PCONN
err = startVideoStream(renderContext, drFlags); err = startVideoStream(renderContext, drFlags);
if (err != 0) { if (err != 0) {
Limelog("Video stream start failed: %d\n", err); Limelog("Video stream start failed: %d\n", err);
ListenerCallbacks.stageFailed(STAGE_VIDEO_STREAM_START); ListenerCallbacks.stageFailed(STAGE_VIDEO_STREAM_START, err);
goto Cleanup; goto Cleanup;
} }
stage++; stage++;
@ -164,7 +186,7 @@ int LiStartConnection(IP_ADDRESS host, PSTREAM_CONFIGURATION streamConfig, PCONN
err = startAudioStream(); err = startAudioStream();
if (err != 0) { if (err != 0) {
Limelog("Audio stream start failed: %d\n", err); Limelog("Audio stream start failed: %d\n", err);
ListenerCallbacks.stageFailed(STAGE_AUDIO_STREAM_START); ListenerCallbacks.stageFailed(STAGE_AUDIO_STREAM_START, err);
goto Cleanup; goto Cleanup;
} }
stage++; stage++;
@ -172,6 +194,21 @@ int LiStartConnection(IP_ADDRESS host, PSTREAM_CONFIGURATION streamConfig, PCONN
ListenerCallbacks.stageComplete(STAGE_AUDIO_STREAM_START); ListenerCallbacks.stageComplete(STAGE_AUDIO_STREAM_START);
Limelog("done\n"); 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: Cleanup:
return err; return err;
} }

35
limelight-common/Input.h Normal file
View 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)

View 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;
}

View File

@ -34,3 +34,8 @@ void initializeAudioStream(IP_ADDRESS host, PAUDIO_RENDERER_CALLBACKS arCallback
void destroyAudioStream(void); void destroyAudioStream(void);
int startAudioStream(void); int startAudioStream(void);
void stopAudioStream(void); void stopAudioStream(void);
int initializeInputStream(IP_ADDRESS addr, PCONNECTION_LISTENER_CALLBACKS clCallbacks);
void destroyInputStream(void);
int startInputStream(void);
int stopInputStream(void);

View File

@ -59,13 +59,16 @@ typedef struct _AUDIO_RENDERER_CALLBACKS {
#define STAGE_CONTROL_STREAM_INIT 3 #define STAGE_CONTROL_STREAM_INIT 3
#define STAGE_VIDEO_STREAM_INIT 4 #define STAGE_VIDEO_STREAM_INIT 4
#define STAGE_AUDIO_STREAM_INIT 5 #define STAGE_AUDIO_STREAM_INIT 5
#define STAGE_CONTROL_STREAM_START 6 #define STAGE_INPUT_STREAM_INIT 6
#define STAGE_VIDEO_STREAM_START 7 #define STAGE_CONTROL_STREAM_START 7
#define STAGE_AUDIO_STREAM_START 8 #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(*ConnListenerStageStarting)(int stage);
typedef void(*ConnListenerStageComplete)(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(*ConnListenerConnectionStarted)(void);
typedef void(*ConnListenerConnectionTerminated)(int errorCode); typedef void(*ConnListenerConnectionTerminated)(int errorCode);
@ -88,6 +91,22 @@ int LiStartConnection(IP_ADDRESS host, PSTREAM_CONFIGURATION streamConfig, PCONN
void LiStopConnection(void); void LiStopConnection(void);
const char* LiGetStageName(int stage); 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 #ifdef __cplusplus
} }
#endif #endif

View File

@ -126,9 +126,6 @@
<OptimizeReferences>true</OptimizeReferences> <OptimizeReferences>true</OptimizeReferences>
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup>
<Text Include="ReadMe.txt" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="AudioStream.c" /> <ClCompile Include="AudioStream.c" />
<ClCompile Include="ByteBuffer.c" /> <ClCompile Include="ByteBuffer.c" />
@ -136,6 +133,7 @@
<ClCompile Include="Connection.c" /> <ClCompile Include="Connection.c" />
<ClCompile Include="ControlStream.c" /> <ClCompile Include="ControlStream.c" />
<ClCompile Include="Handshake.c" /> <ClCompile Include="Handshake.c" />
<ClCompile Include="InputStream.c" />
<ClCompile Include="LinkedBlockingQueue.c" /> <ClCompile Include="LinkedBlockingQueue.c" />
<ClCompile Include="PlatformSockets.c" /> <ClCompile Include="PlatformSockets.c" />
<ClCompile Include="PlatformThreads.c" /> <ClCompile Include="PlatformThreads.c" />
@ -144,6 +142,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="ByteBuffer.h" /> <ClInclude Include="ByteBuffer.h" />
<ClInclude Include="Input.h" />
<ClInclude Include="Limelight-internal.h" /> <ClInclude Include="Limelight-internal.h" />
<ClInclude Include="Limelight.h" /> <ClInclude Include="Limelight.h" />
<ClInclude Include="LinkedBlockingQueue.h" /> <ClInclude Include="LinkedBlockingQueue.h" />

View File

@ -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> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter> </Filter>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Text Include="ReadMe.txt" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="ByteBuffer.c"> <ClCompile Include="ByteBuffer.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
@ -51,6 +48,9 @@
<ClCompile Include="AudioStream.c"> <ClCompile Include="AudioStream.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="InputStream.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="PlatformSockets.h"> <ClInclude Include="PlatformSockets.h">
@ -77,5 +77,8 @@
<ClInclude Include="Limelight.h"> <ClInclude Include="Limelight.h">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="Input.h">
<Filter>Source Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
</Project> </Project>