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 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
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);
int startAudioStream(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_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

View File

@ -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" />

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>
</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>